/* ============================================================================
   V9 — NEUES LEITUNGS-DASHBOARD (v413, 16.08.2026)
   ============================================================================
   Design 1:1 aus dem abgenommenen Mockup ~/mockup_dpi/index.html v2.57
   ("Stahl hell": Graphit-Toene + Stahl #9FB4C9 als einzige Akzentfarbe,
   Stil-Konvention 30.07.2026).

   BAU-GRUNDSATZ (ENDBAU_PLAN_V1.md A0): Diese Datei steht NEBEN dem Bestand.
   JEDE Regel hier ist auf `.v9` gescoped — das Bestands-Stylesheet style.css
   wird nicht angefasst und kann von hier aus nichts erben. Flag aus = altes
   Dashboard, unveraendert, ohne Deploy.

   MOBILE-FIXIERUNG: Die eiserne Regel wird von style.css getragen
   (html,body{max-width:100vw;overflow-x:hidden}) und gilt fuer diese Seite
   mit, weil V9 in derselben index.html laeuft. Hier wird sie NICHT gelockert:
   nichts in diesem Blatt darf die Seite horizontal aufziehen (breite Inhalte
   scrollen in .v9 .tabelle-scroll, nie im Body). Waechter:
   backend/tests/test_mobile_fixierung.py

   KLASSEN-VOLLSTAENDIGKEIT (Fehler-Gedaechtnis "benutzte-css-klasse-ist-
   nirgends-definiert"): jede Klasse, die dashboard_v9.js benutzt, ist hier
   definiert. Waechter: backend/tests/test_v413_dashboard_v9.py
   ========================================================================= */

.v9{
  --bg:#f4f6f8; --panel:#ffffff; --ink:#232830; --muted:#6b7480; --line:#e3e7ec;
  --stahl:#9FB4C9; --stahl-dunkel:#7d95ad; --nav:#232830; --nav-ink:#cfd6de;
  /* CI-ABGRENZUNG VARIANTE C (Enrico 16.08.2026, Wahl "c"): EINE Kantenfarbe fuer das
     ganze Haus. Als Token, weil es KEIN gemeinsames Stylesheet gibt - jedes Portal
     traegt seinen eigenen <style>-Block. Derselbe Hex-Wert in 20 Dateien waere beim
     naechsten CI-Wunsch 20 Suchlaeufe; als Token ist es je Datei eine Zeile.
     --kante     = Grundkante jeder Kachel (2px Flaeche, sonnenlicht-tauglich)
     --kante-auf = Kante der GEOEFFNETEN Kachel (fast schwarz) */
  --kante:#b7c1cc; --kante-auf:#232830;
  --ok:#3d8b60; --warn:#b07a1f; --bad:#b0483d;
  --warnbg:#fdf6e7; --badbg:#faeeec; --okbg:#edf5f0;
  font:14px/1.45 -apple-system,'Segoe UI',Roboto,sans-serif;
  background:var(--bg); color:var(--ink);
  min-height:100vh;
}
.v9 *{box-sizing:border-box}
.v9 a{color:inherit;text-decoration:none}
.v9 h1,.v9 h2{margin:0}
/* Icon-Grundregel: V9 bringt sie SELBST mit, statt sich auf `.si` aus style.css zu
   verlassen. Ohne fill:none/stroke rendern die Pfade aus icons.js als gefuellte
   schwarze Flaechen - beim Lesen des Codes unsichtbar, im Bild sofort. Gefunden im
   eigenen Mobil-Beleg 16.08., nicht beim Nutzer. */
