:root {
  --bg:#0b1623; --card:#121b2a; --card2:#0f1826; --border:#22304a;
  --text:#e7ecf3; --muted:#8a98ad; --accent:#00AEEF; --accent-hover:#0089B6;
  --green:#22c55e; --orange:#f59e0b; --red:#ef4444;
}
* { box-sizing:border-box; }
html, body { margin:0; min-height:100%; }
body {
  background:var(--bg); color:var(--text);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;
}
header {
  display:flex; align-items:center; justify-content:space-between;
  padding:16px 24px; border-bottom:1px solid var(--border);
  background:var(--card2); position:sticky; top:0; z-index:10;
}
header .brand { display:flex; align-items:center; gap:12px; }
header .logo {
  width:36px; height:36px; border-radius:9px; display:flex; align-items:center; justify-content:center;
  background:linear-gradient(135deg,#00AEEF,#225ab4); font-weight:800; color:#fff;
}
header h1 { font-size:16px; margin:0; font-weight:700; }
header .user { color:var(--muted); font-size:13px; }
header .user b { color:var(--text); }
.wrap { max-width:920px; margin:0 auto; padding:24px; display:flex; flex-direction:column; gap:16px; }
.col { display:flex; flex-direction:column; gap:16px; }
/* EIN-Spalten-Layout ueber die volle Breite. Die frueher rechts klebende
   Ergebnisspalte (.right/#resultPane) ist ersatzlos entfallen - das Ergebnis
   steht jetzt in einer eigenen Registerkarte. Damit gibt es auch keinen
   "Vollbreiten-Modus" (.layout.full/.tabbar.full) mehr: ALLE Registerkarten
   sind breit, die Maximalbreite ist die frueher nur dort geltende. */
.layout { max-width:1800px; margin:0 auto; padding:24px; }
.layout .pages { min-width:0; display:flex; flex-direction:column; gap:16px; }
/* Rechen-Kopf in der Reiterleiste: Statusanzeige, "neu berechnen"-Knopf,
   Fortschrittsbalken und Klartext-Meldung fehlender Pflichteingaben. Sass
   frueher im Kopf der Ergebnisspalte - dort waere er jetzt nur noch auf der
   Registerkarte "Ergebnis" sichtbar, geaendert wird aber unter "Eingaben".
   In der klebenden Leiste ist er auf JEDER Registerkarte erreichbar. */
.calc-head { flex:0 1 auto; min-width:240px; max-width:min(560px, 100%); padding-bottom:8px; }
.calc-head-row { display:flex; align-items:center; justify-content:flex-end; gap:10px; flex-wrap:wrap; }
.calc-indicator { font-size:12px; font-weight:700; color:var(--accent); min-height:16px; text-align:right; }
/* DER VERALTET-ZUSTAND, SEIT DEM 21.08.2026 AN EINEM KNOPF STATT AN EINEM
   EIGENEN. Bis dahin gab es hier .recalc-btn - einen zweiten, orangen
   Rechenknopf in der Reiterleiste. Er ist entfallen (er rechnete EINEN Fall,
   den es als Eingabe gar nicht gibt); die Markierung sitzt jetzt auf dem einen
   verbliebenen Knopf #designRun, den syncRunKnopf() mit .recalc-due versieht.
   Farbe und Puls sind unveraendert - der Zustand soll aussehen wie bisher. */
.recalc-due {
  background:var(--amber, #f59e0b) !important; color:#1a1a1a !important; border:none;
  box-shadow:0 0 0 0 rgba(245,158,11,0.5); animation:recalcPulse 1.6s ease-in-out infinite;
}
.recalc-due:hover { filter:brightness(1.08); }
@keyframes recalcPulse {
  0%   { box-shadow:0 0 0 0 rgba(245,158,11,0.55); }
  70%  { box-shadow:0 0 0 8px rgba(245,158,11,0); }
  100% { box-shadow:0 0 0 0 rgba(245,158,11,0); }
}
.calc-indicator.busy::before { content:"● "; animation:pulse 1s ease-in-out infinite; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
/* ============ DIE SAMMELSTELLE: "WARUM GEHT ES NICHT" ============
   Der Kasten haengt seit dem 21.08.2026 NICHT mehr im Rechen-Kopf, sondern
   direkt darunter - der Kopf verschwindet auf allen Registerkarten ausser
   "Auslegung", die Meldungen darin verwiesen aber auf genau diese anderen
   Registerkarten und verschwanden damit beim Hinwechseln. */
.input-problems {
  margin-top:8px; padding:9px 12px; border-radius:8px; font-size:12px; line-height:1.5;
  background:rgba(239,68,68,0.10); border:1px solid var(--red); color:var(--text);
}
/* EIN BEFUND. Dieselbe Bauform in der Sammelstelle und an der Kachel - zwei
   Bauformen waeren zwei Gelegenheiten, dass man die eine fuer eine andere
   Sorte Meldung haelt als die andere. */
.befund + .befund { margin-top:7px; padding-top:7px; border-top:1px solid rgba(255,255,255,0.10); }
/* WAS DER BEFUND BLOCKIERT - vorangestellt, weil das die Frage ist, mit der
   man auf diesen Kasten schaut. Rot fuer eine echte Sperre, orange fuer einen
   Hinweis, der nichts blockiert. */
.befund-was { font-weight:700; text-transform:uppercase; letter-spacing:0.03em; font-size:10.5px; }
.befund-fehler .befund-was { color:var(--red); }
.befund-hinweis .befund-was { color:var(--orange); }
/* Der Befundkasten AN DER KACHEL - dort, wo man handelt. Er sitzt zwischen
   Standzeile und Liste; der linke Balken trennt ihn von der Standzeile, die
   eine Aussage ueber den GUELTIGEN Zustand ist und kein Fehler. */
.lib-befund {
  margin:-6px 0 12px; padding:8px 11px; border-radius:8px; font-size:11.5px; line-height:1.5;
  background:rgba(239,68,68,0.08); border-left:3px solid var(--red); color:var(--text);
}
/* Traegt der Kasten NUR Hinweise, ist er orange und nicht rot - sonst waere
   die Farbe wertlos, und der naechste echte Fehler saehe aus wie der Hinweis
   von vorhin. Die Klasse setzt libBefundSchreiben(); bewusst keine
   :has()-Regel, damit das Merkmal auch pruefbar ist. */
.lib-befund.lib-befund-hinweis { background:rgba(245,158,11,0.08); border-left-color:var(--orange); }
/* Leeres Pflichtfeld: rot umrandet, damit man es ohne Suchen findet. */
input.field-missing { border-color:var(--red); background:rgba(239,68,68,0.08); }
input.field-missing:focus { border-color:var(--red); box-shadow:0 0 0 3px rgba(239,68,68,0.18); }

/* ============ In dieser Betriebsart FIXIERTES Feld ============
   HARTE REGEL: Felder werden NIE ausgeblendet. Gilt eine Eingabe in der
   gewaehlten Betriebsart nicht, wird sie ausgegraut, optisch gedaempft und auf
   ihren dann geltenden Festwert gesetzt - sie BLEIBT aber SICHTBAR. So sieht
   man weiterhin, welche Groessen es gibt, und erkennt zugleich, dass sie im
   aktuellen Szenario fest sind. Kein display:none, kein Entfernen aus dem DOM.
   Der Grund steht jeweils im title-Tooltip des Feldes. */
input.field-fixed, select.field-fixed, textarea.field-fixed {
  opacity:0.42; border-style:dashed; background:#0a1220; color:var(--muted);
  cursor:not-allowed; font-weight:400;
}
/* readonly-Felder (pvKwp/battKwh) sind sonst blau+fett - im fixierten Zustand
   muss die Daempfung gewinnen, sonst sieht ein fixiertes Feld aktiver aus als
   ein normales. */
input.field-fixed[readonly] { color:var(--muted); font-weight:400; }
input.field-fixed:focus, select.field-fixed:focus { box-shadow:none; border-color:var(--border); }
/* Beschriftung des fixierten Feldes mitdaempfen - ohne das wirkt nur der
   Eingabekasten inaktiv und die Zeile liest sich widerspruechlich. */
.field-fixed-box > label.f { opacity:0.5; }
.field-fixed-box.check { opacity:0.5; cursor:not-allowed; }
/* WARUM DIESES FELD GRAU IST - sichtbar, nicht nur im Tooltip (21.08.2026).
   Ein graues Feld ohne sichtbaren Grund ist von einem kaputten nicht zu
   unterscheiden; ein Tooltip ist ohne Maus nicht erreichbar und im Ausdruck
   weg. Bewusst gedaempft: es ist eine Erklaerung, keine Fehlermeldung. */
.field-why {
  margin-top:4px; font-size:10.5px; line-height:1.4; color:var(--muted);
  border-left:2px solid var(--border); padding-left:7px;
}
/* Ergebnis-Stapel je Betriebsart (autark / netzgekoppelt). Nur EINER ist
   sichtbar - das sind ERGEBNISSE, keine Eingabefelder; fuer sie gilt die
   Ausblende-Regel nicht. Flex, damit style.display='' wieder korrekt stapelt. */
.res-stack { display:flex; flex-direction:column; gap:16px; }
/* Warnung im Netz-Ergebnis (z.B. negative Netto-Spitzenreduktion). */
.grid-warn {
  margin-top:12px; padding:10px 12px; border-radius:8px; font-size:12px; line-height:1.55;
  background:rgba(239,68,68,0.10); border:1px solid var(--red); color:var(--text);
}
.grid-note {
  margin-top:12px; padding:10px 12px; border-radius:8px; font-size:12px; line-height:1.55;
  background:var(--card2); border-left:3px solid var(--orange); color:var(--text);
}
/* Mehrfachauswahl der Preisjahre - Hoehe aus size, nicht aus der Feld-Regel. */
select[multiple] { height:auto; padding:6px 8px; }
select[multiple] option { padding:3px 4px; }
/* Rueckwaertsrechnung "Was muesste gelten": erste Spalte linksbuendig (Text),
   Zahlen rechts. Die Zeile "Zusaetzlicher Erloes" ist die Zusammenfassung des
   Fehlbetrags und wird abgesetzt - sie beantwortet die Frage, mit der man ins
   Kundengespraech geht. */
table.be-table th { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em; text-align:left; }
table.be-table td:first-child, table.be-table th:first-child { text-align:left; }
table.be-table tbody tr:first-child td { background:rgba(0,174,239,0.08); font-weight:600; }
table.be-table tbody tr:first-child td:first-child { color:var(--accent); }
/* Ergebnistabelle der Netzrechnung: Optimum-Zeile hervorheben. */
table.grid-table tr.grid-opt td { background:rgba(0,174,239,0.12); font-weight:800; }
table.grid-table tr.grid-opt td:first-child { color:var(--accent); }
.calc-bar { height:3px; border-radius:2px; margin-top:6px; overflow:hidden; }
.calc-bar.busy { background:var(--card2); }
.calc-bar.busy::before { content:""; display:block; height:100%; width:40%; border-radius:2px; background:var(--accent); animation:indet 1.1s ease-in-out infinite; }
@keyframes indet { 0%{margin-left:-40%} 100%{margin-left:100%} }
@media (max-width:1024px) {
  /* Bei schmalen Fenstern wird die Reiterleiste mehrzeilig - dann frisst ein
     klebender Kopf zu viel Hoehe. */
  .tabbar { position:static; }
  .layout { padding:16px; }
}
/* Reiterleiste als eigene Kopfzeile ueber die volle Breite, klebt unter dem
   Seitenkopf. Der Inhalt bleibt auf dieselbe Maximalbreite zentriert wie der
   Rest, damit die Reiter auf breiten Monitoren nicht auseinanderfliegen.
   Rechts sitzt der Rechen-Kopf (.calc-head); wird es zu eng, bricht er als
   ganzer Block unter die Reiter um (flex-wrap). */
.tabbar {
  position:sticky; top:69px; z-index:9;
  background:var(--bg); border-bottom:1px solid var(--border);
  padding:10px 24px 0;
}
.tabbar .tabbar-inner {
  max-width:1800px; margin:0 auto;
  display:flex; flex-wrap:wrap; align-items:flex-end; justify-content:space-between; gap:4px 20px;
}
.tabbar .tabs { flex:1 1 auto; border-bottom:none; }
.tabs { display:flex; flex-wrap:wrap; gap:6px; border-bottom:1px solid var(--border); margin-bottom:-2px; }
.tabs .tab {
  padding:10px 18px; border:1px solid var(--border); border-bottom:none;
  border-radius:10px 10px 0 0; background:var(--card2); color:var(--muted);
  font-size:13px; font-weight:700; font-family:inherit; cursor:pointer;
}
.tabs .tab:hover { color:var(--text); }
.tabs .tab.active { background:var(--card); color:var(--accent); border-color:var(--border); }
.tabpage { display:flex; flex-direction:column; gap:16px; }
/* ============ Mehrspaltiges Raster fuer Eingabe-Panels ============
   Ohne Ergebnisspalte hat jede Registerkarte die volle Bildschirmbreite. Die
   Eingabe-Panels wuerden darin als endlose schmale Spalte untereinander
   haengen - deshalb auto-fit: so viele Spalten, wie bei 460 px Mindestbreite
   hineinpassen (auf einem 1800-px-Fenster drei), auf schmalen Fenstern
   automatisch eine. min(460px,100%) verhindert waagerechtes Ueberlaufen auf
   sehr schmalen Geraeten. align-items:start, damit ein kurzes Panel nicht auf
   die Hoehe des laengsten seiner Zeile aufgeblasen wird. */
/* Eingabe-Kacheln stehen UNTEREINANDER ueber die volle Breite, nicht nebeneinander.
   Grund: die Reihenfolge IST die Rechenreihenfolge - von oben nach unten laeuft man
   die Kette entlang. Ein mehrspaltiges Raster zerstoert genau diese Lesart, weil
   nebeneinander stehende Kacheln keine erkennbare Abfolge haben. */
.panel-grid { display:flex; flex-direction:column; gap:16px; }
/* Schrittnummer je Kachel - macht die Abfolge sichtbar und zitierbar
   ("Schritt 4 stimmt nicht"). Wird per CSS-Zaehler vergeben, damit beim
   Umsortieren nichts von Hand nachgezogen werden muss. */
.panel-grid { counter-reset: step; }
.panel-grid > .panel > h2::before {
  counter-increment: step;
  content: counter(step) " · ";
  color: var(--muted); font-weight: 800;
}
/* Innerhalb einer Kachel duerfen Felder weiterhin nebeneinander stehen - die
   volle Breite waere sonst verschenkt. */
.panel-grid > .panel .grid2,
.panel-grid > .panel .grid3, .panel-grid > .panel .grid4 { max-width: 1100px; }

/* KAPITEL mit Unterkacheln.
   Das technische Konzept ist EIN Schritt der Kette, kein Dutzend gleichrangige -
   sonst verschwindet die Lesart "Vorgaben oben, Entwurf in der Mitte, Bewertung
   unten" hinter lauter Nummern. Die Unterkacheln erben die Kapitelnummer und
   zaehlen darunter weiter (5.1, 5.2, ...), damit sie zitierbar bleiben. */
.chapter { counter-reset: sub; }
.chapter > .subpanel {
  background: var(--card2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  margin: 0 0 14px;
}
.chapter > .subpanel:last-of-type { margin-bottom: 0; }
.chapter > .subpanel > h3 {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--accent); margin: 0 0 12px; font-weight: 700;
}
.chapter > .subpanel > h3::before {
  counter-increment: sub;
  content: counter(step) "." counter(sub) " · ";
  color: var(--muted); font-weight: 800;
}
.chapter .grid2, .chapter .grid3, .chapter .grid4 { max-width: 1100px; }
/* Peripherie-Positionen: je Position Auswahl + zwei Preisfelder, durch eine
   duenne Linie getrennt. Ohne Trenner verschwimmen zehn gleich aussehende
   Dreier-Zeilen zu einer Wand. */
.peri-group { padding: 12px 0; border-top: 1px solid var(--border); }
.peri-group:first-child { border-top: 0; padding-top: 0; }
.peri-group > label.f { text-transform: none; font-size: 13px; color: var(--text); }
.peri-group .peri-note { font-size: 11px; font-style: italic; }
/* Die gesammelte Rechenkette am Kapitelende abgesetzt, damit erkennbar ist:
   ab hier steht nicht mehr, was man eintraegt, sondern was daraus folgt. */
.chapter > .stage-slot:not(:empty) {
  margin-top: 18px; padding-top: 16px; border-top: 2px solid var(--border);
}
/* Der Rechenweg bleibt einspaltig und vollbreit: Rechenkette und Zeitlupe
   brauchen die ganze Breite (Tabellen, Stundendiagramm). */
.trace-sec { display:flex; flex-direction:column; gap:16px; }
.panel.locked { text-align:center; padding:40px 24px; }
.panel.locked .lock-icon { font-size:40px; line-height:1; margin-bottom:10px; }
.panel.locked h2 { color:var(--muted); }
.panel { background:var(--card); border:1px solid var(--border); border-radius:14px; padding:18px 20px; }

/* ======================================================================
   DIE DREI EBENEN DER REGISTERKARTE "AUSLEGUNG" (27.08.2026)
   ======================================================================
   Ein Trennstreifen, keine Kachel: er soll den Stapel gliedern, nicht mit
   den Bloecken darunter um Aufmerksamkeit ringen. Deshalb kein Rahmen und
   kein Hintergrund, nur eine Linie darueber und viel Luft. */
.ebene {
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
  margin:22px 0 -2px; padding-top:16px; border-top:1px solid var(--border);
}
.ebene-nr {
  flex:0 0 auto; width:22px; height:22px; border-radius:50%;
  background:var(--accent); color:#04121d;
  font-size:12px; font-weight:800; line-height:22px; text-align:center;
}
.ebene-titel { font-size:15px; font-weight:700; letter-spacing:-0.01em; color:var(--text); }
.ebene-sub { font-size:12px; color:var(--muted); }

/* ---- Zugeklappte Bloecke (Ebene 3 und die Monatsbilanz) --------------
   Der Kopf sieht aus wie die <h2> eines Panels - dieselbe Groesse, dieselbe
   Farbe, dieselbe Versalschrift -, damit ein zugeklappter Block nicht wie
   ein Fremdkoerper zwischen offenen steht. Dazu ein Dreieck und ein Zeiger,
   denn ein Element, das auf Klick etwas tut und wie reiner Text aussieht,
   findet niemand. */
.panel-klapp { padding:14px 20px; }
.klapp > .klapp-kopf {
  cursor:pointer; list-style:none; user-select:none;
  font-size:13px; text-transform:uppercase; letter-spacing:0.05em;
  color:var(--accent); font-weight:700;
  display:flex; align-items:baseline; gap:8px; flex-wrap:wrap;
}
.klapp > .klapp-kopf::-webkit-details-marker { display:none; }
.klapp > .klapp-kopf::before { content:"\25B8"; font-size:11px; }
.klapp[open] > .klapp-kopf::before { content:"\25BE"; }
.klapp > .klapp-kopf:hover { color:var(--text); }
.klapp-hint { text-transform:none; letter-spacing:0; font-weight:400; font-size:12px; color:var(--muted); }
.klapp-body { padding-top:14px; }
/* Innerhalb eines Panels (Monatsbilanz unter ihrem Diagramm): eine Stufe
   leiser als die Panel-Ueberschrift, mit Trennlinie nach oben. */
.klapp-innen { margin-top:16px; border-top:1px solid var(--border); padding-top:12px; }
.klapp-innen > .klapp-kopf { color:var(--muted); }
.klapp-innen > .klapp-kopf:hover { color:var(--accent); }

.panel h2 { font-size:13px; text-transform:uppercase; letter-spacing:0.05em; color:var(--accent); margin:0 0 14px; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.grid3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px; }
/* Vier Felder nebeneinander - fuer die Funklast-Ableitung, seit die Zahl der
   Mieter (Betreiber am Mast) als eigene Groesse danebensteht. */
.grid4 { display:grid; grid-template-columns:repeat(4, 1fr); gap:12px; }
label.f { display:block; font-size:11px; font-weight:600; color:var(--muted); margin:0 0 5px; text-transform:uppercase; letter-spacing:0.03em; }
input, select {
  width:100%; padding:10px 11px; border-radius:9px; border:1px solid var(--border);
  background:#0b1322; color:var(--text); font-size:14px; font-family:inherit; outline:none;
}
input:focus, select:focus { border-color:var(--accent); box-shadow:0 0 0 3px rgba(0,174,239,0.16); }
input[readonly] { background:#0e1a30; color:var(--accent); font-weight:700; cursor:default; }
input[readonly]:focus { box-shadow:none; border-color:var(--border); }
textarea {
  width:100%; padding:10px 11px; border-radius:9px; border:1px solid var(--border);
  background:#0b1322; color:var(--text); font-size:14px; font-family:inherit; outline:none; resize:vertical;
}
textarea:focus { border-color:var(--accent); box-shadow:0 0 0 3px rgba(0,174,239,0.16); }
.topic-row { background:var(--card2); border:1px solid var(--border); border-radius:9px; padding:10px 12px; margin-bottom:8px; }
.topic-row .topic-head { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.topic-row .topic-title { font-size:13px; font-weight:700; }
.topic-row .topic-title.done { text-decoration:line-through; color:var(--muted); }
.topic-row .topic-desc { font-size:12px; color:var(--muted); margin-top:4px; white-space:pre-wrap; }
.topic-badge { font-size:10px; font-weight:700; text-transform:uppercase; padding:2px 8px; border-radius:20px; }
.topic-badge.open { background:rgba(0,174,239,0.15); color:var(--accent); }
.topic-badge.done { background:rgba(34,197,94,0.15); color:var(--green); }
.check { display:flex; align-items:center; gap:8px; }
.check input { width:auto; }
button.primary {
  width:100%; padding:13px; border:none; border-radius:10px; background:var(--accent); color:#fff;
  font-size:15px; font-weight:700; font-family:inherit; cursor:pointer; transition:background .15s; margin-top:4px;
}
button.primary:hover:not(:disabled) { background:var(--accent-hover); }
button.primary:disabled { opacity:0.6; cursor:default; }
button.ghost {
  padding:8px 12px; border:1px solid var(--border); border-radius:8px; background:transparent;
  color:var(--text); font-size:12px; font-weight:600; cursor:pointer;
}
button.ghost:hover { border-color:var(--accent); }
/* Physikalische Aufbaukosten: Kurve + Aufschluesselung je Komponente */
.ccost-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:14px; }
.ccost-card { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:10px 12px; }
.ccost-head { display:flex; justify-content:space-between; align-items:baseline; font-size:14px; margin-bottom:4px; }
.ccost-table { width:100%; font-size:12px; margin:6px 0 4px; }
.ccost-table td { padding:2px 0; }
.ccost-table td:last-child { text-align:right; font-weight:600; white-space:nowrap; }
.ccost-table tr.ccost-total td { border-top:1px solid var(--border); padding-top:4px; color:var(--accent); }
.ccost-tiers { font-size:11px; color:var(--muted); line-height:1.5; margin-top:4px; }
/* ============ Korridor-Zeile einer Eingangsgroesse ============
   "Optimierung" ist kein eigener Arbeitsschritt, sondern eine EIGENSCHAFT jeder
   Eingangsgroesse: fester Wert ODER Korridor (von/bis/Schritt). Die Zeile sitzt
   deshalb direkt unter dem zugehoerigen Eingabefeld in den Technischen
   Eingangsgroessen - nicht in einer zweiten Maske im Optimierungs-Reiter.
   HARTE REGEL: nichts wird ausgeblendet. Die gerade nicht geltende Seite wird
   ueber .field-fixed (siehe oben) ausgegraut und bleibt sichtbar. */
.corridor {
  display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:12px;
  background:var(--card2); border:1px solid var(--border); border-radius:10px;
  padding:8px 10px; font-size:12px; color:var(--muted);
}
.corridor .corr-lab {
  font-size:11px; font-weight:600; color:var(--muted);
  text-transform:uppercase; letter-spacing:0.03em; margin-right:2px;
}
.corridor select { width:auto; margin:0; padding:6px 8px; font-size:12px; }
.corridor .corr-range { display:inline-flex; align-items:center; flex-wrap:wrap; gap:4px; }
.corridor .corr-range input { width:66px; margin:0 2px; padding:5px 6px; font-size:12px; }
.corridor .unit { margin-left:4px; }
/* Freigegebener Korridor: die Zeile fuehrt jetzt den Wert der Groesse - deshalb
   deutlich abgesetzt (das zugehoerige Hauptfeld ist im Gegenzug ausgegraut). */
.corridor.is-range { border-color:var(--accent); background:rgba(0,174,239,0.06); }
.corridor.is-range .corr-lab { color:var(--accent); }
/* ============ „Daraus folgt" — sofort ableitbare Folgegroessen ============
   Sitzt UNTEN in der Eingabe-Kachel, optisch abgesetzt: duenne Trennlinie
   darueber, gedaempfte und kleinere Schrift als der Rest der Kachel, Werte
   fett. Inhalt ist reine Arithmetik aus den Feldern DERSELBEN Kachel (kein
   Server, keine Preisreihe, keine Simulation) - deshalb darf sich die Zeile
   live beim Tippen bewegen. Sie ist ANZEIGE und keine Eingabe: sie loest
   bewusst kein "Ergebnis nicht mehr aktuell" aus. */
.derived {
  margin-top:14px; padding-top:10px; border-top:1px solid var(--border);
  font-size:11.5px; line-height:1.8; color:var(--muted);
}
.derived:empty { display:none; }
.derived .drv-lab {
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:0.05em;
  color:var(--muted); opacity:0.8; margin-right:8px;
}
.derived b { color:var(--text); font-weight:700; }
.derived .drv-sep { margin:0 7px; opacity:0.4; }
.derived .drv-warn { color:var(--orange); font-weight:700; }
/* Gegenstueck zu .drv-warn: was der Katalogeintrag WIRKLICH in die Rechnung
   bringt. Es steht in derselben Zeile wie die Luecken daneben - eine Kachel,
   die nur Warnungen zeigt, sagt nicht, was sie kann. */
.derived .drv-ok { color:var(--green); font-weight:700; }
.derived .drv-note { display:block; margin-top:5px; opacity:0.9; }
/* Kachel gilt in der gewaehlten Betriebsart nicht: die Zeile wird - wie die
   Felder darueber - nur GEDAEMPFT, nie ausgeblendet. */
.derived.is-inactive { opacity:0.5; }
.derived.is-inactive b { color:var(--muted); }

/* HIER STAND BIS ZUM 21.08.2026 .opt-summary (samt .opt-sum-name/-n/-range/
   -fixed/-foot/-block) - das Stilblatt der ZWEITEN Vorabschaetzung
   (#optEstimate, autark). Es gibt nur noch EINE Anzeige der Raumgroesse, und
   die traegt in beiden Betriebsarten dieselben Klassen (.space-axes /.kpis).
   Totes Stilblatt ist dieselbe Falle wie ein toter Leser: es sieht aus, als
   gebe es die Sache noch. */
/* Sensitivitaetsanalyse: Slider + Sparkline je Parameter */
.sens-row { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:10px 12px; margin-bottom:12px; }
.sens-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; font-size:13px; margin-bottom:2px; flex-wrap:wrap; }
.sens-readout { font-size:12px; color:var(--muted); }
.sens-spark { margin:2px 0 4px; }
.sens-slider { width:100%; margin:0; cursor:pointer; accent-color:#22c55e; }
.kpis { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.kpi { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:12px 14px; }
.kpi .v { font-size:22px; font-weight:800; letter-spacing:-0.01em; }
.kpi .l { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em; margin-top:2px; }
.kpi.good .v { color:var(--green); } .kpi.warn .v { color:var(--orange); } .kpi.bad .v { color:var(--red); }
/* Wirtschaftlichkeits-Farbgruppen: GELB=Investition/CAPEX, ROT=Betrieb/OPEX, BLAU=Lebenszyklus/NPC */
.kpi.cap  { border-left:3px solid #eab308; } .kpi.cap  .v { color:#eab308; }
.kpi.op   { border-left:3px solid var(--red); } .kpi.op  .v { color:var(--red); }
.kpi.life { border-left:3px solid var(--accent); } .kpi.life .v { color:var(--accent); }
/* DATENLUECKE AN EINER ERGEBNIS-KACHEL. Sie faerbt die BESCHRIFTUNG und nicht
   den Wert: der Wert ist richtig gerechnet, nur seine Quelle ist unscharf
   ("Preis auf Anfrage", "Preis aus Klassenformel"). Ein orange gefaerbter Wert
   haette behauptet, die Zahl selbst sei fragwuerdig. */
.kpi .l .datenluecke { color:var(--orange); text-transform:none; letter-spacing:0; }
/* Wirtschaftlichkeit — Aufschluesselung (Berechnungsdetails) */
.eb-grid { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
@media (max-width:680px){ .eb-grid { grid-template-columns:1fr; } }
.eb-block { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:12px 14px; border-left:3px solid var(--border); }
.eb-block h3 { margin:0 0 8px; font-size:14px; font-weight:700; }
.eb-block h3 .muted { font-weight:400; font-size:12px; }
.eb-block p.muted { margin:8px 0 0; font-size:12px; line-height:1.5; }
.eb-cap  { border-left-color:#eab308; } .eb-cap  h3 { color:#eab308; }
.eb-op   { border-left-color:var(--red); } .eb-op  h3 { color:var(--red); }
.eb-life { border-left-color:var(--accent); } .eb-life h3 { color:var(--accent); }
.eb-lcoe { border-left-color:var(--green); } .eb-lcoe h3 { color:var(--green); }
.eb-table { width:100%; border-collapse:collapse; font-size:13px; }
.eb-table td { padding:4px 0; border-bottom:1px solid var(--border); }
.eb-table td .muted { font-size:11px; }
.eb-table tr.eb-sum td { font-weight:800; border-top:2px solid var(--border); border-bottom:none; padding-top:6px; }
.eb-glossary { margin-top:12px; font-size:12px; line-height:1.6; }
.eb-glossary b { color:var(--text); }
/* Stueckliste (BOM) je Komponente - vollbreit */
.bom-block { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:14px 16px; margin-bottom:16px; }
.bom-block:last-child { margin-bottom:0; }
.bom-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:8px; flex-wrap:wrap; }
.bom-head b { font-size:15px; }
.bom-sub { font-size:11px; text-transform:uppercase; letter-spacing:0.04em; color:var(--muted); margin:12px 0 4px; }
.bom-table { width:100%; border-collapse:collapse; font-size:13px; }
.bom-table th { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em; padding:4px 8px 6px; border-bottom:1px solid var(--border); text-align:left; }
.bom-table td { padding:5px 8px; border-bottom:1px solid var(--border); }
.bom-table td .muted { font-size:11px; }
.bom-table tr.bom-total td { font-weight:800; border-top:2px solid var(--border); border-bottom:none; padding-top:7px; }
/* Ueberschlag & Plausibilitaet: die Ampel-Schwellen stehen je Zeile mit in der
   letzten Spalte - dort etwas Luft, damit Wert und Schwelle lesbar bleiben. */
.sanity-table td, .sanity-table th { vertical-align:top; }
.sanity-table td:last-child { white-space:nowrap; line-height:1.5; }
.sanity-table td .muted { font-size:10px; }
table { width:100%; border-collapse:collapse; font-size:12px; }
th, td { text-align:right; padding:6px 8px; border-bottom:1px solid var(--border); }
th:first-child, td:first-child { text-align:left; }
th { color:var(--muted); font-weight:600; text-transform:uppercase; font-size:10px; letter-spacing:0.04em; }
.notes { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:8px; }
.notes li { background:var(--card2); border-left:3px solid var(--accent); border-radius:6px; padding:9px 12px; font-size:13px; color:var(--text); line-height:1.4; }
.notes li.warn { border-left-color:#ef4444; }
.profile { display:grid; grid-template-columns:repeat(12,1fr); gap:5px; }
.profile .h { font-size:10px; color:var(--muted); text-align:center; }
.profile input { padding:6px 3px; text-align:center; font-size:11px; }
.barchart { display:flex; gap:3px; height:180px; align-items:stretch; }
.barchart .bar-col { flex:1 1 0; display:flex; flex-direction:column; align-items:center; min-width:0; }
.barchart .bar-val { font-size:9px; color:var(--muted); line-height:1; margin-bottom:3px; }
.barchart .bar-track { flex:1; width:72%; display:flex; align-items:flex-end; background:var(--card2); border:1px solid var(--border); border-radius:4px; overflow:hidden; }
.barchart .bar-fill { width:100%; background:linear-gradient(180deg,#00AEEF,#0089B6); border-radius:3px 3px 0 0; min-height:2px; transition:height .12s ease; }
.barchart .bar-h { font-size:9px; color:var(--muted); margin-top:4px; line-height:1; }
/* EIN DIAGRAMM WIRD NICHT GROESSER ALS SEINE ZEICHENFLAECHE (26.08.2026).
   Jedes Diagramm ist eine SVG mit fester viewBox (520 bis 900 Einheiten
   breit). "width:100%" allein streckt sie auf die volle Panel-Breite - im
   Ergebnis-Layout bis 1.750 Punkte -, und dabei wird ALLES mitgestreckt:
   eine Achsenbeschriftung mit font-size 9 stand am Ende 30 Punkte hoch im
   Bild, ein 205 Einheiten hohes Diagramm wurde 690 Punkte hoch. Die Schrift
   war nicht falsch gewaehlt, sie war mitskaliert.
   Die Obergrenze haelt den Massstab bei hoechstens 1: eine viewBox-Einheit
   ist ein Bildschirmpunkt, "font-size 11" sind 11 Punkte. Nach unten skaliert
   weiterhin alles mit (schmales Fenster, zwei Spalten) - dort ist das auch
   richtig, weil dann das ganze Bild kleiner wird. */
.chart { width:100%; max-width:900px; height:auto; display:block; background:var(--card2); border:1px solid var(--border); border-radius:9px; }
.chart text { fill:var(--muted); font-family:inherit; }
.chart-legend { display:flex; flex-wrap:wrap; gap:14px; margin-top:8px; font-size:12px; color:var(--muted); }
.chart-legend span { display:inline-flex; align-items:center; gap:6px; }
.chart-legend i { width:14px; height:3px; border-radius:2px; display:inline-block; }
.muted { color:var(--muted); font-size:12px; }
.src { font-size:11px; color:var(--muted); margin-top:8px; }
.src b.pvgis { color:var(--green); } .src b.fallback { color:var(--orange); }
.saved-row {
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  background:var(--card2); border:1px solid var(--border); border-radius:9px;
  padding:9px 12px; margin-bottom:8px;
}
.saved-row .saved-meta { display:flex; flex-direction:column; gap:2px; min-width:0; }
.saved-row .saved-meta b { font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.saved-row .saved-meta span { font-size:11px; }
.saved-row .saved-act { display:flex; gap:6px; flex-shrink:0; }
.kpis { grid-template-columns:repeat(3,1fr); }
@media (max-width:680px){ .kpis { grid-template-columns:1fr 1fr; } }

/* ================= Rechenweg (Trace) =================
   Aufklappbare Rechenkette + "Zeitlupe" (Stundenverlauf). Bewusst aus den
   vorhandenen Bausteinen gebaut (panel/card2/eb-table/chart), damit der Tab
   nicht wie eine Fremdkoerper-Oberflaeche wirkt. */
.trace-stack { display:flex; flex-direction:column; gap:16px; }
/* Eine Stufe der Kette: Kopfzeile immer sichtbar, Rumpf klappbar. */
.trace-stage { background:var(--card2); border:1px solid var(--border); border-radius:10px; margin-bottom:10px; overflow:hidden; }
.trace-stage:last-child { margin-bottom:0; }
.trace-stage.open { border-color:var(--accent); }
.trace-stage-head {
  display:flex; align-items:baseline; gap:10px; width:100%; text-align:left;
  background:none; border:none; color:var(--text); font-family:inherit;
  padding:11px 13px; cursor:pointer;
}
.trace-stage-head:hover { background:rgba(0,174,239,0.07); }
.trace-stage-head .tw-caret { color:var(--accent); font-size:10px; width:12px; flex-shrink:0; }
.trace-stage-head .tw-title { font-size:13px; font-weight:700; flex-shrink:0; }
.trace-stage-head .tw-summary { font-size:12px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.trace-stage-body { padding:0 13px 12px; }
.trace-table { width:100%; border-collapse:collapse; font-size:13px; }
.trace-table th {
  font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em;
  padding:4px 8px 6px; border-bottom:1px solid var(--border); text-align:left;
}
.trace-table td { padding:5px 8px; border-bottom:1px solid var(--border); vertical-align:top; }
.trace-table td.tw-val { text-align:right; font-weight:600; white-space:nowrap; }
.trace-table td.tw-detail { font-size:11px; color:var(--muted); line-height:1.5; }
/* Ergebnis der Stufe (emphasis) - der Wert, der in die naechste Stufe geht. */
.trace-table tr.tw-emph td { background:rgba(0,174,239,0.08); font-weight:700; border-bottom:1px solid var(--accent); }
.trace-table tr.tw-emph td.tw-val { color:var(--accent); }
.trace-table tr.tw-emph td.tw-detail { font-weight:400; }
/* Sprungziele zu markanten Stunden im Jahr. */
.trace-jumps { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px; }
.trace-jumps button { padding:5px 10px; font-size:11px; }
.trace-slider { width:100%; margin:0; cursor:pointer; accent-color:var(--accent); }
.trace-hour-head { font-size:13px; font-weight:700; margin-bottom:6px; }

/* ================= Strompreise (P0094 Phase 0) =================
   Bewusst aus den vorhandenen Bausteinen gebaut (panel/card2/trace-table/
   chart/chart-legend); nur was es noch nicht gab, steht hier: die Kachelreihe
   fuer Kennzahlen, der Inline-Balken in der Jahres-Tabelle und die grosse
   Ablesezahl des Spread-Explorers. */
#pricesContent { flex-direction:column; gap:16px; }
.pr-tiles { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:12px; }
.pr-tile { background:var(--card2); border:1px solid var(--border); border-radius:10px; padding:11px 13px; }
.pr-tile .v { font-size:20px; font-weight:800; letter-spacing:-0.01em; }
.pr-tile .l { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em; margin-top:2px; }
.pr-tile .s { font-size:11px; color:var(--muted); margin-top:4px; line-height:1.45; }
.pr-tile.accent { border-left:3px solid var(--accent); } .pr-tile.accent .v { color:var(--accent); }
.pr-tile.good   { border-left:3px solid var(--green); }  .pr-tile.good .v   { color:var(--green); }
.pr-tile.warn   { border-left:3px solid var(--orange); } .pr-tile.warn .v   { color:var(--orange); }
/* Inline-Balken (mittlerer Tagesspread je Jahr) - macht den Trend ohne
   zweites Diagramm sichtbar. */
.pr-bar { display:block; height:6px; border-radius:3px; background:#0b1322; border:1px solid var(--border); overflow:hidden; margin-top:5px; }
.pr-bar i { display:block; height:100%; background:linear-gradient(90deg,#00AEEF,#22c55e); }
/* Negative Preise sind normal (rund 5 % aller Stunden) - eigene Farbe statt
   Warn-Optik. */
.pr-neg { color:var(--red); font-weight:700; }
.pr-emph { color:var(--accent); font-weight:800; }
table.pr-table tr.pr-sel td { background:rgba(0,174,239,0.10); }
table.pr-table tr.pr-sel td:first-child { color:var(--accent); font-weight:800; }
.pr-readout { background:var(--card2); border:1px solid var(--accent); border-radius:10px; padding:12px 14px; }
.pr-readout .big { font-size:28px; font-weight:800; color:var(--accent); letter-spacing:-0.02em; }
.pr-readout .sub { font-size:12.5px; color:var(--muted); margin-top:4px; line-height:1.55; }
.pr-note { font-size:12px; color:var(--text); line-height:1.55; background:var(--card2);
  border-left:3px solid var(--accent); border-radius:6px; padding:9px 12px; }

/* Einfuegepunkt fuer eine Stufe der Rechenkette ZWISCHEN den Eingabe-Kacheln.
   Die Kacheln sind nach Objekt gruppiert, die Kette laeuft nach Rechenschritt -
   hier treffen sich beide: jede Stufe erscheint dort, wo ihre Eingaben
   vollstaendig sind. Leer bleibt der Punkt unsichtbar (kein Rahmen, kein Abstand). */
.stage-slot:empty { display:none; }
.stage-slot .trace-stack { margin:0; }
.stage-slot .trace-stage { border-left:3px solid var(--accent); }

/* Sammelbecken fuer Stufen ohne Kachel-Zuordnung. Leer unsichtbar - die Kette
   steht sonst doppelt (einmal bei den Kacheln, einmal am Stueck). */
.trace-rest:empty { display:none; }

/* ============ KOMPONENTEN-BIBLIOTHEK ============
   Eine Zeile je Baustein: Haken (geht in den Loesungsraum), Name mit Herkunft
   und Kennwerten, Bedienknoepfe. Der Haken steht LINKS und ist die wichtigste
   Information der Zeile - er entscheidet, was gerechnet wird. */

/* ---- Zwei Teile je Kachel ----
   Oben das Geraet DIESER Rechnung (Auswahl + die Felder, die sie vorbefuellt),
   darunter der Katalog fuer den Loesungsraum. Seit dem 04.08.2026 stehen die
   Geraetefelder hier statt auf der Registerkarte "Eingaben"; ohne optische
   Trennung waeren beide Teile eine einzige Wand aus Eingabefeldern.

   Der obere Teil ist EINKLAPPBAR (<details>), startet aber OFFEN. Das ist kein
   Widerspruch zur harten Regel "Eingabefelder nie ausblenden": ausgeblendet
   wird nichts, der Nutzer kann den Block bei Bedarf selbst zuklappen, wenn ihn
   gerade nur der Katalog interessiert. */
.lib-fall {
  background:var(--card2); border:1px solid var(--border); border-radius:10px;
  padding:12px 14px; margin:0 0 14px;
}
.lib-fall > summary {
  cursor:pointer; list-style:none; font-size:12px; font-weight:700;
  text-transform:uppercase; letter-spacing:0.05em; color:var(--accent);
}
.lib-fall > summary::-webkit-details-marker { display:none; }
.lib-fall > summary::before { content:"▾ "; color:var(--muted); }
.lib-fall:not([open]) > summary::before { content:"▸ "; }
.lib-fall > summary:hover { filter:brightness(1.15); }
.lib-fall-body { margin-top:12px; }

.lib-list { display:flex; flex-direction:column; }
.lib-row {
  display:flex; align-items:flex-start; gap:12px;
  padding:10px 0; border-top:1px solid var(--border);
}
.lib-row:first-child { border-top:0; }
/* Gewaehlte Zeilen abheben: auf einen Blick sichtbar, wie gross der
   Loesungsraum ist, ohne die Stueckzahlen einzeln zu lesen. Massgeblich ist
   BIS > 0 - "wird ueberhaupt verbaut". */
.lib-row.lib-on { background:rgba(0,174,239,0.06); }
/* .lib-pick traegt weiterhin das Haekchen - aber nur noch EINS, das der
   Betriebsfuehrung (Registerkarte "Betriebslogik", Stufe 3). In den fuenf
   Komponenten-Kacheln ist es am 20.08.2026 der Stueckzahl-Spanne gewichen. */
.lib-pick { margin:0; flex:0 0 auto; padding-top:2px; }

/* ---- STUECKZAHL VON-BIS (20.08.2026) --------------------------------------
   Sie sitzt dort, wo bis dahin das Haekchen sass: ganz links, vor dem Namen.
   Die Zeilen der Katalogliste sind eng (29 davon in einer Kachel), deshalb
   stehen beide Felder mit ihren Kurzwoertern in EINER Zeile und sind bewusst
   schmal gehalten - breit genug fuer zwei Ziffern, das ist der ganze Bedarf
   (Obergrenze 20, siehe LIB_MENGE_MAX in app.js).
   Feste Breite (nicht flex): sonst laufen die Namen der Eintraege je nach
   Stueckzahl unterschiedlich weit ein, und die Liste wird unruhig. */
.lib-menge {
  flex:0 0 auto; display:flex; align-items:center; gap:4px; padding-top:1px;
}
.lib-mk {
  font-size:10px; text-transform:uppercase; letter-spacing:0.04em;
  color:var(--muted); font-weight:700;
}
.lib-mz {
  width:40px; min-width:0; margin:0; padding:3px 4px; text-align:center;
  font-size:12px; font-family:inherit;
  background:var(--card2); color:var(--text);
  border:1px solid var(--border); border-radius:5px;
}
.lib-mz:focus { outline:none; border-color:var(--accent); }
/* DIE WINKEL-SPANNE HAT EIGENE KLASSEN (25.08.2026), obwohl sie genauso
   aussieht wie eine Stueckzahl-Spanne.
   Sie hat sich zuerst .lib-menge / .lib-mk / .lib-mz geliehen - und damit die
   BEDEUTUNG dieser Klassen zerstoert: Der Rauchtest zaehlt .lib-menge als
   "Baustein mit Stueckzahl-Spanne" und meldete prompt 51 Spannen zu 48
   Bausteinen. Eine Klasse, die zwei Dinge bedeutet, kann keine von beiden mehr
   bewachen. Aussehen wird geerbt, Bedeutung nicht.
   Gradfelder sind ausserdem breiter: "-180" braucht vier Zeichen, und ein
   abgeschnittenes Minus waere ein Vorzeichenfehler mit Ansage. */
.pv-spanne { flex:0 0 auto; display:flex; align-items:center; gap:4px; padding-top:1px; }
.pv-sk {
  font-size:10px; text-transform:uppercase; letter-spacing:0.04em;
  color:var(--muted); font-weight:700;
}
.pv-sz {
  width:56px; min-width:0; margin:0; padding:3px 4px; text-align:center;
  font-size:12px; font-family:inherit;
  background:var(--card2); color:var(--text);
  border:1px solid var(--border); border-radius:5px;
}
.pv-sz:focus { outline:none; border-color:var(--accent); }
/* Die Ausrichtung sitzt unter der Modulliste und ist optisch abgesetzt -
   sie beschreibt die MONTAGE aller Module, nicht eine einzelne Katalogzeile. */
.pv-winkel { margin-top:16px; border-top:1px solid var(--border); padding-top:14px; }
.pv-winkel-titel { font-size:13px; font-weight:700; color:var(--text); }
/* VON > BIS: keine stille Korrektur, sondern eine sichtbare Meldung. Die
   Felder bleiben BEDIENBAR und sichtbar (harte Regel) - gesperrt wird der
   Startknopf, und zwar mit dem Grund im Klartext daneben (syncDesignRun). */
.lib-menge-fehl .lib-mz { border-color:var(--red); color:var(--red); }
.lib-menge-fehl .lib-mk { color:var(--red); }
/* ---- HIER STANDEN BIS ZUM 20.08.2026 .lib-auto / .lib-autotxt /
   .lib-menge-auto -----------------------------------------------------------
   Die Stilregeln des "dritten Zustands" einer Katalogzeile: erst der Haken
   "Groesse ableiten" der Batterie (bis 20.08.2026), zuletzt der Haken
   "Handwert" des Aggregats. Beide Bedienelemente sind entfallen, und die
   Regeln sind MITGEGANGEN statt liegenzubleiben - eine Klasse ohne Element
   findet der naechste Leser und haelt sie fuer einen lebenden Zustand.

   GESPERRTE MENGENFELDER GIBT ES WEITERHIN, und die Regel darunter bleibt
   deshalb: sie treffen die eine Zeile, die einen geladenen Altstand ohne
   Geraet markiert. Ihre Stueckzahl wirkt dort nicht (die Nennleistung kommt
   aus dem gespeicherten Stand), und sie werden GEGRAUT UND SICHTBAR gehalten,
   NICHT ausgeblendet: ein verschwundenes Feld laesst den Bediener raten, ob es
   die Zahl noch gibt. */
.lib-mz:disabled {
  opacity:0.45; cursor:not-allowed; background:var(--card);
}
.lib-row.lib-fehl { background:rgba(239,68,68,0.10); }
.lib-name { flex:1 1 auto; min-width:0; font-size:13px; }
.lib-kw { font-size:11px; margin-top:2px; }
.lib-act { flex:0 0 auto; display:flex; gap:6px; flex-wrap:wrap; }
.lib-btn { padding:5px 10px; font-size:11.5px; width:auto; min-width:0; }
/* Archivieren ist der einzige Knopf mit Folgen - rot, wie ueberall im Stack. */
.lib-btn.lib-del { border-color:var(--red); color:var(--red); }
.lib-tag {
  display:inline-block; margin-left:6px; padding:1px 6px; border-radius:4px;
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:0.04em;
  background:var(--card2); color:var(--muted); border:1px solid var(--border);
}
.lib-tag.lib-own { color:var(--accent); border-color:var(--accent); }
/* DIE STANDZEILE EINER KATALOG-KACHEL ("Kein Baustein im Loesungsraum",
   "2 von 18 Bausteinen ... bis zu 5 Stueck"). Sie stand bis zum 19.08.2026
   UNTER der Liste, gedaempft und mit Trennlinie - also am Ende eines Blocks,
   den man wegscrollt, in derselben Grauschrift wie jede Nebenbemerkung.
   Ausgerechnet die Aussage "diese Kachel traegt gerade nichts bei" braucht man
   aber VOR der Liste und nicht danach.
   Jetzt steht sie direkt unter der Ueberschrift, in Vollton und fett. NICHT
   groesser: die Groesse ist nicht das Problem gewesen, der Kontrast war es.
   Die Trennlinie faellt weg - oben trennt sie nichts, sie wuerde die
   Ueberschrift von ihrer eigenen Kachel abschneiden. */
.lib-count { font-size:11.5px; font-weight:700; color:var(--text); margin:-4px 0 12px; }

/* ---- MARKEN AN EINER KATALOGZEILE (Backup-Aggregat) ------------------------
   Datenqualitaet und harte Nebenbedingungen. Sie sind der halbe Zweck dieses
   Katalogs: 13 der 29 Aggregat-Preise sind ANFRAGEPFLICHTIG, drei Eintraege
   sind Stacks ohne Peripherie, sieben haben gar keinen Tank. Eine Zahl sieht
   allein nicht anders aus, ob ein Datenblatt oder ein Platzhalter dahinter
   steht - die Marke ist das einzige, was den Unterschied sichtbar macht.

   BEWUSST KEINE MARKE FUER `belegt`: eine Marke an jeder Zeile ist keine Marke
   mehr, sondern Grundrauschen. Gezeigt wird, was VOM BELEGTEN ABWEICHT. */
.lib-flags { display:flex; flex-wrap:wrap; gap:4px; margin-top:4px; }
.lib-flag {
  display:inline-block; padding:1px 6px; border-radius:4px;
  font-size:10px; font-weight:700; letter-spacing:0.02em;
  border:1px solid var(--border); background:var(--card2); color:var(--muted);
  cursor:help;
}
/* Geschaetzt = Groessenordnung belastbar, Einzelwert nicht. Gedaempft. */
.lib-flag.lib-q-schaetzung { color:var(--muted); border-color:var(--border); }
/* Anfrage = die Zahl ist ein PLATZHALTER und kein Angebot. Deutlich sichtbar,
   aber nicht rot: es ist kein Fehler, sondern eine Eigenschaft des Marktes. */
.lib-flag.lib-q-anfrage { color:var(--accent); border-color:var(--accent); }
/* Die drei harten Nebenbedingungen (Patronen / nur Stack / ausser Zuschnitt)
   plus "kein Preis hinterlegt". Sie aendern, was man mit dem Eintrag ueberhaupt
   rechnen DARF - deshalb dieselbe Farbe wie jede andere Warnung im Werkzeug. */
.lib-flag.lib-flag-warn { color:var(--red); border-color:var(--red); }
/* SACHANGABEN OHNE WARNCHARAKTER (21.08.2026, mit der PV-Kachel). Sie sagen,
   WORAUF eine Zahl sich bezieht - die Bezugsmenge eines Modulpreises, die
   Bauweise der Verglasung. Ohne sie waere eine EUR/Wp-Zahl nicht
   interpretierbar (Einzelmodul gegen Palette ist Faktor 1,7, und das meiste
   davon ist Fracht). Sie sind aber KEIN Mangel und duerfen deshalb nicht rot
   sein - sonst waere die haeufigste Marke der Kachel eine Warnung, und die
   echten Warnungen gingen darin unter. */
.lib-flag.lib-flag-info { color:var(--muted); border-color:var(--border); }

/* Die Qualitaetsmarke steht auch im Fliesstext der Kachel (am gewaehlten
   Geraet). Dort ohne Rahmen - im Satz waere ein Kaestchen ein Fremdkoerper. */
.lib-q { font-weight:700; }
.lib-q.lib-q-schaetzung { color:var(--muted); }
.lib-q.lib-q-anfrage { color:var(--accent); }
/* Der Geraete-Absatz der Aggregat-Kachel setzt seine Saetze auf eigene Zeilen:
   sie sind je fuer sich eine Aussage (Datenqualitaet, Stack, Patronen), und
   hintereinandergesetzt liest man die zweite nicht mehr. */
.backup-zeile { display:block; margin-top:4px; }
.backup-zeile:first-child { margin-top:0; }

/* ---- Ordnung in langen Listen (29 Ladegeraete, 20 Batterien, ...) ----
   Gruppiert wird nach HERSTELLER (Katalogfeld, nicht aus dem Namen geraten).
   Die Werksklassen bilden dabei von selbst eine eigene Gruppe - genau die
   Trennung, die vorher fehlte. Die Gruppenzeile ist ein AUSKLAPPER, kein
   Handlungsknopf: deshalb bewusst NICHT in der Knopfform des Stacks
   (240 px, Rahmen), sondern als Ueberschrift mit Pfeil. */
.lib-group + .lib-group { margin-top:4px; }
.lib-ghead {
  display:flex; align-items:baseline; gap:8px; width:100%; text-align:left;
  padding:8px 0; background:transparent; border:0; border-top:1px solid var(--border);
  color:var(--text); font-family:inherit; font-size:12.5px; font-weight:700; cursor:pointer;
}
.lib-group:first-child .lib-ghead { border-top:0; }
.lib-ghead:hover .lib-gname { color:var(--accent); }
.lib-gcaret { flex:0 0 auto; width:12px; color:var(--muted); }
.lib-gname { flex:0 0 auto; }
.lib-gn { color:var(--muted); font-weight:600; font-size:11.5px; }
.lib-gsel {
  margin-left:auto; color:var(--accent); font-weight:600; font-size:11px;
}
/* Der Inhalt einer Gruppe ruecht ein - die Ueberschrift bleibt so als
   Gliederungsebene erkennbar, auch wenn mehrere Gruppen offen stehen. */
.lib-group .lib-list { padding-left:20px; }
/* HIER STANDEN DREI TOTE REGELBLOECKE, entfernt am 19.08.2026:
   .lib-search-wrap / .lib-search-n (Suchfeld ueber den Katalog-Listen) und
   weiter oben .lib-liste-x (Vorspann ueber der Liste). Beide Bauteile sind mit
   dem Aufraeumen der Komponenten-Kacheln aus index.html und app.js
   verschwunden - die Stilregeln blieben als Leichen stehen. Der Rauchtest prueft
   jetzt am Stilblatt, dass sie nicht zurueckkommen. */
/* Der Editor sitzt IN der Liste an der Stelle des Eintrags - so sieht man beim
   Aendern die Nachbarn und damit, ob der neue Wert in die Reihe passt. */
.lib-editor {
  background:var(--card2); border:1px solid var(--accent); border-radius:10px;
  padding:14px 16px; margin:4px 0 10px;
}
.lib-editor .lib-in-name { font-weight:600; }

/* ============ REGISTERKARTE "AUSLEGUNG" ============
   Der Kopfstreifen ist der gemeinsame Kopf BEIDER Suchlaeufe (autark:
   Korridore, netzgekoppelt: Loesungsraum). Er bleibt kompakt - alles darunter
   ist Ergebnis. */
.design-head .design-mode {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  background:var(--card2); border:1px solid var(--border); border-radius:10px;
  padding:10px 12px;
}
.design-mode-l { font-size:11px; text-transform:uppercase; letter-spacing:0.03em; color:var(--muted); font-weight:700; }
.design-mode-v { font-size:14px; font-weight:800; color:var(--accent); }
/* Startzeile: EIN Knopf, daneben Abbruch und Statuszeile. */
.design-run { display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-top:14px; }
.design-run button.primary { width:auto; min-width:240px; flex:0 0 auto; margin-top:0; }
/* Ebenen-Umschalter. Er schaltet ERGEBNISBLOECKE um, keine Eingabefelder -
   die harte Regel "nie ausblenden" gilt fuer Eingaben und ist nicht beruehrt. */
.level-switch { display:flex; gap:6px; }
.level-switch .lvl {
  flex:1 1 0; padding:11px 20px; border:1px solid var(--border); border-radius:7px;
  background:var(--card); color:var(--muted); font-size:13px; font-weight:600;
  font-family:inherit; cursor:pointer;
}
.level-switch .lvl:hover { color:var(--text); }
.level-switch .lvl.active { border-color:var(--accent); color:var(--accent); background:var(--card2); }
/* Herkunftszeile ueber dem Einzelfall: woher dieser Fall kommt und zurueck. */
.design-origin {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  background:var(--card2); border:1px solid var(--accent); border-radius:10px;
  padding:10px 12px; font-size:12.5px;
}
.design-origin button { margin-left:auto; }
/* Anklickbare Rangliste und Pareto-Punkte - der Weg in die Tiefe. */
table.space-table tr.space-row { cursor:pointer; }
table.space-table tr.space-row:hover td { background:rgba(0,174,239,0.16); }
/* .space-dot ist mit dem Naechster-Punkt-Verfahren entfallen: der Zeiger muss
   keinen Kreis mehr treffen, sondern nur noch dem gemeinten Punkt am naechsten
   sein. Der Mauszeiger haengt deshalb am ganzen Diagramm (#spacePareto svg). */

/* ============ DIAGRAMM-TOOLTIP (Pareto-Front, Tornado) ============
   Bewusst KEIN <title> im SVG: das erscheint erst nach rund einer Sekunde,
   laesst sich nicht gestalten, bricht keine Zeilen und ist mit der Tastatur
   gar nicht erreichbar. Bei tausenden Punkten waeren es ausserdem tausende
   zusaetzliche Knoten. Statt dessen EIN absolut gesetztes div ueber dem
   Diagramm, gefuettert von einem Naechster-Punkt-Verfahren.
   pointer-events:none ist zwingend - sonst faengt der Tooltip den Zeiger ab,
   den er gerade erklaert, und flackert. */
.viz-host { position:relative; }
.viz-tip {
  position:absolute; z-index:5; pointer-events:none;
  min-width:180px; max-width:320px;
  background:var(--card); border:1px solid var(--accent); border-radius:9px;
  padding:9px 11px; font-size:12px; line-height:1.55; color:var(--text);
  box-shadow:0 8px 22px rgba(0,0,0,0.45);
}
.viz-tip[hidden] { display:none; }
/* Der WERT fuehrt, die Bezeichnung folgt - im Tooltip hat der Leser die
   Kategorie schon (er zeigt darauf) und will die Zahl. In der Legende ist es
   umgekehrt, deshalb dort die andere Gewichtung. */
.viz-tip .tip-v { font-size:15px; font-weight:800; letter-spacing:-0.01em; }
.viz-tip .tip-l { color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:0.04em; }
.viz-tip .tip-sep { border-top:1px solid var(--border); margin:6px 0; }
.viz-tip .tip-row { display:flex; justify-content:space-between; gap:12px; }
.viz-tip .tip-row b { font-variant-numeric:tabular-nums; }
.viz-tip .tip-hint { color:var(--muted); font-size:11px; margin-top:5px; }
.viz-tip .tip-warn { color:var(--orange); font-weight:700; }
/* Das Diagramm selbst faengt den Zeiger - der Klick fuehrt in die Tiefe. */
#spacePareto svg { cursor:crosshair; }
/* Hinweiszeile unter dem Diagramm, wenn die Wolke verdichtet wurde. Verdichtung
   wird BENANNT, nie stillschweigend gemacht. */
.viz-hint { font-size:11.5px; color:var(--muted); margin-top:6px; }
/* Legenden-Marken mit FORM, nicht nur Farbe: gefuellter Punkt, offener Kreis,
   blasser Punkt, Linie, gestrichelte Linie. Ohne Form waeren die drei
   Punktzustaende nur ueber die Farbe zu unterscheiden. */
.chart-legend svg { flex:0 0 auto; display:block; }

/* ============ TORNADO (Sensitivitaet des Loesungsraums) ============ */
.sens-targets { display:flex; gap:6px; flex-wrap:wrap; margin:0 0 12px; }
.sens-targets .sens-t {
  padding:7px 14px; border:1px solid var(--border); border-radius:7px;
  background:var(--card2); color:var(--muted);
  font-size:12px; font-weight:600; font-family:inherit; cursor:pointer;
}
.sens-targets .sens-t:hover { color:var(--text); }
.sens-targets .sens-t.active { border-color:var(--accent); color:var(--accent); }
#spaceSens svg { cursor:default; }
#spaceSens .tornado-row { cursor:default; }
/* NICHT VARIIERTE ACHSEN bekommen KEINEN Balken, sondern diesen Block. Ein
   Balken der Laenge null zwischen echten Balken liest sich als "gemessen,
   bedeutungslos" - das ist genau die Fehlinterpretation, um die es hier geht.
   Nicht gemessen und ohne Wirkung sind zwei verschiedene Aussagen. */
.sens-fix {
  margin-top:12px; padding:9px 12px; border-radius:8px; font-size:11.5px; line-height:1.6;
  background:rgba(148,163,184,0.10); border-left:2px solid var(--muted); color:var(--muted);
}
.sens-fix b { color:var(--text); }
/* Zurueckgehaltene Zahlen: der Server liefert bewusst nur die Meldung, wenn das
   Gitter loechrig ist und die Achsen konfundiert sind. Dann steht hier auch nur
   sie - geschoente Zahlen waeren schlimmer als keine. */
.sens-block {
  padding:10px 12px; border-radius:8px; font-size:12px; line-height:1.6;
  background:rgba(245,158,11,0.10); border:1px solid var(--orange); color:var(--text);
}
table.sens-table { font-size:12px; }
table.sens-table th { font-size:10.5px; }
table.sens-table td { font-variant-numeric:tabular-nums; }
table.sens-table td:first-child { font-variant-numeric:normal; }
table.sens-table tr.sens-off td { opacity:0.55; }

/* ============ LOESUNGSRAUM ============
   Die Achsen-Uebersicht ganz oben: eine Kachel je Kategorie mit der Zahl der
   angehakten Bausteine. Sie beantwortet auf einen Blick "wie gross ist das,
   was ich gleich rechne" - ohne dass man die Haken zaehlen muss. */
.space-axes { display:flex; gap:10px; flex-wrap:wrap; }
.space-axis {
  flex:1 1 120px; background:var(--card2); border:1px solid var(--border);
  border-radius:10px; padding:10px 12px; text-align:center;
}
/* Leere Kategorien warnen: dort greift der gesamte Werkskatalog, was den Raum
   unbeabsichtigt aufblaeht. */
/* NICHTS AUSGEWAEHLT (0 Varianten) - eine Luecke, orange. */
.space-axis-leer { border-color:var(--orange); }
/* GENAU EINE VARIANTE - gueltig, spannt den Raum nur nicht auf. Gedaempft und
   ausdruecklich NICHT orange: seit dem 21.08.2026 ist ein Raum aus lauter
   Ein-Varianten-Achsen ein gueltiger Raum mit einer Ergebniszeile. Bis dahin
   trugen beide Zustaende dieselbe Klasse - die eine Anzeige meinte damit
   "nichts gewaehlt", die andere "nichts zu suchen". */
.space-axis-eins { opacity:0.72; }
/* KEINE ACHSE in dieser Betriebsart - kein Mangel, sondern eine Eigenschaft des
   Rechenwegs. Deshalb gedaempfter als eine Ein-Varianten-Achse und OHNE
   Warnfarbe: orange ist die Null nur dort, wo sie wirklich eine Luecke ist. */
.space-axis-keine { opacity:0.45; border-style:dashed; }
.space-axis-keine .space-axis-n { color:var(--muted); font-size:18px; }
.space-axes-fuss { font-size:11.5px; margin-top:8px; }
.space-axis-n { font-size:22px; font-weight:800; color:var(--accent); line-height:1.1; }
.space-axis-leer .space-axis-n { color:var(--orange); }
.space-axis-eins .space-axis-n { color:var(--muted); }
.space-axis-l { font-size:11px; color:var(--muted); margin-top:3px; }
/* Rangliste: erste Spalten links (Bezeichnungen), Zahlen rechts. */
table.space-table { font-size:12px; }
table.space-table th { font-size:10.5px; color:var(--muted); text-transform:uppercase; letter-spacing:0.03em; }
/* Pareto-optimale Zeilen hervorheben - sie sind die eigentliche Auswahl. */
table.space-table tr.space-pareto td { background:rgba(0,174,239,0.10); font-weight:600; }
.space-warn { color:var(--orange); font-weight:800; }
/* Die Warnzeile UNTER ihrer Ranglistenzeile (21.08.2026). Sie war bis dahin
   nur im Tooltip erreichbar - also ohne Maus gar nicht und im Ausdruck nie.
   Kein `cursor:pointer` und kein Hover: sie ist keine anklickbare Zeile. */
table.space-table tr.space-warnrow td {
  padding-top:0; border-top:0; font-size:11px; line-height:1.45; color:var(--muted);
  text-align:left; white-space:normal;
}
/* ============ EINFLUSSBILD (26.08.2026) ====================================
   Viele kleine Kacheln nebeneinander, jede gleich gross. DIE FESTE BREITE IST
   ABSICHT: in jede Kachel wird ein SVG mit viewBox == Anzeigebreite gezeichnet,
   damit die Schrift wirklich 9 px hat und nicht mitskaliert (dieselbe Lehre wie
   beim Raum-Diagramm). Eine Kachel, die sich mit dem Fenster dehnt, muesste vor
   jedem Zeichnen gemessen werden - und waere bei zwanzig Kacheln zwanzig
   Messungen im Layout-Pfad. Vorhersagbar ist hier mehr wert als bildschirm-
   fuellend. */
.eb-gitter { display:flex; flex-wrap:wrap; gap:10px; }
/* DER BEHAELTER STAPELT, ER SETZT NICHT NEBENEINANDER.
   Er trug bis zum 26.08.2026 selbst .eb-gitter (flex + wrap) - damit wurden die
   REIHEN zu Flex-Elementen und rutschten nebeneinander, sobald Platz war. Aus
   dem Raster wurde ein Haufen: die Spalten standen in jeder Reihe woanders, und
   genau davon lebt die Darstellung. Ein Gitter im Gitter ist die Falle;
   deshalb heisst der Behaelter jetzt anders als seine Kinder. */
.eb-matrix { display:flex; flex-direction:column; gap:16px; }
/* EINE REIHE JE ERGEBNISGROESSE: senkrechte Beschriftung links, Kacheln rechts.
   Der Name gehoert NEBEN die Reihe und nicht ueber ihre erste Kachel - er
   beschriftet alle. Bei sieben Reihen spart das sieben Textzeilen, und das Auge
   findet die Reihe am Rand statt sie im Bild zu suchen. */
.eb-reihe { display:flex; align-items:stretch; gap:10px; }
.eb-label {
  /* vertical-rl + Drehung = von unten nach oben lesbar. Nur vertical-rl allein
     stuende der Text kopfueber auf der Seite. */
  writing-mode:vertical-rl; transform:rotate(180deg);
  display:flex; align-items:center; justify-content:center;
  flex:0 0 auto; padding:6px 3px;
  font-size:12.5px; font-weight:700; letter-spacing:0.01em;
  background:var(--card2); border:1px solid var(--border); border-radius:8px;
  white-space:nowrap; overflow:hidden;
}
.eb-rechts { flex:1 1 auto; min-width:0; }
.eb-wechsel { font-size:11px; color:var(--muted); margin:0 0 4px; }
.eb-karte {
  width:250px; padding:6px 6px 5px; cursor:pointer;
  background:var(--card2); border:1px solid var(--border); border-radius:8px;
}
.eb-karte:hover { border-color:var(--accent); }
/* Die ganze Kachel ist EIN Klickziel: die Punktauswahl. Bis zum 26.08.2026 war
   die Ueberschrift ein zweites (sie oeffnete die Achse gross im Raum-Diagramm);
   das Diagramm ist entfallen, und mit ihm der Zeiger auf der Ueberschrift - ein
   Element, das aussieht wie ein Knopf und keiner mehr ist, ist schlimmer als
   eines, das nie einer war. */
.eb-karte svg { cursor:crosshair; }
.eb-karte svg { display:block; }
.eb-kopf { display:flex; justify-content:space-between; align-items:baseline; gap:6px; font-size:11px; }
.eb-name { color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Der Varianzanteil. Erst ab zehn Prozent farbig - darunter ist es Rauschen,
   und eine hervorgehobene Null waere eine Betonung ohne Gegenstand. */
.eb-anteil { font-weight:800; color:var(--muted); white-space:nowrap; }
.eb-anteil.eb-stark { color:var(--accent); }
.eb-fuss { font-size:10.5px; color:var(--muted); margin-top:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Die Werte der AUSGEWAEHLTEN Variante. Sie tragen die Farbe des Fadenkreuzes
   (helles Weiss), damit man sie ohne Nachdenken derselben Sache zuordnet -
   Marke im Bild und Zahl am Rand gehoeren zusammen. */
.eb-gewaehlt {
  font-weight:700; color:var(--text); white-space:nowrap;
  padding:0 4px; border:1px solid var(--border); border-radius:4px;
  background:rgba(231,236,243,0.08);
}
.eb-gewaehlt-y { color:var(--text); font-weight:700; }

/* Der Aufklapper der Rangliste. Er sieht aus wie eine Ueberschrift, weil er an
   der Stelle einer Ueberschrift steht - nur mit Zeiger und Dreieck, damit man
   sieht, dass etwas dahintersteckt. */
.rank-summary {
  cursor:pointer; list-style:none; user-select:none;
  font-size:15px; font-weight:700; letter-spacing:-0.01em;
  display:flex; align-items:baseline; gap:8px;
}
.rank-summary::-webkit-details-marker { display:none; }
.rank-summary::before { content:'B8'; color:var(--accent); font-size:12px; }
details[open] > .rank-summary::before { content:'BE'; }
.rank-summary:hover { color:var(--accent); }

/* ============ RANGLISTE: SORTIEREN UND FILTERN AM KOPF (26.08.2026) ========
   Das Auswahlfeld "Sortierung der Ergebnisse" darueber ist entfallen; die
   Tabelle bedient sich seither wie eine Tabellenkalkulation.

   DER KOPF MUSS AUSSEHEN WIE EIN KNOPF. Ein Element, das auf Klick etwas tut
   und wie reiner Text aussieht, findet niemand - deshalb Zeiger, Hover und ein
   Pfeil an der aktiven Spalte. `user-select:none`, weil ein Doppelklick auf
   einen Sortierkopf sonst die Beschriftung markiert statt zweimal zu sortieren.
   `white-space:nowrap` haelt Kopf und Pfeil zusammen: umbricht der Pfeil in
   eine zweite Zeile, wandert er unter das falsche Wort. */
table.rank-table th.rank-kopf {
  cursor:pointer; user-select:none; white-space:nowrap;
  text-align:right;
}
table.rank-table th.rank-kopf.rank-links { text-align:left; }
table.rank-table th.rank-kopf:hover { color:var(--text); }
table.rank-table th.rank-kopf.rank-aktiv { color:var(--accent); }
.rank-pfeil { font-size:9px; letter-spacing:0; }
/* DIE FILTERZEILE. Sie steht IM Kopf (zweite thead-Zeile) und nicht ueber der
   Tabelle: ein Filter gehoert unter die Spalte, auf die er wirkt - sonst muss
   man abzaehlen, welches Feld zu welcher Spalte gehoert.
   Kein `text-transform` und kein `letter-spacing` wie beim Kopf darueber: das
   sind Eingabefelder, keine Ueberschriften. */
table.rank-table tr.rank-filterzeile th {
  padding-top:2px; padding-bottom:6px; text-transform:none; letter-spacing:0;
  vertical-align:top; white-space:nowrap;
}
table.rank-table .rank-f {
  width:100%; min-width:64px; max-width:150px;
  padding:2px 4px; font-size:10.5px; line-height:1.3;
  background:var(--card2); color:var(--text);
  border:1px solid var(--border); border-radius:5px;
}
/* Zwei Felder (von/bis) in einer Spalte - jedes die halbe Breite, damit die
   Spalte nicht durch die Filterzeile breiter wird als durch ihre Zahlen. */
table.rank-table .rank-fz-zahl { width:48%; min-width:40px; display:inline-block; }
table.rank-table .rank-fz-zahl + .rank-fz-zahl { margin-left:4%; }
table.rank-table .rank-f:focus { outline:none; border-color:var(--accent); }
/* Alle Filter zurueck. Steht in der "#"-Spalte, also ganz links am Anfang der
   Filterzeile, und erscheint NUR, wenn ueberhaupt etwas gefiltert ist - ein
   Knopf ohne Wirkung ist eine Einladung zum Draufdruecken. */
.rank-reset {
  cursor:pointer; padding:1px 6px; font-size:11px; line-height:1.4;
  background:var(--card2); color:var(--orange);
  border:1px solid var(--orange); border-radius:5px;
}
.rank-reset:hover { background:rgba(245,158,11,0.14); }

/* NICHT MACHBARE VARIANTEN (21.08.2026). Sie stehen SICHTBAR in der autarken
   Rangliste - "was gut ist und was schlecht ist" schliesst die schlechten ein.
   Gedaempft, nicht ausgeblendet und nicht ausgegraut bis zur Unlesbarkeit: man
   soll sie lesen koennen, ohne sie fuer ein Angebot zu halten. Anklickbar
   bleiben sie ausdruecklich - gerade bei ihnen will man wissen, woran es
   liegt, und die Einzelrechnung sagt es genauer als ein Halbsatz. */
table.space-table tr.opt-unmachbar td {
  color:var(--muted); background:rgba(245,158,11,0.06);
}

/* ============ BETRIEBSLOGIK ============
   .ops-principle ist am 19.08.2026 mitentfallen: der abgesetzte Kasten "Das
   tragende Prinzip" war der Kern der Einleitungskachel ueber dieser Seite, und
   die Kachel ist ersatzlos weg (Begruendung: README.md Kap. 8.10 sowie die
   Kommentarbloecke in index.html und app.js). Ohne ihren einzigen Nutzer waere
   die Regel eine Leiche. */
/* BETRIEBSFENSTER: vorgelagerte Ebene, KEINE Stufe der Kette. Der Unterschied
   muss man sehen koennen - deshalb eigener Rahmen, eigene Farbe (orange statt
   Akzentblau) und ein Abzeichen "vorgelagert" statt einer Stufennummer. */
.ops-window { border:1px solid var(--orange); }
.ops-window-head { display:flex; gap:14px; align-items:center; margin-bottom:10px; }
.ops-window-badge {
  flex:0 0 auto; padding:5px 10px; border-radius:999px; font-size:10.5px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase;
  background:rgba(245,158,11,0.12); border:1px solid var(--orange); color:var(--orange);
}
.ops-window-title h2 { margin:0; }
.ops-window-x { font-size:12.5px; line-height:1.6; margin-bottom:10px; }
/* Wirkort-Gruppe innerhalb des Fensters: Klimatisierung / Ladepfad / Entladepfad. */
.ops-wgroup { margin-top:18px; padding-top:14px; border-top:2px solid var(--border); }
.ops-wgroup-t { font-size:14px; font-weight:800; margin-bottom:4px; }
.ops-wgroup-x { font-size:12px; line-height:1.6; margin-bottom:12px; }
/* "Was diese Ebene/Stufe NICHT tut" - das Dokumentationsmuster aus
   battery_charge_guard.h. Fuer eine Spezifikation der wertvollste Teil. */
.ops-nottut {
  margin:10px 0; padding:8px 11px; border-radius:7px; font-size:11.5px; line-height:1.55;
  background:rgba(148,163,184,0.10); border-left:2px solid var(--muted); color:var(--muted);
}
.ops-nottut b { color:var(--text, inherit); }
/* Sollwert hinter dem Regelnamen. */
.ops-rule-soll { font-weight:400; font-size:11px; opacity:.6; margin-left:6px; }
/* Vorrangkette: die Nummer traegt die Aussage, deshalb gross und farbig. */
.ops-step { display:flex; gap:14px; padding:12px 0; border-top:1px solid var(--border); }
.ops-step:first-child { border-top:0; padding-top:0; }
.ops-step-n {
  flex:0 0 34px; height:34px; border-radius:8px; background:var(--card2);
  border:1px solid var(--accent); color:var(--accent);
  font-size:16px; font-weight:800; display:flex; align-items:center; justify-content:center;
}
.ops-step-body { flex:1 1 auto; min-width:0; }
.ops-step-t { font-size:13px; font-weight:700; margin-bottom:3px; }
.ops-step-x { font-size:12px; line-height:1.6; }
/* Eine Regel: Titel, Beschreibung, das echte Eingabefeld, und - falls die
   Hardware sie nicht erfuellen kann - was dann passiert. */
.ops-rule { padding:14px 0; border-top:1px solid var(--border); }
.ops-rule:first-of-type { border-top:0; padding-top:0; }
.ops-rule-t { font-size:13px; font-weight:700; margin-bottom:4px; }
.ops-rule-x { font-size:12px; line-height:1.6; margin-bottom:10px; }
/* Ein Regel-Kasten traegt EIN Feld - oder mehrere, wenn sie zur selben Regel
   gehoeren (Regelverfahren + dessen Realisierungsgrad). Dazu, wo es einen gibt,
   den "Daraus folgt"-Kasten des Feldes. */
.ops-rule-field { max-width:340px; }
.ops-rule-field > * + * { margin-top:10px; }
.ops-rule-field.ops-rule-field-multi {
  max-width:700px; display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:12px; align-items:start;
}
.ops-rule-field.ops-rule-field-multi > * + * { margin-top:0; }
/* Die "Daraus folgt"-Zeile laeuft ueber beide Spalten - sie gehoert zu beiden.
   Ebenso die rote Notiz bei vertauschten SoC-Baendern: sie bezieht sich auf das
   Verhaeltnis der beiden Schwellen, nicht auf eine davon. */
.ops-rule-field.ops-rule-field-multi > .derived,
.ops-rule-field.ops-rule-field-multi > #socWarn { grid-column:1 / -1; }
.ops-rule-nohw {
  margin-top:10px; padding:8px 11px; border-radius:7px; font-size:11.5px; line-height:1.55;
  background:rgba(245,158,11,0.08); border-left:2px solid var(--orange); color:var(--muted);
}
.ops-rule-nohw b { color:var(--orange); }
/* NICHT GELTENDE BETRIEBSART: gedaempft und gekennzeichnet - NICHT ausgeblendet
   und NICHT entfernt. Die Forderungen der jeweils anderen Auslegung sollen
   lesbar bleiben, sonst waere diese Seite in jeder Betriebsart eine andere.
   Bewusst nur Deckkraft: kein display, kein visibility - die harte Regel
   "Felder werden nie ausgeblendet" gilt hier unveraendert weiter. */
.ops-fremd { opacity:.5; }
.ops-gilt {
  display:inline-block; margin-left:8px; padding:1px 8px; border-radius:999px;
  border:1px solid var(--border); font-size:10.5px; font-weight:700;
  letter-spacing:.02em; white-space:nowrap; vertical-align:middle;
}
/* GESPERRTE REGEL (adminOnly, Rolle "user"). Der Wortlaut kommt gar nicht erst
   vom Server (redactOpsRules) - hier steht nur, dass es ihn gibt. Der
   FELD-KASTEN wird trotzdem gezeichnet (moveOpsFields braucht sein Ziel) und
   nur verborgen: Ausblenden aus ZUGRIFFSgruenden ist die bestehende Ausnahme,
   genau wie beim Lastprofil-Editor und beim Rechenweg. */
.ops-rule-lock {
  padding:8px 11px; border-radius:7px; font-size:11.5px; line-height:1.55;
  background:var(--card2); border-left:2px solid var(--border); color:var(--muted);
  margin-bottom:10px;
}
.ops-rule-field.ops-rule-field-locked { display:none; }
/* Die Feld-Heimat: statischer Ausgangsort der Eingabefelder dieser Registerkarte.
   Nach dem Umzug zu den Regeln normalerweise leer - app.js blendet den KASTEN
   dann aus (keine Felder darin, also kein Verstoss gegen die Ausblende-Regel).
   Der frueher hier stehende Hinweis .ops-moved ist mit dem Kapitel
   "Regelungstechnik" entfallen: es gibt kein altes Kapitel mehr, aus dem etwas
   umziehen koennte. */
#opsFelder > div:not(.muted) + div { margin-top:12px; }
/* Soll gegen Ist. Die Farbe traegt die Aussage - gruen erfuellt, orange
   teilweise, rot nicht. */
.ops-fulfil { display:flex; gap:12px; align-items:flex-start; padding:11px 0; border-top:1px solid var(--border); }
.ops-fulfil:first-child { border-top:0; padding-top:0; }
.ops-fulfil-s { flex:0 0 24px; font-size:16px; font-weight:800; text-align:center; }
.ops-fulfil.ops-ok .ops-fulfil-s { color:var(--green); }
.ops-fulfil.ops-part .ops-fulfil-s { color:var(--orange); }
.ops-fulfil.ops-no .ops-fulfil-s { color:var(--red); }
.ops-fulfil-b { flex:1 1 auto; min-width:0; }
.ops-fulfil-t { font-size:12.5px; font-weight:600; }
.ops-fulfil-w { font-size:11px; margin-top:2px; }
.ops-fulfil-f { font-size:12px; line-height:1.55; margin-top:5px; }
.ops-fulfil-k { flex:0 0 auto; font-size:12.5px; font-weight:700; color:var(--orange); white-space:nowrap; }

/* Zaehlung ueber der Soll-Ist-Liste. */
.ops-fulfil-sum{padding:10px 12px;margin:0 0 10px;border-radius:7px;
  background:var(--panel2,#1b1f27);font-size:13px}
/* Vorrangstufe hinter dem Regelnamen - blass, damit sie den Titel nicht frisst. */
.ops-fulfil-v{font-weight:400;font-size:11px;opacity:.55;margin-left:8px}
/* Zwischenueberschrift beim Ebenenwechsel Fenster -> Kette. Ohne sie liest sich
   die Liste wieder als EINE Leiter - und genau das ist sie nicht. */
.ops-fulfil-head{margin:16px 0 6px;padding-top:12px;border-top:2px solid var(--border);
  font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.ops-fulfil-head:first-child{margin-top:0;padding-top:0;border-top:0}
/* Die Regeln OHNE Abgleich werden genannt statt verschwiegen - eine Liste, in
   der Forderungen fehlen, ohne dass jemand sagt warum, liest sich wie ein
   Fehler. */
.ops-fulfil-fuss{margin-top:14px;padding-top:12px;border-top:2px solid var(--border);
  font-size:11.5px;line-height:1.55}


/* ======================================================================
   BETRIEBSLOGIK, GEGLIEDERT NACH BAUTEILEN (25.08.2026)
   ======================================================================
   Die Registerkarte war vorher zwei Bloecke (Betriebsfenster + Vorrangkette)
   mit gedaempften Regeln fuer die jeweils andere Betriebsart. Sie ist jetzt
   EIN Panel mit acht Bauteil-Abschnitten; die Betriebsart steht als Marke
   daneben statt als Graustufe. Nichts wird mehr ausgegraut - man soll lesen
   koennen, was fuer die andere Betriebsart gilt. */
.bl-panel > h2 { margin-bottom: 6px; }
.bl-kopf { margin: 0 0 14px; }
.bl-kette {
  margin: 0 0 18px; padding: 10px 12px; border-radius: 7px;
  background: var(--card2); border: 1px solid var(--border); font-size: 13px;
}
.bl-bauteil { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--border); }
.bl-bauteil-kopf { display: flex; align-items: center; gap: 8px; }
.bl-bauteil-name { font-size: 15px; font-weight: 700; color: var(--text); }
.bl-bauteil-satz { margin: 4px 0 12px; }
.bl-regel { margin: 0 0 16px; padding-left: 12px; border-left: 2px solid var(--border); }
.bl-regel-t { font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.bl-vorrang { font-size: 11px; color: var(--muted); margin-top: 6px; letter-spacing: 0.02em; }
.bl-leer { font-size: 13px; color: var(--muted); padding-left: 12px; border-left: 2px dashed var(--border); }

/* DIE MARKE ERSETZT DAS AUSGRAUEN. Zwei Zeichen sagen dasselbe wie ein
   gedaempfter Block plus ein Satz - und der Text bleibt lesbar. */
.bl-marke {
  display: inline-block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; padding: 1px 6px; border-radius: 4px; vertical-align: middle;
}
.bl-marke-netz { background: rgba(0,174,239,0.15); color: #00AEEF; }
.bl-marke-autark { background: rgba(34,197,94,0.15); color: #22c55e; }
.bl-marke-beide { background: var(--card2); color: var(--muted); }

/* SPEZIFIKATION SICHTBAR MACHEN. 17 der Parameter legen fest, was die
   Steuerung tun SOLL, gehen aber noch nicht in die Rechnung ein. Ohne diese
   Zeile saehe ein Block mit sechs Feldern aus, als wirkten alle sechs - genau
   die Bauform, die in diesem Werkzeug mehrfach zugeschlagen hat. */
.bl-spez {
  font-size: 11px; color: #f59e0b; margin-top: 6px;
  padding: 5px 8px; border-radius: 5px; background: rgba(245,158,11,0.08);
}

/* Der volle Regeltext ist Firmware-Spezifikation und keine Erklaerprosa (in
   einem Absatz steckt Ticket 39585fd0 namentlich). Er bleibt vollstaendig -
   zugeklappt. */
.bl-mehr { margin-top: 8px; }
.bl-mehr > summary {
  cursor: pointer; font-size: 11px; color: var(--accent); font-weight: 600;
  list-style: none;
}
.bl-mehr > summary::-webkit-details-marker { display: none; }
.bl-mehr > summary::before { content: "\25B8 "; }
.bl-mehr[open] > summary::before { content: "\25BE "; }
.bl-mehr-x { margin-top: 8px; font-size: 12px; }


/* ======================================================================
   RAUM-DIAGRAMM (25.08.2026)
   ======================================================================
   Vier Auswahlfelder ueber dem Bild: waagerecht, senkrecht, Darstellung,
   Farbe. Sie stehen in EINER Zeile und brechen auf schmalen Fenstern um -
   ein Steuerelement, das man scrollen muss, benutzt niemand. */
.rd-steuer {
  display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 12px;
  padding: 10px 12px; border-radius: 7px;
  background: var(--card2); border: 1px solid var(--border);
}
.rd-steuer > div { flex: 1 1 170px; min-width: 150px; }
.rd-steuer .f { margin-bottom: 4px; }
.rd-steuer select { width: 100%; }