.v9 svg{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* ---- Layout ---- */
.v9 .layout{display:flex;min-height:100vh}

/* ---- Navigation links ---- */
.v9 .nav{width:232px;background:var(--nav);color:var(--nav-ink);padding:14px 10px;
  flex-shrink:0;display:flex;flex-direction:column}
.v9 .nav .logo{color:#fff;font-weight:700;font-size:15px;padding:6px 10px 14px;letter-spacing:.3px}
.v9 .nav .logo span{color:var(--stahl)}
.v9 .nav .sect{font-size:11px;text-transform:uppercase;letter-spacing:.8px;opacity:.55;padding:12px 10px 4px}
.v9 .nav a{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:8px;
  margin:1px 0;font-size:13.5px;cursor:pointer}
.v9 .nav a:hover{background:rgba(255,255,255,.07)}
.v9 .nav a.akt{background:rgba(159,180,201,.18);color:#fff}
.v9 .nav a svg{width:17px;height:17px;flex-shrink:0}
/* Badges: ROT ist ausschliesslich fuer erledigbare Aktionen reserviert (AK-Q2/AK-UE5).
   BL-3 (Ulla/Kimi-Review): Jahres-Arbeitsvorrat ist GRAU (.z), Kalibrier-Hinweise GELB (.g).
   In v1 gibt es deshalb KEINE rote Nav-Badge — erst das Warnungs-Backend (Welle 1)
   liefert echte, erledigbare Faelle. */
.v9 .nav .badge{margin-left:auto;background:var(--bad);color:#fff;font-size:11px;
  font-weight:700;border-radius:9px;padding:1px 7px}
.v9 .nav .badge.z{background:rgba(255,255,255,.14);color:var(--nav-ink)}
.v9 .nav .badge.g{background:var(--warnbg);color:var(--warn)}
/* P3-12 (23.08.2026): BLAU fuer "N neu" in der Nav - dieselbe Aussage und dieselbe Farbe
   wie die .nbadge an den aufklappbaren Karten (#1d6ad6), damit die Zahl in der Nav und die
   Zahl auf der Kachel sichtbar dasselbe meinen. */
.v9 .nav .badge.n{background:#1d6ad6;color:#fff}
.v9 .nav .version{margin-top:auto;padding:14px 10px 4px;font-size:11px;opacity:.45;letter-spacing:.4px}
.v9 .nav .altlink{padding:2px 10px 10px;font-size:11.5px;opacity:.6}
.v9 .nav .altlink a{text-decoration:underline;cursor:pointer}

/* ---- Hauptbereich ---- */
.v9 .main{flex:1;padding:20px 26px;max-width:1180px;min-width:0}
.v9 .kopf{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap}
.v9 .kopf h1{font-size:24px}
.v9 .kopf .stand{color:var(--muted);font-size:12px}
.v9 .kopf .wer{margin-left:auto;font-size:12.5px;color:var(--muted)}
.v9 .kopf .wer b{color:var(--ink)}
.v9 .krumen{font-size:12.5px;color:var(--muted);margin-bottom:10px}
.v9 .krumen a{color:var(--stahl-dunkel);font-weight:600;cursor:pointer}

/* ---- Kacheln ---- */
.v9 .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}
.v9 .kachel{background:var(--panel);border:1px solid var(--line);border-radius:12px;
  padding:14px 16px;cursor:pointer;display:flex;flex-direction:column;gap:6px;min-height:118px;
  transition:box-shadow .12s,border-color .12s;position:relative}
.v9 .kachel:hover{border-color:var(--stahl);box-shadow:0 2px 10px rgba(35,40,48,.08)}
.v9 .kachel .kzeile{display:flex;align-items:center;gap:9px}
.v9 .kachel .kzeile svg{width:19px;height:19px;color:var(--stahl-dunkel);flex-shrink:0}
.v9 .kachel .titel{font-weight:700;font-size:16px}
.v9 .kachel .zahl{font-size:24px;font-weight:700;margin-top:2px}
.v9 .kachel .sub{color:var(--muted);font-size:13px}
.v9 .kachel .pillen{display:flex;flex-wrap:wrap;gap:5px;margin-top:2px}
/* Olha-Auflage A3 (16.08.): in LISTENZEILEN gab es diese Regel nicht — `.pill` ist
   inline-block ohne margin, und die Konto-Zeile setzt ihre drei Pillen ohne Trenner
   aneinander. Ergebnis: sie klebten zu einem Block. Die beiden anderen Nutzungen kamen
   nur durch, weil sie `.join(' ')` verwenden bzw. in `.kachel` liegen — genau die
   Hausfehlerklasse "benutzte-css-klasse-ist-nirgends-definiert". Der Abstand gehoert ins
   Blatt, nicht in ein Leerzeichen im Markup. */
.v9 .item .pillen{display:flex;flex-wrap:wrap;gap:5px;align-items:flex-start}

/* Auftraggeber-Kopfzeile der Projekte-Seite (A4): die Modul-Pillen JE MODUL unter dem
   Auftraggeber-Namen. Eigene Klasse statt `.pillen`, weil `.pillen` ausschliesslich unter
   `.kachel` bzw. `.item` definiert ist - hier haette sie ohne `display:flex;gap` geklebt
   (A3-Befund aus v415, Hausfehlerklasse "benutzte-css-klasse-ist-nirgends-definiert"). */

/* ---- Status-Pillen "Variante B" (AK-Q5): blasse Flaeche, global als .pill ---- */
.v9 .pill{font-size:11px;font-weight:700;border-radius:9px;padding:2px 8px;
  flex-shrink:0;white-space:nowrap;display:inline-block}
.v9 .pill.bad{background:var(--badbg);color:var(--bad)}
.v9 .pill.warn{background:var(--warnbg);color:var(--warn)}
.v9 .pill.ok{background:var(--okbg);color:var(--ok)}
.v9 .pill.st{background:#eef2f6;color:var(--stahl-dunkel)}

/* ---- Karten / Listen ---- */
/* CI-ABGRENZUNG VARIANTE C (Enrico 16.08.2026, woertlich "c" auf die Varianten-Vorlage
   ci-abgrenzung.html). Anlass war seine iPhone-Ruege: "geoeffnete kacheln sind nicht klar
   abgegrenzt von geschlossenen und das global ... bei sonnenschein sieht man den uebergang
   gar nicht mehr." Deshalb 2px FLAECHE statt Haarlinie - eine 1px-Linie in --line (#e3e7ec)
   verschwindet auf einem #f4f6f8-Grund bei Sonnenlicht restlos.
   Werte 1:1 aus der abgenommenen Vorlage uebernommen, nicht neu erfunden.
   Waechter: backend/tests/test_v268_ci_c_abgrenzung.py */
.v9 .card{background:var(--panel);border:2px solid var(--kante);border-radius:10px;
  padding:14px 16px;margin-bottom:14px;overflow-wrap:anywhere}
.v9 .card h2{font-size:14.5px;margin-bottom:8px;display:flex;align-items:center;gap:8px}
.v9 .card h2 svg{width:16px;height:16px;color:var(--stahl-dunkel);flex-shrink:0}
.v9 .item{display:flex;align-items:center;gap:10px;padding:8px 6px;
  border-top:1px solid var(--line);cursor:pointer;flex-wrap:wrap}
.v9 .item:hover{background:#f8fafb}
.v9 .item.statisch{cursor:default}
.v9 .item.statisch:hover{background:transparent}
.v9 .item .t{font-weight:600;font-size:13.5px;overflow-wrap:anywhere}
.v9 .item .s{color:var(--muted);font-size:12px;overflow-wrap:anywhere}
.v9 .item .grow{flex:1;min-width:0}
.v9 .mpill{font-size:11px;font-weight:700;border-radius:8px;padding:1px 7px;
  background:#eef2f6;color:var(--stahl-dunkel);flex-shrink:0}
.v9 .leer{color:var(--muted);padding:14px 6px;font-size:13px}

/* ---- Knoepfe: EIN Haus-Standard 13px / 8-14 / Radius 9 (AK-Q6) ---- */
.v9 .btn{display:inline-flex;align-items:center;gap:7px;background:var(--stahl);border:none;
  color:#1d2229;font-weight:700;font-size:13px;border-radius:9px;padding:8px 14px;cursor:pointer;
  font-family:inherit}
.v9 .btn:hover{background:var(--stahl-dunkel);color:#fff}
.v9 .btn.sec{background:#eef2f6;color:var(--ink)}
.v9 .btn.sec:hover{background:#dfe6ee;color:var(--ink)}
.v9 .btn svg{width:14px;height:14px;flex-shrink:0}

/* ---- Ampel-Punkt ---- */
.v9 .dot{width:9px;height:9px;border-radius:50%;background:var(--ok);display:inline-block;
  margin-right:6px;box-shadow:0 0 0 3px rgba(61,139,96,.15);flex-shrink:0}
.v9 .dot.aus{background:#b6bec7;box-shadow:none}
/* Olha-Auflage AUF-2 (16.08., v415): der Verlauf faerbt "erreicht" gruen. Fuer die Stufe
   "gesperrt" hiess das ein GRUENER Punkt an einem Zugang, der niemanden mehr hereinlaesst —
   in einer Kachel, in der Gruen sonst "alles in Ordnung" bedeutet, liest sich das falsch.
   Erreicht bleibt erreicht, aber die Farbe sagt jetzt WELCHER Zustand erreicht wurde. */
.v9 .dot.bad{background:var(--bad);box-shadow:none}
/* GELB fehlte im Haus-Vokabular: `.dot` war gruen, `.dot.bad` rot, `.dot.aus` grau. Die
   Tablet-Ampel (AK-GV6) braucht drei Zustaende, und Gelb heisst dort ausdruecklich "wissen",
   nicht "Problem" - deshalb ein eigener Ton statt Rot in blass. */
.v9 .dot.warn{background:var(--warn);box-shadow:0 0 0 3px rgba(176,122,31,.15)}
/* In der Tablet-Zeile und der Parameter-Liste tragen die Punkte das Mass der Vorlage (10px)
   und ihren Ring - sie sind dort das eigentliche Signal, nicht Beiwerk. */
.v9 .tabzeile>.dot,.v9 .param>.dot{width:10px;height:10px;margin-right:0}
.v9 .tabzeile>.dot:not(.warn):not(.bad),.v9 .param>.dot:not(.warn):not(.bad){
  box-shadow:0 0 0 3px rgba(61,139,96,.15)}
.v9 .tabzeile>.dot.bad,.v9 .param>.dot.bad{box-shadow:0 0 0 3px rgba(176,72,61,.18)}

/* ---- Klapp-Karten (Enrico 13.08.: Default ZU) ---- */
.v9 .klapp>h2{cursor:pointer;user-select:none;flex-wrap:nowrap}
.v9 .klapp>h2 .ktitel{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v9 .klapp .pfeil{margin-left:auto;color:var(--muted);transition:transform .15s;flex-shrink:0}
.v9 .klapp.zu .inhalt{display:none}
.v9 .klapp.zu>h2{margin-bottom:0}
.v9 .klapp.zu .pfeil{transform:rotate(-90deg)}
/* Variante C, Zustand (s. Kommentar an .card): die GEOEFFNETE Kachel bekommt den fast
   schwarzen Rahmen + Schatten, die geschlossene bleibt ruhig und leicht abgesetzt.
   `:not(.zu)` statt einer eigenen `.offen`-Klasse, weil das Markup den Zustand allein
   ueber `.zu` fuehrt (dashboard_v9.js) - eine zweite Zustandsquelle waere eine Luege,
   die irgendwann auseinanderlaeuft. */
.v9 .klapp:not(.zu){border-color:var(--kante-auf);box-shadow:0 3px 10px rgba(35,40,48,.16)}
.v9 .klapp.zu{background:#f7f9fb;border-color:#c9d2db}
.v9 .klapp:not(.zu)>.inhalt{border-top:2px dashed #c3ccd6;padding-top:9px;margin-top:9px}
/* Die erste Zeile im Inhalt zoege sonst direkt unter der gestrichelten Kopftrennung
   ihre eigene Linie - zwei Striche uebereinander. */
.v9 .klapp:not(.zu)>.inhalt>.item:first-child{border-top:0}
.v9 .kstand{flex-shrink:0;font-size:11.5px;color:var(--muted);font-weight:400}
/* A5 (22.08.2026, Welle 3): blaue "N neu"-Zahl - 1:1 aus mockup_dpi/index.html (.nbadge),
   dort #1d6ad6 auf jedem Theme fest (keine Dark-Mode-Variable im Mockup, hier bewusst
   uebernommen statt erfunden - Farbe war Teil der abgenommenen Optik, nicht der Funktion). */
.v9 .nbadge{flex-shrink:0;background:#1d6ad6;color:#fff;font-size:11px;font-weight:700;
  border-radius:9px;padding:1px 7px}
/* v2.95 — DER NAME WIRD NIE ZUM OPFER.
   Bei 390 px stehen im Auftraggeber-Kopf drei Dinge nebeneinander: Logo, Name, Zaehler.
   Seit v2.94 hat "DPI (Direktauftrag)" ein Logo (ein breites Wortbild) und seit v2.95 traegt
   der Zaehler statt "115 Objekte" die Modul-Zahlen ("13/102 DGUV · 1/13 Leiter"). Beides
   zusammen liess nur noch "DPI (Direk…" uebrig - im 390-px-Bild sofort zu sehen, in der
   Suite unsichtbar. `.ktitel` ist das einzige schrumpfende Kind, also trifft es immer den
   NAMEN, und der ist die Identitaet der Karte.
   Auf schmalen Geraeten darf der Kopf deshalb umbrechen: der Zaehler rutscht unter den
   Namen, wenn er nicht mehr danebenpasst. `flex-shrink:0` an `.kstand` bleibt - er soll
   umbrechen, nicht zerquetscht werden. Passt alles nebeneinander (die meisten Karten,
   jeder Desktop), aendert sich nichts. */
@media (max-width: 480px){
  /* Der Zaehler bekommt IMMER seine eigene Zeile, nicht nur wenn es gerade knapp wird.
     Zwei Anlaeufe davor, beide im Bild verworfen: (1) `flex-wrap` allein liess den Pfeil
     mit dem Zaehler nach unten rutschen; (2) ein `order:-1` am Pfeil setzte ihn an den
     linken Rand VOR das Logo. Mit "nur umbrechen wenn noetig" stand der Pfeil ausserdem
     mal am rechten Rand und mal mitten in der Zeile - je nach Laenge des Zaehlers. Eine
     Kopfzeile, deren Bauteile je nach Datenlage die Plaetze tauschen, liest sich unruhig.
     Jetzt gilt auf schmalen Geraeten eine feste Ordnung: Zeile 1 Logo + Name + Pfeil
     (rechts), Zeile 2 der Zaehler (rechtsbuendig). Der Name wird nie gekuerzt. */
  .v9 .klapp>h2{flex-wrap:wrap}
  .v9 .klapp>h2 .kstand{order:1;flex-basis:100%;text-align:right;margin-top:2px}
  /* Und der Name selbst darf UMBRECHEN statt zu kuerzen. Ohne diese Zeile blieb der
     Zaehler zwar unten, aber lange Titel wurden weiter abgeschnitten - im Termine-Beleg
     stand "Leitern · Regale · Gerüste: 19 offene Objekte …" und der Pfeil rutschte dabei
     sogar in eine eigene Zeile. Zwei Fliegen: der Titel nimmt sich die Zeilen, die er
     braucht, und der Pfeil bleibt neben ihm.
     Auf dem Desktop bleibt `ellipsis` (Regel oben) - dort ist Platz, und eine Kachel-
     Kopfzeile, die dreizeilig wird, waere dort die schlechtere Loesung. */
  /* `flex:1 1 0` statt des geerbten `1 1 auto` ist hier das Entscheidende: mit
     Basis `auto` bemisst sich der Titel an seinem INHALT, ein langer Name macht den
     Kasten also breiter als die Zeile - und schiebt den Pfeil in eine eigene Zeile
     (im Termine-Bild genau so passiert). Mit Basis 0 nimmt er nur den Platz, der
     uebrig ist, und der Text bricht INNERHALB seines Kastens um. */
  .v9 .klapp>h2 .ktitel{flex:1 1 0;white-space:normal;overflow:visible;text-overflow:clip}
}
/* ^ SCHLIESST den 480-px-Block. Bis v2.98 stand diese Klammer 52 Zeilen zu frueh (direkt
   hinter `.v9 .klapp>h2`, Zeile 164) — der Block blieb damit OFFEN und verschluckte den
   GESAMTEN Rest des Blattes. Alles ab hier galt nur noch unter 480 px: die Logo-Groesse
   (`img.tlogo`), die Kategorie-Zwischenzeile, `.tabelle-scroll` (Mobile-Fixierung!), die
   gelbe Hinweis-Box, die komplette Geraete-Welle aus v2.96 und die Safe-Area. Auf dem
   Desktop rendern Logos deshalb in Originalgroesse (Enrico 18.08., gemessen 180x179 px).
   Der Fehler kam mit v2.95 (76469e8) herein und war im Quelltext nicht zu sehen, weil eine
   verirrte Klammer wie das Ende der Regel davor aussieht.
   Waechter: backend/tests/test_v2_99_css_klammern.py */

/* Kategorie-Zwischenzeile INNERHALB einer Traeger-Klappkarte (v2.71, dritte Gliederungs-
   ebene der Abrechnungs-Kachel). Bewusst KEINE eigene Karte: sie soll die Zeilen darunter
   ordnen, nicht mit ihnen um Aufmerksamkeit konkurrieren. `sticky` haelt die Kategorie
   beim Scrollen einer langen Gruppe sichtbar - sonst weiss man nach acht Zeilen nicht
   mehr, ob man noch bei "Kitas" oder schon bei "Museen" ist. */
.v9 .kgruppe{display:flex;align-items:center;justify-content:space-between;gap:8px;
  position:sticky;top:0;z-index:1;background:var(--panel);
  font-size:11.5px;text-transform:uppercase;letter-spacing:.4px;font-weight:700;
  color:var(--muted);padding:9px 0 5px;border-bottom:1px solid var(--line)}
.v9 .kgruppe:first-child{padding-top:2px}
/* Die Zeile direkt unter einer Kategorie braucht keine zweite Trennlinie. */
.v9 .kgruppe + .item{border-top:0}
.v9 img.tlogo{height:22px;width:auto;max-width:84px;object-fit:contain;flex-shrink:0;
  border-radius:3px;display:block}

/* ---- Tabellen: scrollen IN der Karte, nie im Body (Mobile-Fixierung) ---- */
.v9 .tabelle-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.v9 table{width:100%;border-collapse:collapse;font-size:13px}
.v9 th{text-align:left;color:var(--muted);font-size:11.5px;text-transform:uppercase;
  letter-spacing:.4px;padding:6px;border-bottom:1px solid var(--line);white-space:nowrap}
.v9 td{padding:8px 6px;border-bottom:1px solid var(--line);vertical-align:top}

/* ---- Geraeteverwaltung: Aktionen an der Geraetezeile (v2.96) ----
   DESKTOP folgt der abgenommenen Vorlage geraete_demo.html: die Knoepfe stehen NEBEN dem
   Text, rechts, in einer Reihe. `align-items:flex-start` an der Zeile, damit sie oben
   andocken statt mittig zu schwimmen - die Geraetezeile ist vier Textzeilen hoch.
   MOBIL siehe @media unten: gestapelt in voller Breite. Das ist KEINE Geschmacksfrage,
   sondern die Mobile-Fixierungs-Regel - die Vorlage selbst quetscht bei 390 px die
   Textspalte auf vier Woerter je Zeile, weil ihre zwei Knoepfe in DERSELBEN Flex-Zeile
   wie der Text sitzen (belegt im Beleg vorlage_demo_390.png vom 18.08.2026). */
.v9 .item.mgzeile{align-items:flex-start}
.v9 .mgknoepfe{display:flex;gap:8px;flex-wrap:wrap;flex:0 1 auto;max-width:40%;
  align-items:center;justify-content:flex-end}
/* max-width haelt dem TEXT seinen Raum. Ohne den Deckel nimmt sich die Knopfreihe bei drei
   Knoepfen (Kalibrierung + Zertifikat herunterladen + ersetzen) so viel Breite, dass die
   Kalibrier-Zeile umbricht und ein "·" allein am Zeilenanfang haengt - im Bildvergleich
   gegen Enricos Mockup sofort sichtbar, im Quelltext nicht. Die Knoepfe brechen dann lieber
   selbst in eine zweite Reihe um; die Vorlage kennt nur zwei Knoepfe und diesen Fall nicht. */
.v9 .mgupload{cursor:pointer}
/* Der Zertifikat-Download haengt am Wort, nicht an einem dritten Knopf (s. Kommentar in
   dashboard_v9_kacheln.js). Muss als anklickbar ERKENNBAR sein - Unterstrich plus Stahlton. */
.v9 .item .zertlink{color:var(--stahl-dunkel);text-decoration:underline;cursor:pointer}
.v9 .item .zertlink:hover{color:var(--ink)}
/* Die Kalibrier-Eingabe haengt UNTER der Zeile, damit sichtbar bleibt, an welchem Geraet
   gearbeitet wird. Gestrichelte Kante = dasselbe Trenn-Muster wie die Klappzeile der Vorlage. */
.v9 .mgkal{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:10px 6px 12px;border-top:1px dashed var(--line)}
.v9 .mgkal label{color:var(--muted);font-size:12px}
.v9 .mgkal input[type=date]{font:inherit;font-size:13px;padding:7px 9px;
  border:1px solid var(--kante);border-radius:9px;background:var(--panel);color:var(--ink)}

/* ---- Geraeteverwaltung v2.99: Klapp-Kachel je Geraet + Tablet-Ampel (AK-GV5/GV6) ----
   Werte 1:1 aus der abgenommenen Vorlage ~/mockup_dpi/geraete_demo.html uebernommen
   (Enrico-Screenshot 17.08.), nicht neu erfunden: .abschnitt 12px/700/uppercase/.4px,
   summary-Einzug 22px, Parameter-Einzug 34px, Punkt 10px, Trennlinie #f0f2f5. */

/* Untertitel unter der Ueberschrift und Fussnote am Seitenende (Vorlage: .hint). */
.v9 .unter{color:var(--muted);font-size:12.5px;margin:-8px 0 16px;max-width:820px}
.v9 .unter.fuss{margin:14px 0 4px}

/* Abschnitts-Ueberschrift UEBER den Karten ("MESSGERAETE (KALIBRIERPFLICHTIG)").
   Die Versalien kommen aus dem Blatt, nicht aus dem Markup - im Quelltext steht der Satz
   in normaler Schreibweise und bleibt damit vorlesbar und durchsuchbar. */
.v9 .abschnitt{font-size:12px;font-weight:700;color:var(--muted);margin:14px 0 4px;
  text-transform:uppercase;letter-spacing:.4px}

/* ZUGEKLAPPTER KOPF der Geraete-Kachel: zwei Zeilen (Identitaet + "wer hatte es wann",
   Enrico 18.08. "elementar wichtig ... wer wann das geraet zuletzt hatte"). `.ktitel` traegt
   hausweit nowrap+ellipsis fuer EINZEILIGE Titel - hier wird das ausdruecklich aufgehoben,
   sonst waere die zweite Zeile gar nicht darstellbar. */
.v9 .klapp>h2 .ktitel.mgtitel{white-space:normal;overflow:visible;text-overflow:clip;
  display:flex;flex-direction:column;gap:2px;min-width:0}
.v9 .ktitel.mgtitel .t{font-weight:700;font-size:13.5px;overflow-wrap:anywhere}
.v9 .ktitel.mgtitel .s{color:var(--muted);font-size:12px;font-weight:400;overflow-wrap:anywhere}
/* Der Kopf einer Geraete-Kachel darf umbrechen (die Hausregel `flex-wrap:nowrap` gilt fuer
   einzeilige Koepfe) - sonst wird der Name gekuerzt, sobald Status-Pillen daneben stehen
   (Fehler-Gedaechtnis "kachel-kopfzeile-kuerzt-bei-390px-den-namen-statt-der-zahl"). */
/* Bewusst eine echte Klasse am <h2> statt `:has(.mgtitel)` - eine nicht unterstuetzte
   :has()-Regel wird vom Browser KOMPLETT verworfen, und dann kuerzt der Kopf wieder den
   Namen. Eine Klasse kann nicht ausfallen. */
.v9 .klapp>h2.mgkopf{flex-wrap:wrap;align-items:flex-start}
.v9 .mgstatus{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  margin-left:auto;justify-content:flex-end}

/* TABLET-ZEILE unter dem Geraet (Vorlage: details.tab summary). Gestrichelte Kante trennt
   sie vom Geraet, ohne eine zweite Karte aufzumachen. */
.v9 .tabzeile{display:flex;align-items:center;gap:10px;flex-wrap:wrap;cursor:pointer;
  padding:9px 6px 9px 22px;border-top:1px dashed var(--line)}
.v9 .tabzeile b{font-size:13px}
.v9 .tabzeile:hover{background:#f8fafb}
.v9 .tabzeile .chev{margin-left:auto;color:var(--muted);flex-shrink:0}
.v9 .tabzeile .s{color:var(--muted);font-size:12px}
.v9 .tabzeile .s.warn,.v9 .param .wert.warn{color:var(--warn);font-weight:700}
.v9 .tabzeile .s.bad,.v9 .param .wert.bad{color:var(--bad);font-weight:700}

/* PARAMETER-ZEILEN der aufgeklappten Tablet-Zeile: je eigener Punkt links, Wert rechts. */
.v9 .param{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:7px 6px 7px 34px;border-top:1px solid #f0f2f5;font-size:12.5px}
.v9 .param .wert{margin-left:auto;color:var(--muted);font-size:12px;text-align:right}

/* ---- Hinweis-Box ---- */
.v9 .hinweis{background:var(--warnbg);border:1px solid #ecd9ae;border-radius:10px;
  padding:10px 12px;font-size:12.5px;margin-bottom:12px}

/* ---- iPhone-Modus (Enrico 09.08.: das Dashboard wird vor allem am iPhone benutzt) ---- */
.v9 .mtop{display:none}

/* SAFE-AREA (v421, 16.08.2026 — Enrico-Notfall "Kopfzeile hinter der Uhr, verschwindet").
   index.html:5 setzt `viewport-fit=cover`: iOS zieht die Seite damit UNTER Statusleiste,
   Notch/Dynamic Island und Home-Indicator. Wer das anfordert, MUSS die Insets selbst
   zurueckgeben - der Bestand tut das seit jeher (style.css:62 `header.top`), v9.css hatte
   keine einzige `env()`-Regel.
   RECHENWEG des Fehlers: .mtop war 10px + 24px Icon + 10px = 44px hoch, der Inset am iPhone
   mit Dynamic Island ist ~59px -> die Leiste lag VOLLSTAENDIG unter der Statusleiste. Das ist
   EIN Fehler mit zwei Symptomen: "sitzt hinter der Uhr" UND "verschwindet beim Scrollen"
   (sie war nie sichtbar, `position:sticky` war die ganze Zeit korrekt).
   Die 44px stecken ausserdem hartkodiert im `top` der Mobil-Nav - wandert die Leiste nach
   unten, muss die Nav mitwandern, sonst schiebt sich ihr Kopf unter die Leiste.
   Waechter: backend/tests/test_v421_safe_area.py */
@media(max-width:700px){
  /* OLHA-AUFLAGE A1 (16.08.2026): die Hoehe der Kopfleiste stand an ZWEI Stellen als
     hartkodierte 44 - und beide waren falsch. Gemessen sind es 49.39px: hoechster
     Flex-Teilnehmer ist nicht das 24px-Icon, sondern der Rettungsring `.maltlink`
     (12px/600 mit eigener Zeilenhoehe). Die Mobil-Nav sass dadurch ~5px zu hoch, und die
     in v2.71 neue `.kgruppe` waere 9px zu hoch geklebt - kalibriert an meiner Beleg-
     Attrappe statt an der echten Leiste. Jetzt EIN Token, das beide nutzen: wandert die
     Leiste, wandert alles mit. 50px = gemessene 49.39 aufgerundet (nie kleiner als die
     Leiste, sonst schiebt sich etwas darunter). */
  .v9{--mtop-h:50px}
  /* v3.01 TIPPZIEL DES MENUE-KNOPFES — die Leiste wird dabei NICHT hoeher.
     Der Knopf mass 28px (2px Polster + 24px Icon + 2px). Er waechst auf 44px und holt sich
     den Platz aus der EIGENEN Polsterung der Leiste zurueck: 10px -> 3px oben wie unten.
     RECHENWEG: 3 + 44 + 3 = 50px = genau `--mtop-h`. Das Token bleibt damit gueltig, und
     mit ihm `.nav` und `.kgruppe`, die beide darauf rechnen (Olha-A1 / v2.71-Lehre: eine
     Leistenhoehe, die zwei Elemente positioniert, darf nicht unbemerkt wandern).
     SICHTBAR aendert sich NICHTS - das ist nachgerechnet, nicht gehofft:
       waagerecht  alt: Knopf 14..42 (28 breit), Icon-Mitte 28, Logo beginnt bei 42+10=52
                   neu: `margin:0 -8px` -> Knopf 6..50 (44 breit), Icon-Mitte 6+22=28,
                        Logo beginnt bei 50-8+10=52. Beide Werte IDENTISCH.
       senkrecht   alt: Polster 10 + hoechstes Kind 29.39 (der Rettungsring, NICHT das Icon)
                        = 49.39 hoch, Icon-Mitte 24.7
                   neu: 3 + 44 + 3 = 50 hoch, Icon-Mitte 25. Abweichung 0.3px bzw. 0.6px.
     Gewachsen ist also ausschliesslich die Beruehrungsflaeche (28x28 -> 44x44).
     Gemessen im 390-px-Beleg dieser Schicht, nicht nur gerechnet. */
  .v9 .mtop{display:flex;align-items:center;gap:10px;position:sticky;top:0;z-index:60;
    background:var(--nav);color:#fff;font-weight:700;padding:3px 14px;
    padding-top:calc(3px + env(safe-area-inset-top));
    padding-left:calc(14px + env(safe-area-inset-left));
    padding-right:calc(14px + env(safe-area-inset-right))}
  /* OLHA-W5 (16.08.2026): `.kgruppe` klebte bei top:0 und parkte damit UNTER der Kopfleiste
     (.mtop ist sticky mit z-index 60). Die Kategorie-Zeile war auf dem iPhone unsichtbar —
     also genau auf dem Geraet, an dem die Ruege entstand, und genau der Zweck, den der
     Kommentar an der Regel selbst behauptet. Sie haengt jetzt unter der Leiste; z-index
     bleibt unter 60, damit die Leiste oben bleibt. */
  .v9 .kgruppe{top:calc(var(--mtop-h) + env(safe-area-inset-top))}
  .v9 .mtop button{background:none;border:none;color:#fff;padding:0;cursor:pointer;
    display:inline-flex;align-items:center;justify-content:center;
    min-width:44px;min-height:44px;flex-shrink:0;margin:0 -8px}
  .v9 .mtop button svg{width:24px;height:24px;display:block}
  /* MOBILE-FIXIERUNG: die Kopfzeile bekommt mit dem Rettungsring einen zweiten Textblock.
     Damit die beiden bei 390px die Leiste NIEMALS aufziehen koennen (Fehler-Gedaechtnis
     "kundensichtbares-ohne-mobile-und-nutzer-brille"), ist der Titel schrumpfbar und
     kuerzt sich selbst - nicht die Leiste waechst, sondern der Titel weicht. */
  .v9 .mtop .mlogo{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .v9 .mtop .mlogo span{color:var(--stahl)}
  /* RETTUNGSRING (Enrico 16.08.): der Weg zurueck ins alte Dashboard hing bisher NUR in der
     Nav-Spalte - also hinter dem Hamburger, also hinter genau der Leiste, die kaputt war.
     Ein Rollback-Weg darf nie vom Layout abhaengen, das er retten soll. Darum steht er jetzt
     zusaetzlich offen in der Kopfzeile. Layout-unabhaengiger Notausgang bleibt die URL
     `#/dashboard-alt` (v9Uebernimmt), die ohne jedes CSS funktioniert. */
  .v9 .mtop .maltlink{margin-left:auto;font-size:12px;font-weight:600;color:var(--stahl);
    text-decoration:underline;cursor:pointer;white-space:nowrap;padding:6px 0}
  .v9 .layout{display:block}
  .v9 .nav{position:fixed;left:0;top:calc(var(--mtop-h) + env(safe-area-inset-top));bottom:0;
    width:250px;z-index:55;overflow-y:auto;
    padding-left:calc(10px + env(safe-area-inset-left));
    transform:translateX(-100%);transition:transform .18s ease}
  .v9 .nav .logo{display:none}
  .v9 .nav.offen{transform:none;box-shadow:4px 0 24px rgba(0,0,0,.35)}
  .v9 .main{padding:12px 14px;max-width:100%;
    padding-bottom:calc(12px + env(safe-area-inset-bottom));
    padding-left:calc(14px + env(safe-area-inset-left));
    padding-right:calc(14px + env(safe-area-inset-right))}
  .v9 .kopf .wer{width:100%;margin-left:0}
  .v9 .kopf h1{font-size:21px}
  .v9 .grid{grid-template-columns:1fr}
  .v9 .kachel{min-height:0;padding:16px}
  /* Tippziele >=44px auf Touch-Geraeten */
  .v9 .nav a{padding:12px 10px;font-size:14.5px}
  .v9 .item{padding:11px 6px}
  .v9 .btn{padding:11px 16px;font-size:14px}
  /* v3.01 TIPPZIEL DES GLOBALEN ZURUECK-KNOPFES. Er steht auf JEDER V9-Unterseite (AK-Q1)
     und mass im 390-px-Beleg 38px: 11+11 Polster plus 16px Zeilenhoehe bei 14px Schrift.
     Warum nicht `.v9 .btn{min-height:44px}` hausweit, obwohl es dieselbe eine Zeile waere:
     das haette JEDEN Knopf der Oberflaeche mitverschoben - Rechte-Schalter, Filterzeilen,
     Dialogfuesse. Ob die 44px dort ueberall richtig sind, ist eine offene Enrico-Frage und
     ausdruecklich NICHT Teil dieses Releases. Darum traegt der Zurueck-Knopf jetzt eine
     eigene Klasse (dashboard_v9.js, v9Kopf) und nur er waechst.
     Die Klasse ist HIER definiert - `sec` war jahrelang genau das Gegenteil (Fehler-
     Gedaechtnis "benutzte-css-klasse-ist-nirgends-definiert"); ein Waechter haelt das fest. */
  .v9 .btn.zurueck{min-height:44px}
  /* Listenzeile bricht auf dem iPhone in ZWEI Reihen: Text oben, Pillen darunter.
     Ohne das teilen sich Text und drei Status-Pillen die 390 px, der Textblock wird auf
     ~50 px gequetscht und `overflow-wrap:anywhere` bricht den Benutzernamen dann
     BUCHSTABENWEISE um ("gast-alber s — Herr Albe rs"). Im Code unsichtbar, im
     Mobil-Beleg vom 16.08. sofort. Die Fixierung selbst war nie verletzt - die Seite war
     nicht pannbar, nur unlesbar.
     Der Umbruch des TEXTBLOCKS gilt NUR fuer Zeilen, die mit `.zweizeilig` ausgezeichnet sind
     (Konto-, Objekt- und Terminzeilen mit Status-Pillen). Bewusst KEINE `:has()`-Regel: die
     gibt es erst ab Safari 15.4, und ein aelteres iPhone bekaeme dann still die zerquetschte
     Fassung. Zeilen ohne die Klasse (z.B. der Verlauf) behalten `flex:1` und damit
     den Ampelpunkt NEBEN dem Text.
     KORREKTUR Olha-Auflage A1 (16.08.): der Satz "gilt NUR fuer .zweizeilig" stimmte fuer die
     ZWEITE Regel nie — `.v9 .item .pillen` greift bei jeder Listenzeile. Das ist so gewollt
     (eine Pillenreihe soll immer umbrechen duerfen), war aber falsch beschrieben. */
  .v9 .item.zweizeilig .grow{flex-basis:100%}
  .v9 .item .pillen{flex-basis:100%}
  /* GERAETEVERWALTUNG (v2.96): Knoepfe GESTAPELT in voller Breite unter dem Text.
     Enrico-Auflage 18.08. und zugleich die Mobile-Fixierungs-Regel: nebeneinander wuerden
     zwei bis drei Knoepfe den Textblock auf wenige Woerter je Zeile quetschen - genau der
     Defekt, den die abgenommene Desktop-Vorlage bei 390 px zeigt. `flex-basis:100%` statt
     einer :has()-Regel, aus demselben Grund wie oben (Safari < 15.4). */
  .v9 .mgzeile .grow{flex-basis:100%}
  /* max-width:none ist PFLICHT: der Desktop-Deckel von 40% wuerde sonst auch hier gelten
     und die 'vollbreiten' Knoepfe blieben schmale Kaestchen mit dreizeiligem Text -
     im 390-px-Bild sofort sichtbar, im Quelltext nicht. */
  .v9 .mgknoepfe{flex-basis:100%;max-width:none;flex-direction:column;align-items:stretch;gap:6px}
  .v9 .mgknoepfe .btn{width:100%;justify-content:center;min-height:44px}
  /* Der Sprungknopf dieser Seite ist am Telefon ein Tippziel wie jeder andere. GEMESSEN im
     390-px-Bild: ohne diese Zeile 38 px. Bewusst NUR dieser Knopf und nicht `.v9 .btn`
     hausweit - das haette zwoelf andere Seiten mitveraendert, die nicht zu diesem Auftrag
     gehoeren. Der hausweite Fall (`‹ Zurueck`, ebenfalls 38 px) steht als Posten im Bericht. */
  .v9 .mgsprung{min-height:44px}
  /* v2.99 GERAETE-KLAPP-KACHEL AM TELEFON. Der Kopf traegt zwei Textzeilen UND bis zu drei
     Status-Bausteine; nebeneinander bliebe vom Namen nichts uebrig (Fehler-Gedaechtnis
     "kachel-kopfzeile-kuerzt-bei-390px-den-namen-statt-der-zahl"). Feste Ordnung stattdessen:
     Zeile 1 Titelblock + Pfeil, Zeile 2 der Status rechtsbuendig. */
  .v9 .klapp>h2.mgkopf .ktitel.mgtitel{flex:1 1 0}
  .v9 .mgstatus{order:1;flex-basis:100%;justify-content:flex-end;margin-top:3px}
  /* Einzuege der Tablet-Zeile zurueckfahren: 22 bzw. 34 px Einzug plus Punkt plus Text
     lassen bei 390 px keinen lesbaren Rest. Der Wert rutscht unter den Namen statt ihn
     zu quetschen. */
  .v9 .tabzeile{padding-left:6px}
  .v9 .param{padding-left:14px}
  .v9 .param .wert{margin-left:0;flex-basis:100%;text-align:left}
  .v9 .mgkal input[type=date]{flex:1 1 100%;min-height:44px}
  .v9 .mgkal .btn{flex:1 1 45%;justify-content:center;min-height:44px}
}

/* v3.17 (19.08.2026): Regeln aus Inline-Stilen hierher gezogen (Waechter
   test_jede_benutzte_klasse_ist_in_v9_css_definiert, Fehlerklasse
   benutzte-css-klasse-ist-nirgends-definiert). Werte 1:1 unveraendert. */
.v9 .pz-detail{margin:2px 0 14px 12px;padding:2px 0 2px 12px;border-left:3px solid var(--kante,#8a93a0)}
.v9 .pwiz-gruppe>summary{cursor:pointer;min-height:44px;display:flex;align-items:center}
/* v3.18 (19.08.2026): Abmelden unten in der Nav - 44px-Tippziel, dezent wie der Altlink. */
.v9 .altlink.abmelden{margin-top:2px}
.v9 .abmelden a{display:inline-flex;align-items:center;min-height:44px}
/* v3.23 (20.08.2026, Enrico: "gruen, fertig, wie im alten Dashboard oder noch offen, grau"):
   Ueberblicks-Zeile abgeschlossener Objekte in der Termine-Kachel - gruen getoent. */
.v9 .item.fertigzeile{background:var(--okbg)}
/* A22 (22.08.2026, Welle 4-Rest): gelbe Zeile fuer Portal-Konten mit Handlungsbedarf
   (ohne Hauptkontakt / im Bau / eingeladen seit X Tagen) - dieselbe Optik wie .fertigzeile,
   nur die WARN-Farbe statt OK (keine neue Farbklasse, AK-Q5 bleibt Rot echten Zustaenden). */
.v9 .item.warnzeile{background:var(--warnbg)}
/* P211/A15-1 (23.08.2026, Reparatur-Block Posten 11): Sammelansicht #/gesperrt bekommt je
   Zeile ein Thumbnail + eine kompakte Messwertzeile (dashboard_v9_gesperrt.js). Eigene,
   knappe Klassen statt Inline-Stil (Waechter test_jede_benutzte_klasse_ist_in_v9_css_
   definiert) - Praefix `v9gd-` (Gesperrt-Detail) haelt sie von den hausweiten `.mg*`/`.tab*`
   Klassen der Geraeteverwaltung fern. */
.v9 .v9gd-thumb{width:40px;height:40px;object-fit:cover;border-radius:8px;flex-shrink:0;
  background:#eef2f6;display:block}
.v9 .v9gd-mess{display:flex;flex-wrap:wrap;gap:2px 10px;margin-top:2px}
.v9 .v9gd-mess .v9gd-flag-f{color:var(--bad);font-weight:700}
.v9 .v9gd-mess .v9gd-flag-p{color:var(--ok)}
.v9 .v9gd-defekt{color:var(--bad);margin-top:2px}
/* Fehler-Gedaechtnis "karte-meldet-leermenge-statt-routen-ausfall": eigene, deutlich
   erkennbare Farbe statt stillem `.s` - eine ausgefallene Detail-Route soll auffallen,
   nicht wie eine normale Zusatzzeile aussehen. */
.v9 .v9gd-fehlt{color:var(--warn);margin-top:2px}
