/* Die Bausteine des Abrechnungsprofis-Design-Systems, nachgebaut (Konzept 6.1).

   Das Mockup benutzt die React-Komponenten des Design Systems (TopBar, Card,
   DataTable, Badge, StatTile). Die brauchen React und das DC-Bundle — beides
   waere Fremdcode im Ordner. Hier stehen dieselben Bausteine als CSS auf
   denselben Tokens: Karten mit Haarlinien, Mono-Zahlen mit tabular-nums,
   9,5-px-Kennungen in Versalien mit 0.18em Sperrung, Koralle nur fuer
   Hervorhebung und Konflikt. */

html, body { height: 100%; }
body { background: var(--bg-page); }

.ap-app { display: flex; flex-direction: column; height: 100vh; }

/* ------------------------------------------------------------- Kopfzeile */
.ap-top {
  display: flex; align-items: center; gap: var(--sp-6);
  height: 56px; flex: none; padding: 0 var(--sp-5);
  border-bottom: 1px solid var(--divider, var(--line-border));
  background: var(--surface-card);
}
.ap-marke { font: var(--type-h3); font-size: 17px; letter-spacing: -0.02em; }
.ap-marke span { color: var(--text-accent); }
.ap-topnav { display: flex; gap: var(--sp-5); }
.ap-topnav button {
  all: unset; cursor: pointer; font-size: var(--fs-body-s);
  color: var(--text-muted); padding: 2px 0; border-bottom: 2px solid transparent;
}
.ap-topnav button:hover { color: var(--text-body); }
.ap-topnav button.ist-aktiv { color: var(--text-body); border-bottom-color: var(--mint-500); }
.ap-topnav button.ist-deaktiviert,
.ap-nav button.ist-deaktiviert {
  color: var(--text-subtle); cursor: not-allowed; opacity: 0.55;
}
.ap-topnav button.ist-deaktiviert:hover { color: var(--text-subtle); }
.ap-nav button.ist-deaktiviert:hover { background: transparent; color: var(--text-subtle); }
.ap-topaktionen { margin-left: auto; display: flex; gap: var(--sp-2); align-items: center; }
/* Sonne/Mond statt Textknopf: gleiches Muster wie Schloss/Auge — kompakt,
   nur das Zeichen, ohne den Textknopf-Seitenabstand. */
.ap-knopf.ap-themeschalter { padding: 6px; display: inline-flex; align-items: center; }

/* ---------------------------------------------------------- Kontomenue */
.ap-konto { position: relative; display: flex; }
.ap-avatar {
  all: unset; cursor: pointer; display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; box-sizing: border-box;
  background: var(--surface-inset); color: var(--text-muted);
  border: 1px solid var(--line-border);
}
.ap-avatar:hover { color: var(--text-body); border-color: var(--line-strong); }
.ap-avatar[aria-expanded="true"] {
  color: var(--mint-500); border-color: var(--mint-500); background: var(--surface-hover);
}
.ap-konto-menue {
  display: none;
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 50;
  min-width: 160px; padding: var(--sp-2);
  background: var(--surface-card); border: 1px solid var(--line-border);
  border-radius: var(--radius-card); box-shadow: var(--shadow-raised);
  flex-direction: column; gap: 2px;
}
.ap-konto-menue.ist-offen { display: flex; }
.ap-konto-eintrag {
  all: unset; box-sizing: border-box; cursor: pointer; display: block; width: 100%;
  padding: 7px var(--sp-3); font-size: var(--fs-body-s); color: var(--text-body);
  border-radius: var(--radius-control);
}
.ap-konto-eintrag:hover { background: var(--surface-hover); }

/* ------------------------------------------------------------ Navigation */
.ap-mitte { display: flex; flex: 1; min-height: 0; }
.ap-nav {
  width: 236px; flex: none; overflow: auto; padding: var(--sp-5) 0;
  border-right: 1px solid var(--line-border); background: var(--surface-card);
  display: flex; flex-direction: column; gap: var(--sp-5);
}
.ap-nav-gruppe { display: flex; flex-direction: column; gap: 2px; }
.ap-nav-titel { padding: 0 var(--sp-5) var(--sp-2); }
.ap-nav button {
  all: unset; cursor: pointer; display: flex; align-items: center; gap: var(--sp-2);
  padding: 7px var(--sp-5); font-size: var(--fs-body-s); color: var(--text-muted);
}
.ap-nav button:hover { background: var(--surface-hover); color: var(--text-body); }
.ap-nav button.ist-aktiv {
  background: var(--surface-hover); color: var(--text-body);
  box-shadow: inset 2px 0 0 var(--mint-500);
}
.ap-nav .ap-zaehler {
  margin-left: auto; font: var(--type-data); font-size: 11px; color: var(--text-subtle);
}
.ap-nav-fuss {
  margin-top: auto; padding: var(--sp-4) var(--sp-5) 0;
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
}

/* ---------------------------------------------------------------- Inhalt */
.ap-inhalt {
  flex: 1; min-width: 0; overflow: auto; padding: var(--sp-6);
  display: flex; flex-direction: column; gap: var(--sp-5);
}
.ap-krume { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.ap-krume .ap-pfad { font: var(--type-data); color: var(--text-subtle); }
.ap-krume .ap-hier { font-size: var(--fs-body-s); }

/* ----------------------------------------------------------------- Karte */
.ap-karte {
  background: var(--surface-card); border: 1px solid var(--line-border);
  border-radius: var(--radius-card); box-shadow: var(--shadow-raised);
  display: flex; flex-direction: column; min-width: 0;
}
.ap-karte-kopf {
  display: flex; align-items: flex-end; gap: var(--sp-4);
  padding: var(--sp-4) var(--gutter-card); border-bottom: 1px solid var(--line-hair);
}
.ap-karte-kopf .ap-titelblock { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ap-karte-kopf h3 { font: var(--type-h3); }
.ap-karte-kopf .ap-aktionen { margin-left: auto; display: flex; gap: var(--sp-2); align-items: center; }
/* Der Titel der Herkunftskette steht in **einer** Zeile: passt er nicht, wird
   er gekuerzt und der volle Text steht im Tooltip. Umbrechend wurde die
   Kopfzeile je nach Laenge der LV-Bezeichnung zwei- oder dreizeilig — und
   schob mit jedem Klick das Planblatt darunter (Bens Vorgabe 2026-08-19). */
.ap-kettenkarte .ap-karte-kopf h3 {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ap-karte-leib { padding: var(--gutter-card); min-width: 0; }
.ap-karte-leib.ap-blank { padding: 0; }
.ap-tick { font: var(--type-tick); letter-spacing: var(--ls-tick); text-transform: uppercase; color: var(--text-accent); }

/* ------------------------------------------------------------- Kennwerte */
.ap-kacheln { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--sp-3); }
.ap-kachel {
  background: var(--surface-card); border: 1px solid var(--line-border);
  border-radius: var(--radius-card); padding: var(--sp-4) var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
.ap-kachel .ap-wert { font-family: var(--font-mono); font-size: 26px; font-variant-numeric: tabular-nums; }
.ap-kachel .ap-einheit { font-size: var(--fs-caption); color: var(--text-subtle); margin-left: 4px; }

/* --------------------------------------------------------------- Tabelle */
/* Die Hoehe steht hier nur als Rueckfall: gewaehlt wird sie unten in der
   Leiste (10/20/50/100 Zeilen), gesetzt in Pixeln von app.js. */
.ap-tabellenrahmen { overflow: auto; max-height: 460px; }
/* Leiste unter der Tabelle: Tastenhinweis links, Zeilenwahl rechts. */
.ap-tabellenleiste {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  padding: var(--sp-3) var(--gutter-card);
  border-top: 1px solid var(--line-hair);
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
}
.ap-tabellenleiste .ap-fensterwahl {
  margin-left: auto; display: flex; align-items: center; gap: var(--sp-2);
}
.ap-tabellenleiste select {
  font-family: var(--font-mono); font-size: var(--fs-caption); line-height: 1;
  padding: 5px var(--sp-2); color: var(--text-body);
  background: var(--surface-inset); border: 1px solid var(--line-border);
  border-radius: var(--radius-control); cursor: pointer;
}
.ap-tabellenleiste select:focus { outline: none; border-color: var(--focus-ring); }
/* Freiraum unter der letzten Zeile — die Hoehe setzt app.js. */
.ap-nachlauf { width: 100%; }
table.ap-tabelle { width: 100%; border-collapse: collapse; font-size: var(--fs-body-s); }
/* Feste Breiten NUR auf den ersten vier Spalten (User-Vorgabe 2026-09-10,
   praezisiert): die Schwankung zwischen den Aufmaßplänen kam von genau
   diesen — Kennzeichen/Pos. LV/Familientyp sind unbrechbare Tokens ohne
   Leerzeichen, Bezeichnung ist unterschiedlich langer Fließtext. Die
   restlichen Spalten (Zahlen, Einheit, Betrag, Status) sind über alle
   Plaene hinweg von Natur aus kurz und aehnlich lang — table-layout bleibt
   deshalb "auto", sie bleiben flexibel und bemessen sich wie bisher an
   ihrem Inhalt; table-layout:fixed haette JEDE Spalte auf einen festen
   Wert gezwungen. Familientyp braucht overflow-wrap, sonst liefe sein
   unbrechbarer Text ueber die feste Breite hinaus. Reihenfolge = SPALTEN
   in app.js, Spalte 1 ist die Hakenspalte. Eigene Klasse
   ".ap-tabelle-positionen" statt nur ".ap-tabelle": "Alle Plaene"
   (ansichtListe) traegt dieselbe Basisklasse, hat aber eine ganz andere
   Spaltenzahl und -bedeutung. */
table.ap-tabelle-positionen th:nth-child(2), table.ap-tabelle-positionen td:nth-child(2) { width: 105px; }  /* Kennz. */
table.ap-tabelle-positionen th:nth-child(3), table.ap-tabelle-positionen td:nth-child(3) { width: 125px; }  /* Pos. LV */
table.ap-tabelle-positionen th:nth-child(4), table.ap-tabelle-positionen td:nth-child(4) { width: 320px; }  /* Bezeichnung */
table.ap-tabelle-positionen th:nth-child(5), table.ap-tabelle-positionen td:nth-child(5) {
  width: 210px; overflow-wrap: anywhere;                                             /* Familientyp */
}
table.ap-tabelle th {
  position: sticky; top: 0; z-index: 2; background: var(--surface-card);
  text-align: left; font: var(--type-tick); letter-spacing: var(--ls-tick);
  text-transform: uppercase; color: var(--text-subtle);
  padding: var(--sp-3) var(--sp-3); border-bottom: 1px solid var(--line-border);
  white-space: nowrap;
}
/* Die Zahlen stehen rechtsbuendig — die Ueberschrift muss mit, sonst sucht
   das Auge die Spalte (Bens Rueckmeldung 2026-08-15). Ohne diese Zeile
   gewinnt das text-align: left von oben, es ist spezifischer als .ap-num. */
table.ap-tabelle th.ap-num { text-align: right; }

/* Sortieren: jede Ueberschrift ist ein Schalter. Der Pfeil steht blass da,
   bis die Spalte sortiert — dann zeigt er die Richtung. Er ist immer
   vorhanden, damit die Spaltenbreite beim Sortieren nicht springt. */
table.ap-tabelle th.ap-sortierbar { cursor: pointer; user-select: none; }
table.ap-tabelle th.ap-sortierbar:hover { color: var(--text-body); }
table.ap-tabelle th.ist-sortiert { color: var(--text-accent); }
table.ap-tabelle th .ap-pfeil { margin-left: 5px; font-size: 9px; opacity: .35; }
table.ap-tabelle th.ist-sortiert .ap-pfeil { opacity: 1; }
table.ap-tabelle tbody tr.ap-keine-treffer { cursor: default; }
table.ap-tabelle tbody tr.ap-keine-treffer:hover { background: none; }
table.ap-tabelle tbody tr.ap-keine-treffer td {
  padding: var(--sp-6); text-align: center; color: var(--text-subtle);
}

/* Filterfeld im Kartenkopf. Wird die Karte schmal, rutscht es unter die
   Ueberschrift, statt sie in vier Zeilen zu quetschen. */
.ap-positionen .ap-karte-kopf { flex-wrap: wrap; row-gap: var(--sp-3); }
.ap-positionen .ap-karte-kopf .ap-titelblock { flex: 1 1 240px; }
.ap-suche {
  font-family: var(--font-mono); font-size: var(--fs-body-s); line-height: 1;
  padding: 7px var(--sp-3); width: 190px;
  color: var(--text-body); background: var(--surface-inset);
  border: 1px solid var(--line-border); border-radius: var(--radius-control);
}
.ap-suche::placeholder { color: var(--text-subtle); }
.ap-suche:focus { outline: none; border-color: var(--focus-ring); }
.ap-treffer { font: var(--type-data); font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
/* Die Hakenspalte (Bens Vorgabe 2026-09-03): schmal, ganz links, in Kopf,
   Koerper und Fuss dieselbe Breite. Sie traegt keinen Wert der Zeile --
   nur den Haken, der das Bauteil im Plan markiert. Die ganze Zelle ist das
   Ziel des Klicks, nicht bloss das Kaestchen darin; deshalb steht der Haken
   mittig und die Zelle bekommt denselben Zeiger wie die Zeile. */
table.ap-tabelle th.ap-hakenspalte, table.ap-tabelle td.ap-hakenspalte {
  width: 34px; padding-left: var(--sp-3); padding-right: 0;
  text-align: center; vertical-align: middle;
}
.ap-haken {
  /* `accent-color` faerbt das Kaestchen des Browsers, ohne es nachzubauen --
     ein selbst gezeichneter Haken verlöre Tastatur und Vorlesefunktion. */
  accent-color: var(--coral-500); cursor: pointer;
  width: 13px; height: 13px; margin: 0; vertical-align: middle;
}
table.ap-tabelle td {
  padding: 7px var(--sp-3); border-bottom: 1px solid var(--line-hair);
  color: var(--text-body); vertical-align: top;
}
table.ap-tabelle tbody tr { cursor: pointer; }
table.ap-tabelle tbody tr:hover { background: var(--surface-hover); }
/* Markierte Zeile (5.40): dieselbe Farbe wie die markierte Flaeche im Plan —
   die Markierung ist eine Auswahl fuer viele und traegt deshalb deren Ton,
   nur ohne den Rahmen, der der einen gewaehlten Zeile gehoert. */
table.ap-tabelle tbody tr.ist-markiert { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--coral-500); }
table.ap-tabelle tbody tr.ist-gewaehlt { background: var(--status-warn-soft); outline: 1px solid var(--coral-500); outline-offset: -1px; }
table.ap-tabelle tbody tr.ist-gewaehlt td { color: var(--coral-400); }
table.ap-tabelle tfoot td {
  padding: var(--sp-3); border-top: 1px solid var(--line-border);
  font-weight: var(--fw-bold); background: var(--surface-card);
  position: sticky; bottom: 0;
}
.ap-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.ap-mono { font-family: var(--font-mono); white-space: nowrap; }
.ap-gedaempft { color: var(--text-subtle); }
/* Erfundener Einheitspreis (Vermerk „Dummy" im Preisstamm): die Zahl steht da,
   sieht aber nicht aus wie eine belastbare — kursiv und in der Warnfarbe. */
.ap-erfunden, table.ap-tabelle td.ap-erfunden {
  color: var(--status-warn); font-style: italic;
}
/* Die Menge ist die Zahl, um die es geht — gruen und fett (Bens Vorgabe
   2026-08-15). Der Selektor nennt Tabelle und `td`, weil `.ap-menge` allein
   gegen `table.ap-tabelle td { color: … }` weiter oben verliert; die zweite
   Regel haelt das Gruen auch in der gewaehlten Zeile, deren Zellen sonst das
   Korall der Auswahl tragen. */
.ap-menge, table.ap-tabelle td.ap-menge {
  color: var(--text-accent); font-weight: var(--fw-bold);
}
table.ap-tabelle tbody tr.ist-gewaehlt td.ap-menge { color: var(--text-accent); }
/* Die Einheit gehoert zur Menge und steht wie sie gruen und fett (Bens Vorgabe
   2026-08-16) — auch in der Fusszeile und in der gewaehlten Zeile. */
table.ap-tabelle td.ap-einheit { color: var(--text-accent); font-weight: var(--fw-bold); }
table.ap-tabelle tbody tr.ist-gewaehlt td.ap-einheit { color: var(--text-accent); }

/* --------------------------------------------------------------- Abzeichen */
.ap-badge {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font: var(--type-data); font-size: 11px; padding: 2px 7px;
  border-radius: var(--radius-chip); border: 1px solid transparent;
}
.ap-badge::before { content: ""; width: 5px; height: 5px; border-radius: 99px; background: currentColor; }
.ap-badge.ok { color: var(--status-ok); background: var(--status-ok-soft); border-color: var(--status-ok-soft); }
.ap-badge.warn { color: var(--status-warn); background: var(--status-warn-soft); }
.ap-badge.review { color: var(--status-review-fg); background: var(--status-review-soft); }
.ap-badge.neutral { color: var(--text-muted); background: var(--status-neutral-soft); }
.ap-badge.neutral::before { display: none; }

/* --------------------------------------------------------------- Schalter */
.ap-knopf {
  all: unset; cursor: pointer; font-size: var(--fs-body-s); line-height: 1;
  padding: 8px var(--sp-4); border-radius: var(--radius-control);
  background: var(--action-primary-bg); color: var(--action-primary-fg);
  font-weight: var(--fw-medium);
}
.ap-knopf:hover { background: var(--action-primary-hover); }
.ap-knopf.ap-schlicht {
  background: transparent; color: var(--action-secondary-fg);
  border: 1px solid var(--action-secondary-border);
}
.ap-knopf.ap-schlicht:hover { background: var(--surface-hover); }
.ap-knopf.ap-still { background: transparent; color: var(--text-muted); border: 1px solid transparent; }
.ap-knopf.ap-still:hover { color: var(--text-body); background: var(--surface-hover); }

/* ---------------------------------- Kennzahlen der LV-Position (5.18) */
/* „Projekt" und „Plan" sind zwei **Stationen wie die anderen** — eine Reihe,
   dieselben Trenner (Bens Vorgabe 2026-08-17). Sie stehen an der Stelle der
   frueheren Stationen „Plan" und „Modell". */
/* Name unter der Ueberschrift, in **einer** Zeile: passt er nicht, wird er
   gekuerzt — der volle Name steht im Tooltip. Umbrechen liess die Spalte je
   nach Projektnamen unterschiedlich hoch werden. */
.ap-statstation .ap-stationswert {
  min-height: 0; margin-bottom: 0; font-size: 12.5px; line-height: 1.4;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Zwei Spalten **nebeneinander** in der Station (Bens Vorgabe 2026-08-17):
   links Text und Zahlen mit ihrer eigenen Breite und Hoehe, rechts die
   Restbreite des Abschnitts bei gleicher Hoehe — dort wird das Diagramm
   eingepasst. Die Stationsbreite bleibt dabei die aller anderen. */
.ap-station.ap-statstation .ap-text {
  /* Die linke Unterspalte nimmt, was Name und Zahl **ohne Umbruch** brauchen
     — ihre Breite setzt `zahlenspaltenAngleichen()` nach dem Messen, gleich
     fuer „Projekt" und „Plan". Das Bild bekommt den Rest. */
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  column-gap: var(--sp-3); align-items: stretch;
}
.ap-statzahlen { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* Name links, Zahl rechts in **einer** Zeile (Bens Vorgabe 2026-08-17). Die
   Zahlen stehen dadurch rechtsbuendig untereinander; enger wird notfalls der
   Name, denn eine gekuerzte Menge waere eine falsche. */
.ap-statzeile {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-2);
}
.ap-statname {
  font-size: 11px; color: var(--text-subtle); line-height: 1.6; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ap-statwert {
  font-family: var(--font-mono); font-size: 13px; line-height: 1.6;
  font-variant-numeric: tabular-nums; color: var(--text-body);
  white-space: nowrap; flex: 0 0 auto; text-align: right;
}
/* Das Bild haengt **absolut** in seiner Zelle: nur so bestimmt es die Hoehe
   der Zeile nicht mit, sondern richtet sich nach ihr — hoechstens so hoch wie
   der Text links, hoechstens so breit wie die freie Flaeche daneben (Bens
   Vorgabe 2026-08-17). `preserveAspectRatio` haelt dabei das Verhaeltnis; die
   Mindesthoehe traegt den Fall, dass das Bild unter den Text rutscht. */
.ap-statbild { position: relative; min-width: 0; min-height: 70px; }
/* Absolut, damit die Zelle die Zeilenhoehe nicht mitbestimmt: das Bild
   richtet sich nach dem Text, nicht der Text nach dem Bild. Gezeichnet wird
   1:1 in die gemessene Flaeche (app.js), deshalb hier keine Skalierung. */
.ap-statbild svg { position: absolute; left: 0; top: 0; }

/* Das Histogramm. Es waechst ueber die viewBox mit; die Schrift skaliert
   dabei mit und bleibt im Verhaeltnis lesbar. */
/* Keine Skalierung ueber CSS: app.js zeichnet 1:1 in die gemessene Flaeche
   und setzt Breite und Hoehe selbst. Ein `width: 100%` hier zoege das fertige
   Bild wieder auf die Zellenbreite auf — samt Schrift. */
.ap-histogramm { display: block; overflow: visible; }
.ap-hist-balken { fill: var(--mint-500); fill-opacity: 0.35; stroke: var(--mint-500); stroke-width: 0.5; }
/* Die Glocke ist auf die Balken geeicht (n x Klassenbreite x Dichte) — sie
   liegt im selben Massstab und der Vergleich sagt etwas. */
.ap-hist-glocke { fill: none; stroke: var(--text-muted); stroke-width: 1.2; stroke-linejoin: round; }
.ap-hist-achse { stroke: var(--line-border); stroke-width: 1; }
.ap-hist-mittel { stroke: var(--line-border); stroke-width: 1; stroke-dasharray: 2 3; }
/* Die Menge des gewaehlten Bauteils — die einzige rote Linie im Bild. */
.ap-hist-marke { stroke: var(--coral-500); stroke-width: 1.5; }
.ap-hist-marketext {
  fill: var(--coral-400); font-family: var(--font-mono); font-size: 11px;
}
.ap-hist-achstext, .ap-hist-leer {
  fill: var(--text-subtle); font-family: var(--font-mono); font-size: 10px;
}

/* ------------------------------------------------------- Herkunftskette */
/* Fuenf Stationen in **einer** Reihe (Bens Vorgabe 2026-08-17): ab 1180 px
   Fensterbreite stehen genau fuenf Spalten nebeneinander, auch wenn eine davon
   knapp wird. Darunter bricht das Raster um, statt die Spalten unlesbar zu
   quetschen — ein Histogramm in 90 px waere keine Auskunft mehr. */
.ap-kette { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: var(--sp-4) var(--sp-5); }
@media (min-width: 1180px) {
  /* Alle fuenf Stationen gleich breit (Bens Vorgabe 2026-08-17). Was der Text
     in „Projekt" und „Plan" braucht, holt sich die **linke Unterspalte**;
     das Bild bekommt den Rest. */
  .ap-kette { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
.ap-station { display: grid; grid-template-columns: 13px 1fr; gap: var(--sp-3); }
.ap-station .ap-punktspalte { display: flex; flex-direction: column; align-items: center; }
.ap-station .ap-punkt { width: 7px; height: 7px; border-radius: 99px; background: var(--mint-500); margin-top: 5px; }
.ap-station .ap-faden { flex: 1; width: 1px; background: var(--line-hair); }
.ap-station .ap-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* Feste Zeilenhöhen in der Kette: ohne sie wird die Karte je nach Textlänge
   der gewählten Zeile unterschiedlich hoch und schiebt bei jedem Klick alles
   darunter nach oben oder unten. */
.ap-station .ap-stationswert {
  font-family: var(--font-mono); font-size: 13px;
  font-variant-numeric: tabular-nums; min-height: 2.7em;
}
.ap-station .ap-detail {
  font-size: 11.5px; color: var(--text-subtle); line-height: 1.5;
  min-height: 3em;
}

/* -------------------------------------------------------------- Planblatt */
/* 65 % Planblatt, der Rest die Bilder (Bens Vorgabe 2026-08-15). Die
   Zeilenhoehe gibt allein das Planblatt vor — dafuer haengt der Bildstapel
   absolut und zaehlt nicht mit. min-height faengt sehr flache Plaene ab. */
.ap-plangitter {
  display: grid; grid-template-columns: minmax(0, 65fr) minmax(0, 35fr);
  gap: var(--sp-4); align-items: stretch; min-height: 340px;
  /* Der Inhalt ist eine Spalten-Flexbox — ohne das hier staucht sie das
     Raster und die Karten stehen ueber ihren Rahmen hinaus. */
  flex: 0 0 auto;
}
@media (max-width: 1100px) { .ap-plangitter { grid-template-columns: 1fr; min-height: 0; } }

.ap-plangitter > .ap-karte { display: flex; flex-direction: column; min-width: 0; }
/* Der Rahmen um die Planflaeche. Er traegt nur den Vollbildknopf und muss
   dafuer stillstehen: in der Planflaeche selbst — die scrollt — wanderte der
   Knopf mit dem Blatt aus dem Bild. */
.ap-planrahmen {
  position: relative; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
}
/* **Feste** Hoehe, nicht nur eine obere Schranke (Bens Vorgabe 2026-08-29).
   Frueher stand hier `max-height: 66vh`, und die Flaeche schrumpfte auf das
   Blatt darin. Damit hing ihre Hoehe am Zoom — und die Untergrenze des Zooms
   haengt an ihrer Hoehe (app.js, `kleinsterZoom`): kleineres Blatt, niedrigere
   Flaeche, wieder kleineres Blatt. Eine feste Hoehe macht daraus einen
   stehenden Rahmen, in den das Blatt hineingezoomt wird. */
.ap-planflaeche {
  flex: 1 1 auto;
  position: relative; background: var(--surface-inset);
  border-top: 1px solid var(--line-hair); border-bottom: 1px solid var(--line-hair);
  overflow: auto; height: 66vh;
}
/* Das Blatt steht **mittig**, sobald es schmaler ist als sein Rahmen (Bens
   Vorgabe 2026-08-29) — herausgezoomt rutschte es sonst an den linken Rand
   und die ganze rechte Haelfte blieb leer. Fuellt es den Rahmen oder ist es
   breiter, rechnet `auto` zu null und nichts aendert sich. Die Luft fuer eine
   aufgeklappte Liste setzt `planLuftSetzen` als `marginLeft` am Element; das
   sticht diese Regel und gilt weiter. */
.ap-planbuehne {
  position: relative; transform-origin: 0 0;
  margin-left: auto; margin-right: auto;
}
.ap-planbuehne img { display: block; width: 100%; height: auto; }
/* Der Platzhalter an der rechten unteren Ecke des Blatts: er traegt nichts und
   ist nur da, damit das Planblatt nach rechts und nach unten so weit scrollt,
   dass eine aufgeklappte Liste am Rand noch hineinpasst (Konzept 5.14).
   Absolut gesetzt, damit er nichts verschiebt — die Luft links und oben machen
   dagegen die Raender der Buehne, weil Raender rechts und unten nicht zum
   Scrollbereich zaehlen. Seine Masse setzt app.js, nur nach Bedarf. */
.ap-planluft { position: absolute; top: 100%; left: 100%; }
.ap-leiter { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
/* Führungslinie und Ankerpunkt: der Kopf des Tags darf ausweichen, aber es
   muss ablesbar bleiben, zu welchem Bauteil er gehört (Konzept 6.3.3).
   Gezeichnet wird ein Kantenzug (polyline) — die gedruckte Revit-Fahne ist
   geknickt, und die interaktive folgt ihr (D29). `fill: none` ist dabei
   Pflicht: eine polyline füllt sonst die Fläche zwischen ihren Enden. */
.ap-leiter polyline { fill: none; stroke: var(--line-strong); stroke-width: 1;
  stroke-linejoin: round; }
.ap-leiter polyline.ist-gewaehlt { stroke: var(--coral-500); stroke-width: 1.5; }
.ap-leiter circle.ap-anker { fill: var(--line-strong); }
.ap-leiter circle.ap-anker.ist-gewaehlt { fill: var(--coral-500); }

/* Die Umrandung des gewaehlten Bauteils im Plan (Konzept 5.29): der Schnitt
   bzw. die Projektion in der Akzentfarbe, dazu je ein Mass in x und y. Der
   Plan darunter ist ein gedrucktes Blatt voller feiner Linien — die Flaeche
   ist deshalb schwach gefuellt und die Kontur kraeftig, sonst geht die
   Markierung in der Zeichnung unter (dieselbe Ueberlegung wie beim
   Durchbruch in 5.7). */
/* Der Umriss: jede Kante des Bauteils, wie die Ansicht sie zeigt. `fill:none`
   ist Pflicht — eine polyline fuellt sonst die Flaeche zwischen ihren Enden.

   **1,8 statt 1,5** (Bens Vorgabe 2026-08-29): der Umriss ist die Aussage des
   Blatts — das ist das Bauteil —, und der gedruckte Plan darunter ist voller
   Linien derselben Staerke. Eine Spur kraeftiger hebt ihn heraus, ohne dass
   er zum Balken wird. */
.ap-planbox polyline.ap-umriss {
  fill: none; stroke: var(--coral-500); stroke-width: 1.8;
  stroke-linejoin: round; stroke-linecap: round;
}
/* Die verdeckte Kante (5.46): **dieselbe Linie wie der Umriss, nur
   gestrichelt**. Farbe, Breite und Deckkraft kommen unveraendert aus der
   Regel darueber; hier steht allein das Strichmuster.

   **Gleiche Breite, nicht die halbe** (Bens Vorgabe 2026-09-03). Die DIN
   haelt die verdeckte Linie halb so breit, und die 1. Fassung folgte dem mit
   1,2 und gedaempfter Deckkraft. Am Modell ging sie damit unter: die
   Fluchtlinien der Masskette laufen ueber alle Felder, und eine davon liegt
   genau auf der verdeckten Kante — an einer Bauteilecke gibt es beides. Die
   Fluchtlinie ist eine **Strichpunktlinie**; ueber einem Faden von 1,2 war
   sie das Einzige, was man noch sah. Ueber den vollen Umrisslinien stoert
   sie niemanden, weil die kraeftiger sind — also bekommt die verdeckte
   Kante dieselbe Breite. Sie bleibt unterscheidbar: durch die Luecken, nicht
   durch die Schwaeche.

   `stroke-linecap: butt` haelt die Striche gerade so lang, wie sie gedacht
   sind — bei `round` (vom Umriss geerbt) wuechse jeder um seine halbe Breite
   an beiden Enden, und aus 7 px Strich mit 4 px Luft wuerde eine fast
   durchgehende Linie. Bei 1,8 Breite faellt das staerker ins Gewicht als bei
   1,2, und das Muster traegt jetzt die ganze Aussage. */
.ap-planbox polyline.ap-umriss.ap-verdeckt {
  stroke-dasharray: 7 4; stroke-linecap: butt;
}
/* Die Flaeche des Bauteils, ganz schwach eingefaerbt: sie zeigt auf einen
   Blick, welches Stueck gemeint ist, wo eine Kontur aus duennen Linien im
   gedruckten Plan untergeht (Bens Vorgabe 2026-08-28). Ohne Kontur — die
   zeichnen die Umrisslinien darueber.

   **Ein** Pfad fuer alle Raender, `evenodd` als Regel (5.32): der Rand eines
   Durchbruchs liegt darin und wird dadurch ausgespart statt ein zweites Mal
   gefuellt. */
.ap-planbox path.ap-fuellung {
  fill: var(--accent-soft); stroke: none;
}
/* Die schlichte Darstellung (Bens Vorgabe 2026-08-30): das Bauteil nur als
   Flaeche, ohne Massketten und ohne Teilflaechen. Sie darf **decken** — es
   gibt nichts mehr darunter, das man noch nachrechnen wollte, und die Aussage
   ist allein „dieses Stueck ist gemeint". Die schwache Fuellung der
   Arbeitsansicht waere dafuer zu leise. */
.ap-planbox path.ap-fuellung.ap-voll {
  fill: var(--coral-500); fill-opacity: 0.55;
}
/* Die markierten Bauteile (5.40, Bens Vorgabe 2026-09-03): dieselbe Flaeche
   in derselben Farbe wie beim gewaehlten — nur eine Spur leiser gefuellt.
   Markiert sind viele auf einmal, und bei voller Deckung stuenden sie als
   roter Teppich statt als einzelne Stuecke da. Der Umriss bleibt unveraendert
   kraeftig: er traegt die Form. */
.ap-planbox.ist-markiert path.ap-fuellung.ap-voll { fill-opacity: 0.35; }

/* Die Farbreihe der Teilflaechen (Bens Vorgabe 2026-08-29): rot, lila, blau,
   gelb, gruen — in dieser Reihenfolge, und wieder von vorn, wenn ein Bauteil
   mehr als fuenf Stuecke hat.

   **Warum die Reihe hier steht und nicht in den Token.** `tokens/*.css` ist
   die woertliche Kopie des Design Systems; Lila und Blau kommen darin nicht
   vor (es fuehrt Mint, Koralle, Bernstein und die Graustufen). Sie hier zu
   ergaenzen hiesse, die Kopie von ihrer Quelle wegzuschreiben. Rot, Gelb und
   Gruen kommen deshalb aus den Token, Lila und Blau stehen daneben — in
   derselben Machart: heller Ton auf dem dunklen Grund, dunklerer im hellen
   Thema, und die Fuellung derselbe Ton mit 18 % Deckung (achtstellige Hex,
   wie `--accent-soft`).

   Zwei Ziffern hinter der Farbe sind die Deckung der Fuellung: `73` = 45 %
   im dunklen Thema, `66` = 40 % im hellen. Sie sind zweimal gestiegen, und
   beide Male aus demselben Grund — die Farbe soll die Zuordnung Zahl ->
   Stueck tragen, und dafuer muss man sie erkennen:

   * 18/16 % waren die erste Fassung. Auf einem gedruckten Blatt voller Linien
     war die Farbe damit eher zu ahnen als zu sehen.
   * 30/26 % seit dem 2026-08-29 (Bens Vorgabe „etwas deckender").
   * 45/40 % seit dem 2026-08-31 (Bens Vorgabe „weniger transparent und
     kraeftiger") — dazwischen ist der Planhintergrund hinter der Draufsicht
     ungedaempft geworden (5.36), und gegen ein volles Planbild traegt eine
     Fuellung von 30 % nicht mehr.

   Die Grenze nach oben bleibt dieselbe: die Flaeche liegt **ueber** der
   Zeichnung und darf sie nicht zudecken. Im hellen Thema traegt der dunklere
   Ton weiter — er deckt bei gleicher Zahl staerker, daher dort fuenf Punkte
   weniger. */
.ap-planbox {
  --teil-1: var(--coral-500); --teil-1-soft: #FF6A4D73;
  --teil-2: #B98CF0;          --teil-2-soft: #B98CF073;
  --teil-3: #6EC2F5;          --teil-3-soft: #6EC2F573;
  --teil-4: var(--amber-500); --teil-4-soft: #E0A33A73;
  --teil-5: var(--mint-500);  --teil-5-soft: #6EE7B073;
}
[data-theme="light"] .ap-planbox {
  --teil-1: var(--coral-600); --teil-1-soft: #D94A2C66;
  --teil-2: #7B4FC0;          --teil-2-soft: #7B4FC066;
  --teil-3: #1F79B8;          --teil-3-soft: #1F79B866;
  --teil-4: var(--amber-600); --teil-4-soft: #C08A1F66;
  --teil-5: var(--mint-700);  --teil-5-soft: #0E7B5566;
}

/* Die Teilflaechen (5.31): sie zerlegen das Bauteil, statt es zu begrenzen —
   deshalb gestrichelt und duenner als die Kontur. Seit 5.31.2 traegt jede
   ihre eigene Farbe; die Faerbung der ganzen Flaeche entfaellt dann, sonst
   laege unter Lila und Blau ein Rotstich. */
.ap-planbox polygon.ap-teilflaeche {
  fill: none; stroke: var(--coral-500); stroke-width: 0.9;
  stroke-dasharray: 5 4; opacity: 0.75;
}
.ap-planbox polygon.ap-teilflaeche.ap-farbe-1 { fill: var(--teil-1-soft); stroke: var(--teil-1); }
.ap-planbox polygon.ap-teilflaeche.ap-farbe-2 { fill: var(--teil-2-soft); stroke: var(--teil-2); }
.ap-planbox polygon.ap-teilflaeche.ap-farbe-3 { fill: var(--teil-3-soft); stroke: var(--teil-3); }
.ap-planbox polygon.ap-teilflaeche.ap-farbe-4 { fill: var(--teil-4-soft); stroke: var(--teil-4); }
.ap-planbox polygon.ap-teilflaeche.ap-farbe-5 { fill: var(--teil-5-soft); stroke: var(--teil-5); }
/* Die Zahl gehoert zu ihrem Stueck und traegt deshalb dieselbe Farbe — sonst
   muesste man von einer roten Zahl auf eine blaue Flaeche schliessen. Der
   Rand um sie bleibt der des Themas (5.32.1). */
.ap-planbox text.ap-flaechenzahl.ap-farbe-1 { fill: var(--teil-1); }
.ap-planbox text.ap-flaechenzahl.ap-farbe-2 { fill: var(--teil-2); }
.ap-planbox text.ap-flaechenzahl.ap-farbe-3 { fill: var(--teil-3); }
.ap-planbox text.ap-flaechenzahl.ap-farbe-4 { fill: var(--teil-4); }
.ap-planbox text.ap-flaechenzahl.ap-farbe-5 { fill: var(--teil-5); }
/* Nur noch der Rueckfall (Huelle bzw. Bounding-Box): dort steht keine
   Ansichtsgeometrie zur Verfuegung, und die Flaeche sagt „ungefaehr hier". */
.ap-planbox .ap-flaeche {
  fill: var(--accent-soft); stroke: var(--coral-500); stroke-width: 1.5;
}
.ap-planbox line.ap-masslinie {
  stroke: var(--coral-500); stroke-width: 1.2; stroke-linecap: round;
}
/* **Strichpunkt** statt gestrichelt (Bens Vorgabe 2026-08-29). Die Hilfslinie
   laeuft seit 5.32 durch das Bauteil hindurch bis zu seiner Ecke; dort trifft
   sie auf die Umrisslinien und die gestrichelten Teilflaechenraender. Der
   Strichpunkt unterscheidet sie von beiden — er ist in der Zeichnung die
   Linie, die etwas anzeigt statt etwas zu begrenzen.

   `stroke-linecap` bleibt ungesetzt (`butt`): mit runden Enden liefe der
   Punkt zum Strich aus und das Muster waere wieder eine Strichlinie.

   **Das Muster muss lang sein** (Bens Befund 2026-08-29: „sind noch keine
   Strichpunktlinien"). Der erste Versuch — 7 3 1,5 3 — war rechnerisch ein
   Strichpunkt, aber diese Linien laufen seit 5.32 durch das ganze Bauteil:
   ueber dreihundert Pixel wiederholte sich das Muster vierzigmal, und ein
   Punkt von 1,5 px neben einem Strich von 7 px ist dabei nur ein kuerzerer
   Strich. Aus zwei Metern Abstand ist das eine Strichlinie. Mit 20 zu 2 ist
   der Unterschied zehnfach und damit zu sehen. Kraeftiger und deckender ist
   sie dazu — bei 0,6 Deckung ging der Punkt im Plan darunter unter. */
.ap-planbox line.ap-hilfslinie {
  stroke: var(--coral-500); stroke-width: 1; opacity: 0.75;
  stroke-dasharray: 20 5 2 5;
}
/* Der Aussenrand um jede Zahl ist **wieder da** (Bens Vorgabe 2026-08-29) —
   auf einem Planblatt voller feiner Linien und Schraffuren ist eine Ziffer
   ohne ihn schwer zu lesen.

   Seine Farbe ist die des **Themas** (`--bg-page`): hell im hellen, dunkel
   im dunklen (Bens Vorgabe 2026-08-29). Das Planbild kommt aus einer
   Revit-Ansichtsvorlage und ist im Standard dunkel gehalten (5.28) — ein
   fest weisser Rand sass darauf als heller Fleck um jede Ziffer. Es ist
   dieselbe Regel wie ueber dem freigestellten Bild (`.ap-massbox text`,
   5.6): der Rand traegt die Farbe des Grundes, auf dem die Zahl liegt.

   `paint-order: stroke` legt den Rand unter die Fuellung — sonst frisst er
   die Haelfte jeder Ziffer. */
.ap-planbox text {
  font-family: var(--font-mono); font-size: 14px;
  font-variant-numeric: tabular-nums;
  fill: var(--coral-500); paint-order: stroke;
  stroke: var(--bg-page); stroke-width: 3px; stroke-linejoin: round;
}
/* Die innere Kette traegt die Teilmasse; sie soll das Gesamtmass nicht
   uebertoenen — dieselbe Farbe, eine Spur kleiner und leichter. */
.ap-planbox text.ap-mass-teil {
  font-size: 12px; opacity: 0.85;
}
/* Die Flaechenzahlen stehen **in** der Zeichnung, nicht neben ihr: dort ist
   der Untergrund am unruhigsten. Ihren Rand haben sie aus der Regel oben —
   seit 2026-08-29 traegt ihn jede Zahl im Blatt, und zwar denselben. Hier
   bleibt allein, was sie von den Massen unterscheidet: eine Spur kleiner. */
.ap-planbox text.ap-flaechenzahl {
  font-size: 12px; fill: var(--coral-500);
}
/* Die tiefgestellte Nummer: kleiner, aber nicht so klein, dass sie im Plan
   verschwindet. */
.ap-planbox text.ap-flaechenzahl .ap-index { font-size: 0.75em; }

/* Ein Sitz je Bauteil: das Kaestchen und die Liste seiner Aufdopplungen
   (Konzept 5.13). Positioniert wird der Sitz, nicht mehr das Kaestchen —
   die Liste haengt daran und darf seine Groesse nicht veraendern. */
/* **Ohne** `transform: translate(-50%, -50%)`, obwohl das Kaestchen mittig
   auf seinem Punkt sitzt: ein transformierter Vorfahre wird zum Bezug jedes
   `position: fixed` darin — die zweite Ebene sass dann nicht am Fenster,
   sondern am Tag und landete irgendwo (Konzept 5.14). Die halbe Breite und
   Hoehe zieht deshalb app.js beim Setzen von left/top selbst ab. */
.ap-tagsitz { position: absolute; }
.ap-tagsitz:hover, .ap-tagsitz.ist-offen { z-index: 6; }

.ap-tag {
  cursor: pointer; white-space: nowrap; display: inline-flex; gap: 5px;
  align-items: center;
  font: var(--type-data); font-size: 11px; letter-spacing: 0.04em;
  padding: 3px 7px; border-radius: var(--radius-1);
  background: var(--surface-card); border: 1px dashed var(--line-border);
  color: var(--text-muted);
}
.ap-tag:hover { color: var(--text-body); border-color: var(--mint-500); }
.ap-tag.ist-gewaehlt {
  background: var(--status-warn-soft); border-style: solid; border-color: var(--coral-500);
  color: var(--coral-400); box-shadow: 0 0 0 3px var(--status-warn-soft);
}
.ap-tag.ohne-lage { display: none; }

/* Verschiebemodus (Schloss offen, Bens Vorgabe 2026-08-17).
   Der Tag ist dann ein Griff: Zeiger als Kreuz, kein Markieren beim Ziehen,
   und die Aufdopplungsliste bleibt zu — sie klappte sonst unter der Maus auf
   und deckte gerade das zu, wohin man den Kopf setzen will. */
.ap-planbuehne.ap-verschiebemodus { user-select: none; -webkit-user-select: none; }
.ap-planbuehne.ap-verschiebemodus .ap-tag { cursor: move; }
.ap-planbuehne.ap-verschiebemodus .ap-klonliste { display: none !important; }
.ap-tagsitz.wird-gezogen { z-index: 9; }
.ap-tagsitz.wird-gezogen .ap-tag {
  border-style: solid; border-color: var(--mint-500); color: var(--text-body);
  box-shadow: 0 0 0 3px var(--surface-hover);
}
/* Das Schloss selbst: offen heisst an, und das muss man sehen, ohne den
   Buegel zu vergleichen. */
.ap-knopf.ap-flaeche-knopf,
.ap-knopf.ap-schloss, .ap-knopf.ap-auge {
  padding: 4px 8px; display: inline-flex; align-items: center;
}
/* Beide Schalter zeigen ihren Zustand gleich an — sie stehen nebeneinander
   und sind beide Schalter fuer die Arbeit im Blatt (5.30). */
.ap-knopf.ap-flaeche-knopf.ist-an, .ap-knopf.ap-ebenen.ist-an,
.ap-knopf.ap-schloss.ist-an, .ap-knopf.ap-auge.ist-an {
  color: var(--mint-500); border-color: var(--mint-500);
  background: var(--surface-hover);
}
/* Der Vollbildknopf an der rechten unteren Ecke des Blatts (Bens Vorgabe
   2026-08-29). Er liegt ueber dem Plan und muss deshalb einen eigenen Grund
   haben — auf einer weissen Planstelle waere er sonst unsichtbar. */
.ap-knopf.ap-vollbild {
  position: absolute; right: var(--sp-3); bottom: var(--sp-3); z-index: 6;
  padding: 5px 7px; display: inline-flex; align-items: center;
  background: var(--surface-card); border: 1px solid var(--line-border);
  border-radius: var(--radius-control, 6px); opacity: 0.9;
}
.ap-knopf.ap-vollbild:hover { opacity: 1; background: var(--surface-hover); }
.ap-knopf.ap-vollbild.ist-an { color: var(--mint-500); border-color: var(--mint-500); }

/* Im Vollbild steht **nur das Blatt** da: Kopfzeile, Seitenleiste, Tabelle,
   Bildspalte und Fusszeilen sind fort, die Plankarte bekommt den ganzen
   Schirm. Das Vollbild haengt an `#ap-app` und nicht an der Karte — die gibt
   es nach dem naechsten Klick nicht mehr (siehe app.js, vollbildKnopf). */
.ap-app.ist-vollbild { background: var(--bg-page); }
.ap-app.ist-vollbild > .ap-top,
.ap-app.ist-vollbild .ap-nav,
.ap-app.ist-vollbild .ap-krume,
.ap-app.ist-vollbild .ap-positionen,
.ap-app.ist-vollbild .ap-bildspalte,
.ap-app.ist-vollbild .ap-band,
.ap-app.ist-vollbild .ap-fusszeile { display: none; }
/* Das Blatt fuellt den Schirm — und bleibt **darin**. Ohne die Reihe
   `min-height: 0` waechst die Planflaeche auf die natuerliche Hoehe des
   Bildes; dann scrollt der Inhalt statt des Blatts, und der Vollbildknopf an
   seiner unteren Ecke steht unterhalb des Schirms (gemessen 2026-08-29:
   1096 px Blatt in einem 950 px hohen Fenster). */
.ap-app.ist-vollbild .ap-inhalt { padding: var(--sp-3); overflow: hidden; }
.ap-app.ist-vollbild .ap-plangitter {
  grid-template-columns: 1fr; flex: 1 1 auto; min-height: 0;
}
.ap-app.ist-vollbild .ap-plangitter > .ap-karte,
.ap-app.ist-vollbild .ap-planrahmen { min-height: 0; }
/* Im Vollbild gibt der Rahmen die Hoehe vor, nicht 66vh: `height: auto`
   ueberlaesst sie dem Flexlauf, `min-height: 0` erlaubt ihm zu stauchen. */
.ap-app.ist-vollbild .ap-planflaeche { height: auto; min-height: 0; }

/* **Das andere Vollbild** (5.36): dieselbe Mechanik, nur bleibt die Spalte
   mit der Zeichnung stehen und das Planblatt geht. Welches von beidem gilt,
   sagt `ist-zeichnung` am selben Knoten (app.js, `vollbildZiel`) — es gibt nur
   ein Vollbild, aber zwei Dinge, die man gross sehen will. */
.ap-app.ist-vollbild.ist-zeichnung .ap-bildspalte {
  display: block; min-height: 0;
}
.ap-app.ist-vollbild.ist-zeichnung .ap-plangitter > .ap-karte { display: none; }
/* Die Herkunftskette geht hier mit — anders als im Vollbild des Planblatts,
   wo sie stehen bleibt. Sie kostet gut ein Fuenftel der Hoehe, und Kennzeichen
   wie Bezeichnung stehen im Kopf des Rahmens ohnehin. Wer die Kette sucht,
   verlaesst das Vollbild; wer es aufmacht, will die Zeichnung. */
.ap-app.ist-vollbild.ist-zeichnung .ap-kettenkarte { display: none; }
/* Ohne Planblatt gibt es keine Blatthoehe mehr, an der der Stapel haengt —
   er fuellt den Schirm selbst. */
.ap-app.ist-vollbild.ist-zeichnung .ap-bildstapel { position: absolute; inset: 0; }

/* Die Zahl der weiteren Zeilen an diesem Bauteil. Sie steht am Kaestchen,
   damit man den Turm nicht vermisst, den es ersetzt. */
.ap-tagzahl {
  font-size: 10px; padding: 0 4px; border-radius: 6px;
  background: var(--surface-inset); border: 1px solid var(--line-hair);
  color: var(--text-subtle);
}
.ap-tag.ist-gewaehlt .ap-tagzahl, .ap-tag.ist-offen .ap-tagzahl {
  color: var(--coral-400); border-color: var(--coral-500);
}

/* Die Liste der Aufdopplungen. Sie klappt beim Ueberfahren auf und bleibt
   offen, solange eine ihrer Zeilen unten in der Tabelle gewaehlt ist. */
/* Zehn Zeilen stehen gleichzeitig da, dann erst scrollt die Liste (Bens
   Vorgabe 2026-08-16). Die Hoehe rechnet `klonhoeheSetzen()` an einer
   gemessenen Zeile aus und setzt sie als `height` — sie ist ein Fenster, auch
   wenn der Inhalt es nicht fuellt. Hier steht nur der Notwert, falls nicht
   gemessen werden konnte. */
.ap-klonliste {
  display: none; position: absolute; left: 100%; top: -4px; margin-left: 8px;
  /* Die Breite folgt dem Inhalt: Kopfzeile und erste Zeile jedes Eintrags
     brechen nicht um (Bens Vorgabe 2026-08-16). Erst die Bezeichnung darunter
     darf umbrechen — bei rund 50 Zeichen, was die Obergrenze setzt. */
  /* Die Obergrenze waechst genau um das, was die LV-Spalte gegenueber ihren
     neun Standardzeichen dazubekommt (`--ap-lv-spalte`): ein Projekt mit
     kurzen Nummern behaelt seine Liste, eines mit langen schneidet die
     Nummer nicht ab. Die Bezeichnung darunter bricht dadurch weiter bei
     rund 50 Zeichen um. */
  width: max-content; min-width: 230px;
  max-width: calc(52ch + var(--ap-lv-spalte, 9ch) - 9ch);
  max-height: 420px; overflow-y: scroll; overflow-x: hidden;
  /* `scroll` statt `auto`: der Platz der Rollleiste bleibt immer reserviert.
     Sichtbar ist sie nur beim Scrollen (siehe unten) — waere der Platz nicht
     reserviert, ruckte die Liste bei jedem Ein- und Ausblenden. */
  /* Am Ende der Liste soll die Seite hinter ihr nicht weiterscrollen. */
  overscroll-behavior: contain;
  /* Oben **kein** Polster — das traegt der klebende Kopf (s. .ap-klonkopf). */
  padding: 0 var(--sp-2) var(--sp-2); border-radius: var(--radius-2);
  background: var(--surface-card); border: 1px solid var(--line-border);
  box-shadow: var(--shadow-2, 0 8px 24px rgba(0, 0, 0, 0.28));
}
/* Die Seite, auf der die Liste aufklappt. Die Marke sitzt an der **Liste**
   (nicht am Tag): so tragen beide Ebenen dieselbe, und der Seitenknopf im Kopf
   setzt sie von Hand (Bens Vorgabe 2026-08-16). Bei der zweiten Ebene liest
   app.js sie nur — dort ist die Lage gerechnet, nicht gestylt. */
.ap-klonliste.nach-links {
  left: auto; right: 100%; margin-left: 0; margin-right: 8px;
}
/* Nur die **erste** Ebene haengt am Tag. Die zweite haengt seit Konzept 5.16
   ebenfalls am Tagsitz (damit sie im Planblatt steht) — sie ist deshalb hier
   ausdruecklich ausgenommen, sonst klappte beim Ueberfahren des Tags alles
   auf einmal auf. */
.ap-tagsitz:hover > .ap-klonliste:not(.ap-ebene2),
.ap-tagsitz.ist-offen > .ap-klonliste:not(.ap-ebene2) { display: block; }

/* Zweite Ebene (Konzept 5.14/5.16): die Aufdopplungen eines gehosteten
   Bauteils. Sie steht **im Planblatt**, absolut im Tagsitz — so nimmt der Plan
   sie beim Scrollen von selbst mit und der Ueberlauf des Blatts schneidet sie
   am Rand ab. Am Fenster (fixed) lief sie dem Plan sichtbar hinterher (Bens
   Befund 2026-08-16). Ihre Lage rechnet ebene2Setzen; im Rahmen ihrer Zeile
   laege sie hinter dem Ueberlauf der ersten Ebene und waere abgeschnitten,
   deshalb haengt sie nicht dort. */
.ap-klonsitz { position: relative; display: block; }
.ap-klonliste.ap-ebene2 {
  position: absolute; left: 0; top: 0; right: auto; margin: 0; z-index: 40;
}
/* Aufgeklappt wird sie vom Skript (`ist-auf`): ueber die Baumgrenze hinweg
   greift kein `:hover` ihrer Zeile. */
.ap-klonliste.ap-ebene2.ist-auf { display: block; }
/* Die Seitenmarke ist bei der zweiten Ebene **nur eine Auskunft** fuer
   app.js — ihre Lage rechnet ebene2Setzen. Ohne diese Zeile griffe das
   `right: 100%` von oben und die Liste wurde schmal gequetscht, ihr
   Seitenknopf verschwand (Bens Befund 2026-08-16). */
.ap-klonliste.ap-ebene2.nach-links { right: auto; margin: 0; }
/* Die Liste in der Ecke (Konzept 5.47, dritter Modus): dieselbe flache
   Liste, aber sie klebt **in einer Ecke des Blatts** statt am Tag. Sie haengt
   am Planrahmen (wie der Vollbildknopf rechts unten) und steht deshalb still,
   waehrend das Blatt scrollt. Gezeigt wird sie vom Skript (`ist-auf`); der
   Seitenknopf hat in der Ecke nichts zu schalten. Die Regel steht **nach**
   `.nach-links`, damit sie deren `right: 100%` sicher ueberstimmt.

   Welche Ecke, sagt die zweite Klasse (`ap-ecke-lu` … `ap-ecke-ru`, Bens
   Vorgabe 2026-09-05): der Knopf wandert im Uhrzeigersinn von unten links
   ueber oben links und oben rechts nach unten rechts. Grundlage ist unten
   links; die anderen drei setzen je zwei Seiten um. */
.ap-klonliste.ap-ecke {
  position: absolute; left: var(--sp-3); bottom: var(--sp-3);
  top: auto; right: auto; margin: 0; z-index: 7;
}
.ap-klonliste.ap-ecke.ap-ecke-lo { top: var(--sp-3); bottom: auto; }
.ap-klonliste.ap-ecke.ap-ecke-ro { top: var(--sp-3); bottom: auto; right: var(--sp-3); left: auto; }
.ap-klonliste.ap-ecke.ap-ecke-ru { right: var(--sp-3); left: auto; }
/* Modus `aus`: keine Liste — der Knopf leuchtet **rot** statt mint (Bens
   Vorgabe 2026-09-05), damit eine fehlende Liste auffaellt. Steht nach der
   `ist-an`-Regel und ueberstimmt sie. */
.ap-knopf.ap-ebenen.ist-an.ist-aus {
  color: var(--coral-400); border-color: var(--coral-500);
}
.ap-klonliste.ap-ecke.ist-auf { display: block; }
.ap-klonliste.ap-ecke .ap-seitenknopf { display: none; }
/* Das „+n" einer Listenzeile sitzt in seiner eigenen Spalte ganz rechts und
   sagt: hier geht es weiter. Die Spalte steht auch leer da (s. app.js). */
.ap-klonzahl {
  grid-column: 4; grid-row: 1 / span 2; align-self: center;
  justify-self: end; white-space: nowrap;
}
/* Kopf der Liste: links, was sie fuehrt — rechts der Seitenknopf. Er klebt
   oben, damit er auch bei gescrollter Liste erreichbar bleibt.

   Das Polster der Liste sitzt **oben nicht** (siehe .ap-klonliste): stuende es
   dort, scrollten die Zeilen durch diese acht Pixel ueber dem klebenden Kopf
   hindurch und blieben als Spalt sichtbar (Bens Befund 2026-08-16). Der Kopf
   traegt es deshalb selbst und deckt damit alles ab. */
.ap-klonkopf {
  position: sticky; top: 0; z-index: 1; background: var(--surface-card);
  display: flex; align-items: center; gap: var(--sp-3); white-space: nowrap;
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
  padding: var(--sp-2) 0;
}
.ap-seitenknopf {
  margin-left: auto; cursor: pointer; line-height: 1;
  padding: 2px 6px; border-radius: var(--radius-1);
  background: var(--surface-inset); border: 1px solid var(--line-hair);
  color: var(--text-subtle); font-size: 10px;
}
.ap-seitenknopf:hover { color: var(--text-body); border-color: var(--mint-500); }
/* Vier Spalten: Kennzeichen, LV, Menge — und ganz rechts das „+n", das ueber
   beide Zeilen der Zelle steht (Konzept 5.14).

   Die Breiten stehen **fest** (in Zeichen der Mono-Schrift), nicht auf `auto`:
   jede Zeile ist ihr eigenes Raster, und mit `auto` richtete sich jede nach
   ihrem eigenen Inhalt — die Mengen staffelten sich treppenartig gegeneinander
   statt untereinander zu stehen (Bens Befund 2026-08-16). */
.ap-klonzeile {
  display: grid;
  /* Die LV-Spalte misst app.js je Liste an der laengsten Nummer darin
     (`lvBreiteSetzen`, 9 bis 16 Zeichen): `3.13.19.111` stand sonst als
     `3.13.19.1…` da (Bens Befund 2026-09-03 an der Karte zu 00hUZ24). Fest
     ist sie trotzdem -- innerhalb einer Liste haben alle Zeilen dieselbe. */
  grid-template-columns: 10ch var(--ap-lv-spalte, 9ch) 1fr 4ch;
  gap: 2px var(--sp-3);
  width: 100%; text-align: left; cursor: pointer;
  padding: 4px var(--sp-2); border: 0; border-radius: var(--radius-1);
  background: transparent; color: var(--text-body);
  font: var(--type-data); font-size: 11px;
}
.ap-klonzeile:hover { background: var(--surface-inset); }
.ap-klonzeile.ist-gewaehlt {
  background: var(--status-warn-soft); color: var(--coral-400);
}
/* Erste Zeile eines Eintrags: Kennzeichen, LV-Nummer und Menge stehen
   **nebeneinander** und brechen nicht um (Bens Vorgabe 2026-08-16). Laenger
   als ihre Spalte werden Kennzeichen und LV-Nummer abgeschnitten statt die
   Flucht der Mengen zu verschieben. */
.ap-klonkz, .ap-klonlv, .ap-klonmenge { white-space: nowrap; }
.ap-klonkz, .ap-klonlv { overflow: hidden; text-overflow: ellipsis; }
.ap-klonkz { font-weight: 700; }
.ap-klonlv { color: var(--text-muted); }
.ap-klonmenge { text-align: right; }
/* Die Herkunft der Zeile — Grundmenge, Aufdopplung n, Zulage — steht klein
   unter dem Kennzeichen und laeuft ueber alle drei Spalten. */
.ap-klonart { grid-column: 1 / 4; color: var(--text-subtle); font-size: 10px; }
/* Freiraum unter der letzten Zeile, damit auch sie ganz nach oben gescrollt
   werden kann — dieselbe Regel wie unter der Positionstabelle. Die Hoehe setzt
   `klonNachlaufSetzen()`, sie kennt erst das gerenderte Blatt. */
.ap-klonnachlauf { height: 0; }

/* Die Rollleiste der Liste zeigt sich nur, solange gescrollt wird, und
   verschwindet drei Sekunden nach der letzten Bewegung wieder (Bens Vorgabe
   2026-08-16). Die Klasse setzt `rollleisteZeigen()` in app.js. Ausgeblendet
   heisst durchsichtig, nicht weg: die Breite bleibt stehen (overflow: scroll),
   damit die Zeilen nicht springen. */
.ap-klonliste {
  scrollbar-width: thin; scrollbar-color: transparent transparent;
  transition: scrollbar-color var(--dur-base) var(--ease-standard);
}
.ap-klonliste.ist-am-scrollen { scrollbar-color: var(--line-border) transparent; }
.ap-klonliste::-webkit-scrollbar { width: 8px; }
.ap-klonliste::-webkit-scrollbar-track { background: transparent; }
.ap-klonliste::-webkit-scrollbar-thumb {
  background: transparent; border-radius: 4px;
  transition: background var(--dur-base) var(--ease-standard);
}
.ap-klonliste.ist-am-scrollen::-webkit-scrollbar-thumb { background: var(--line-border); }
.ap-planfuss {
  display: flex; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-3) var(--gutter-card) 0;
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
}

/* ------------------------------------------------------------ Bildkarten */
.ap-bildspalte { position: relative; min-width: 0; min-height: 0; }
.ap-bildstapel {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.ap-bildkarte {
  flex: 1 1 0; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
}
.ap-bildkarte .ap-bildflaeche {
  flex: 1 1 auto; min-height: 0;
  background: var(--surface-inset); padding: var(--sp-3); display: flex;
  align-items: center; justify-content: center;
}
/* Das Bild skaliert sich in die Karte hinein, statt sie aufzublaehen. */
.ap-bildkarte .ap-bildflaeche img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
/* Bild und Massbox in einem Feld: der Rahmen ist quadratisch wie das
   exportierte PNG und damit das groesste Quadrat, das in die Bildflaeche
   passt — genau der Ausschnitt, den object-fit: contain sonst allein ergaebe.
   Nur so deckt sich das SVG-Feld mit dem Bild. */
.ap-bildkarte .ap-bildrahmen {
  position: relative; aspect-ratio: 1;
  max-width: 100%; max-height: 100%; min-height: 0;
}
.ap-bildkarte .ap-massbox {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
/* Koralle wie die Markierung im Pruefbogen 05_03: das Bauteil ist mint, die
   Bemassung liegt als Akzent darueber. */
.ap-massbox line {
  stroke: var(--coral-500); stroke-width: 3; fill: none; stroke-linecap: round;
}
.ap-massbox line.ap-verdeckt { stroke-dasharray: 12 10; opacity: 0.55; }
/* Durchbruch: das Bauteil selbst hat keine Fläche zum Einfärben, also
   markiert die Box den Ausschnitt — gefüllt, damit sie im Drahtmodell nicht
   als weitere Kantenschar untergeht (Konzept 5.7). */
.ap-massbox .ap-flaeche { fill: var(--accent-soft); stroke: none; }
.ap-massbox.ap-durchbruch line { stroke-width: 4; }
/* 34 px sind die Groesse in der Bilderansicht, wo die Karte klein ist. In der
   Zeichnungsansicht (5.36) ist das Feld ein Viertel des Gitters — dort setzt
   `isoliertfeld()` die Variable so, dass die Zahlen in **Pixeln** so gross
   stehen wie die der Zeichnung daneben. */
.ap-massbox text {
  font-family: var(--font-mono); font-size: var(--massbox-schrift, 34px);
  font-variant-numeric: tabular-nums;
  fill: var(--coral-400); paint-order: stroke;
  stroke: var(--bg-page); stroke-width: 6px; stroke-linejoin: round;
}
.ap-bildkarte .ap-fehlt {
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
  text-align: center; padding: var(--sp-8) var(--sp-4);
  border: 1px dashed var(--line-border); border-radius: var(--radius-card); width: 100%;
}
/* Schmales Fenster und Druck: untereinander, dann gibt es keine Planhoehe,
   an die sich die Bilder halten koennten. */
@media (max-width: 1100px) {
  .ap-bildstapel { position: static; }
  .ap-bildkarte { flex: 0 0 auto; }
  .ap-bildkarte .ap-bildflaeche { min-height: 190px; }
  /* Die Deckelung sitzt am Rahmen, nicht am Bild: die Massbox muss dieselbe
     Hoehe bekommen, sonst laeuft sie ueber das geschrumpfte Bild hinaus. */
  .ap-bildkarte .ap-bildrahmen { max-height: 42vh; }
  .ap-bildkarte .ap-bildflaeche img { max-height: 42vh; }
}

/* ------------------------------------------- Rahmen und Schalter (5.36)

   Kontext und Isoliert waren zwei lose Karten im Stapel. Sie stehen jetzt in
   **einem** Rahmen, und der traegt oben rechts den Schalter, mit dem die
   Spalte zwischen Bildern und Zeichnung wechselt (Bens Vorgabe 2026-08-30).
   Ein Schalter an einer der beiden Karten haette so ausgesehen, als schalte
   er nur diese. */
/* `position: relative` traegt den Vollbildknopf in der unteren rechten Ecke
   (Bens Vorgabe 2026-08-30) — er haengt an der Karte und nicht am Koerper,
   der beim Zoomen scrollt. */
.ap-bildgruppe { flex: 1 1 0; min-height: 0; overflow: hidden; position: relative; }
/* `auto` statt `hidden`: gezoomt wird die Groesse des Gitters (5.36), und was
   dann nicht mehr hineinpasst, muss erreichbar bleiben. */
.ap-bildgruppe > .ap-karte-leib {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  display: flex; flex-direction: column; gap: var(--sp-4);
}
/* Der Kopf des Rahmens ist schmaler als der einer Inhaltskarte: er nimmt
   Hoehe weg, die der Zeichnung fehlt. */
.ap-bildgruppe > .ap-karte-kopf { padding: var(--sp-3) var(--sp-4); }
/* „Risszeichnung" steht so gross da wie „Aufmassplan 00a" nebenan (Bens
   Vorgabe 2026-08-31) — beides sind Ueberschriften ihrer Karte, und die eine
   kleiner zu setzen machte sie zur Beschriftung der anderen. Die Groesse
   kommt deshalb aus `.ap-karte-kopf h3`; hier steht nur noch, dass sie nicht
   umbricht. */
.ap-bildgruppe > .ap-karte-kopf h3 { white-space: nowrap; }
/* Die erste Zeile traegt Bezeichnung, Kennzeichen und Element-Id (Bens
   Vorgabe 2026-08-31). Sie ist die einzige, die lang werden kann — sie
   bekommt deshalb die Kuerzung, nicht die Ueberschrift darunter. */
.ap-bildgruppe > .ap-karte-kopf .ap-tick {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-transform: none; letter-spacing: normal;
}
.ap-bildgruppe > .ap-karte-kopf .ap-titelblock { flex: 1 1 auto; min-width: 0; }
/* Die Wertzeilen stehen **neben** dem Koerper, nicht darin (Bens Vorgabe
   2026-08-31): der scrollt, seit es den Zoom gibt, und eine Zeile darin
   wanderte mit der Zeichnung aus dem Bild. Als Geschwister bleiben sie unter
   dem Rahmen stehen.

   `flex-shrink: 0`, damit sie nicht zusammengedrueckt werden, wenn die
   Zeichnung den Platz beansprucht — es ist derselbe Grund wie beim Gitter.
   Rechts bleibt Platz fuer den Vollbildknopf, der in derselben Ecke sitzt. */
.ap-bildgruppe > .ap-werte {
  flex-shrink: 0; padding-right: 44px;
}
/* Zwei Knoepfe in einer Fassung: der gewaehlte steht erhoeht, der andere
   liegt still daneben — dieselbe Sprache wie das Auge und das Schloss am
   Planblatt (`.ap-knopf.ap-auge.ist-an`). */
.ap-schalter {
  display: flex; gap: 2px; padding: 2px;
  border: 1px solid var(--line-border); border-radius: var(--radius-card);
  background: var(--surface-inset);
}
.ap-schalter .ap-knopf {
  padding: 3px 10px; font-size: var(--fs-caption); border-radius: 5px;
}
.ap-schalter .ap-knopf.ist-an {
  background: var(--surface-card); color: var(--text-accent);
  border-color: var(--line-border);
}
/* Der Vollbildknopf sitzt in der unteren rechten Ecke der Karte — dieselbe
   Stelle wie am Planblatt (5.33). Er ist ein direktes Kind der Karte, damit
   die absolute Lage an ihr haengt und nicht am scrollenden Koerper. */
.ap-bildgruppe > .ap-knopf.ap-vollbild { padding: 4px 6px; }

/* ------------------------------------------------- Die Zeichnung (5.36)

   Vier Felder, deren Spalten und Zeilen in den Verhaeltnissen der Rahmen
   stehen, die Python liefert (`risse.raster`) — das `aspect-ratio` ueber dem
   Gitter haelt den Massstab in allen vieren gleich. **Hier wird nichts
   ausgerichtet**: das Fluchten faellt aus dem Raster heraus. */
/* Breite und Hoehe setzt `gitterGroesse()` in app.js, nicht das CSS: `width:
   100%` mit `aspect-ratio` und `max-height` verwirft in einem flachen Fenster
   still das Seitenverhaeltnis und zieht die Zeichnung in die Breite (5.36,
   Bens Befund 2026-08-30). Was hier steht, gilt nur bis zur ersten Messung —
   und `max-*` bleibt als Fangnetz, falls sie ausfaellt. */
.ap-risse {
  display: grid; gap: 0;
  position: relative;
  width: 100%; max-width: 100%; max-height: 100%;
  margin: 0 auto; min-height: 0;
  /* `flex-shrink: 0` ist hier **Pflicht**, nicht Feinschliff (Bens Befund
     2026-08-31: „der Zoom funktioniert bis zu einem bestimmten Zoomlevel").
     Der Koerper ist ein Spaltenlayout, seine Hauptachse also die senkrechte —
     und ein Flex-Kind schrumpft darin von sich aus zurueck, sobald es nicht
     mehr hineinpasst. Gemessen: ab der vierten Zoomstufe forderte JS 696 px
     Hoehe, das Element blieb bei 636 px stehen, die Breite wuchs weiter. Das
     Seitenverhaeltnis lief damit auf 1,64 auseinander, und die Zeichnung stand
     in die Breite gezogen da. Gescrollt wird stattdessen (`overflow: auto` am
     Koerper). */
  flex-shrink: 0;
  /* `safe` ist hier das Wesentliche: gezoomt ist die Zeichnung breiter als
     ihr Behaelter, und ein zentriertes Flex-Kind ragt dann auf **beiden**
     Seiten heraus — die linke Haelfte waere unerreichbar, weil dorthin nicht
     gescrollt werden kann. `safe center` faellt in dem Fall auf den Anfang
     zurueck. */
  place-self: safe center;
}
/* Die Verlaengerungslinien liegen **ueber** dem Gitter und in keinem Feld:
   ein Feld schneidet an seiner Kante ab (`overflow: hidden`), und genau
   darueber hinaus sollen sie (5.36). Absolut statt als Gitterkind — ein
   fuenftes Kind bekaeme sonst eine eigene Zelle. */
.ap-risse .ap-fluchtlinien {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
.ap-risse .ap-fluchtlinien line.ap-hilfslinie {
  stroke: var(--coral-500); stroke-width: 1; opacity: 0.7;
  stroke-dasharray: 20 5 2 5;
}
.ap-risse .ap-rissfeld {
  position: relative; min-width: 0; min-height: 0; overflow: hidden;
  /* Nur Haarlinien zwischen den Feldern: ein kraeftiger Rahmen zerschnitte
     genau die Fluchtlinien, die man verfolgen soll.

     **Als Innenschatten, nicht als `border`.** Ein Rahmen zaehlt bei
     `box-sizing: border-box` vom Inhalt ab: das SVG stand damit 2 px
     schmaler und 2 px niedriger als seine viewBox. Weil die Felder ungleich
     gross sind, war der Massstab in jedem ein anderer (gemessen 0,991 gegen
     0,989) — und in x ein anderer als in y. Eine Ecke lag in zwei Rissen
     damit nicht mehr auf derselben Bildlinie, und die Fluchtlinien, die
     ueber dem ganzen Gitter liegen und keinen Rahmen kennen, trafen sie
     ebenfalls knapp daneben. Der Schatten kostet keinen Platz; seitdem ist
     eine Nutzereinheit wirklich ein Pixel (app.js, `risseFuellen`). */
  box-shadow: inset 0 0 0 1px var(--line-hair);
}
/* `position: relative` ist hier **Pflicht**, nicht Kosmetik: der
   Planhintergrund darunter ist absolut positioniert, und ein positioniertes
   Element malt ueber nicht-positionierten Inhalt — das SVG lag damit hinter
   dem Plan. Solange der zu 35 % durchsichtig war, sah man es nicht; ohne die
   Deckelung (Bens Vorgabe 2026-08-30) war die Draufsicht schlagartig leer. */
.ap-risse .ap-riss {
  display: block; position: relative; z-index: 1;
  width: 100%; height: 100%; overflow: visible;
}
/* Der Planausschnitt hinter der Draufsicht. **Ohne eigene Ausgrauung** (Bens
   Vorgabe 2026-08-30): das Planbild kommt aus einer Revit-Ansichtsvorlage und
   ist bereits zurueckgenommen (5.28). Ein Filter darueber nahm ihm den Rest —
   uebrig blieb ein grauer Schleier, in dem keine Wand mehr zu erkennen war,
   und genau die soll man sehen. */
.ap-risse .ap-rissplan {
  position: absolute; inset: 0; z-index: 0; overflow: hidden;
  pointer-events: none;
}
/* Der Ausschnitt entsteht durch Groesse und Lage des Bildes im Rahmen, nicht
   ueber `background-position` — dessen Prozentwerte haengen an
   `Behaelter − Bild` und kippen, sobald das Bild kleiner wird als das Feld
   (app.js, `planhintergrund`). `max-width` muss weg: die Regel aus base.css
   deckelt sonst jedes Bild auf die Behaelterbreite, und genau die soll es
   hier ueberschreiten duerfen. */
.ap-risse .ap-rissplan img {
  position: absolute; max-width: none; max-height: none; display: block;
}
.ap-risse .ap-rissbild { display: flex; align-items: center; justify-content: center; }
.ap-risse .ap-rissbild img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.ap-risse .ap-rissbild .ap-massbox {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
/* Der Name des Feldes sitzt in der **oberen linken** Ecke. Unten lag er im
   ersten Versuch mitten in der x-Maßkette — die läuft unter dem Bauteil quer
   durchs Feld. Oben links ist in allen vier Feldern Platz: die Höhenkette
   liegt links **neben** dem Bauteil, nicht in der Ecke.

   Kleiner als der Rest und ohne eigenen Rand: er benennt das Feld, er ist
   keine Angabe. */
.ap-risse .ap-rissname {
  position: absolute; left: 5px; top: 3px; z-index: 2;
  font: var(--type-tick); font-size: 9px;
  letter-spacing: var(--ls-tick); text-transform: uppercase;
  color: var(--text-subtle); opacity: 0.8; pointer-events: none;
}

/* Der Schalter Freigestellt | Kontext im vierten Feld (Bens Vorgabe
   2026-09-05): oben rechts, gebaut wie „Zeichnung | Bilder", nur kleiner —
   er sitzt in einem Feld, nicht in einem Kartenkopf. Ueber dem Bild und der
   Massbox (`z-index`), damit er klickbar bleibt. */
.ap-risse .ap-rissbild .ap-freischalter {
  position: absolute; right: 5px; top: 3px; z-index: 3;
}
.ap-risse .ap-rissbild .ap-freischalter .ap-knopf {
  padding: 1px 7px; font-size: 9px; letter-spacing: var(--ls-tick);
  text-transform: uppercase;
}

/* Das Zeichenwerk der Umrandung — **eine** Regel fuer beide Orte.

   `.ap-planbox` entsteht an genau einer Stelle (app.js, `umrissZeichnen`) und
   ist im Planblatt dieselbe Gruppe wie in einem Riss der Zeichnungsansicht
   (5.36); der Vorfahre im Selektor war nie noetig. Seit die Risse in Pixeln
   rechnen (`risseFuellen`), stimmen auch die Zahlen ueberein — Strichstaerken,
   Schriftgroessen und die Farben der Teilflaechen gelten damit an beiden
   Orten aus derselben Zeile. Eine zweite Staffel waere ein zweiter Ort, an dem
   dieselbe Entscheidung steht, und der eine wuerde beim naechsten Mal
   vergessen (Bens Vorgabe 2026-08-30: „auch die Logik dahinter"). */

/* Die Ebenen hinter den beiden Rissen mit z-Komponente (Bens Vorgabe
   2026-08-30). Sie sind **Bezug, nicht Zeichnung**: gedaempft, gestrichelt
   und in der Textfarbe statt in der Akzentfarbe — sonst laese man sie als
   Kante des Bauteils. */
.ap-riss line.ap-ebenenlinie {
  stroke: var(--text-subtle); stroke-width: 1; opacity: 0.6;
  stroke-dasharray: 14 6;
}
.ap-riss text.ap-ebenentext {
  font-family: var(--font-mono); font-size: 11px;
  font-variant-numeric: tabular-nums;
  fill: var(--text-subtle); paint-order: stroke;
  stroke: var(--bg-page); stroke-width: 3px; stroke-linejoin: round;
}

/* Schmales Fenster: der Stapel steht nicht mehr absolut, die Gruppe braucht
   dann eine eigene Hoehe — sonst faellt die Zeichnung auf null zusammen. */
@media (max-width: 1100px) {
  .ap-bildgruppe { flex: 0 0 auto; }
  .ap-risse { max-height: 60vh; }
}

.ap-werte { display: flex; flex-direction: column; gap: 4px; padding: var(--sp-3) var(--gutter-card) var(--sp-4); }
.ap-werte .ap-wertzeile {
  display: flex; justify-content: space-between; gap: var(--sp-3);
  border-top: 1px solid var(--line-hair); padding-top: 4px;
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
}
.ap-werte .ap-wertzeile span:last-child { color: var(--text-muted); }

/* ----------------------------------------------------------- Hinweisband */
.ap-band {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-card);
  border: 1px solid var(--status-review-soft); background: var(--status-review-soft);
  color: var(--status-review-fg); font-size: var(--fs-body-s);
}
.ap-band.ap-still { border-color: var(--line-border); background: var(--surface-card); color: var(--text-muted); }
.ap-band ul { margin: var(--sp-2) 0 0; padding-left: var(--sp-4); }
.ap-band li { margin-bottom: 3px; }

/* ------------------------------------------------------------- Leerstelle */
.ap-leer {
  border: 1px dashed var(--line-border); border-radius: var(--radius-card);
  padding: var(--sp-12) var(--sp-6); text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  color: var(--text-subtle);
}

.ap-fusszeile {
  margin-top: auto; padding-top: var(--sp-5);
  font: var(--type-data); font-size: var(--fs-caption); color: var(--text-subtle);
  display: flex; gap: var(--sp-4); flex-wrap: wrap;
}

@media print {
  .ap-nav, .ap-top, .ap-zoomleiste { display: none; }
  .ap-inhalt { overflow: visible; }
  .ap-planflaeche { height: auto; max-height: none; }
  .ap-bildstapel { position: static; }
  .ap-bildkarte { flex: 0 0 auto; }
  .ap-nachlauf { display: none; }
  /* Gedruckt wird die ganze Liste — die Zeilenwahl ist eine Sache des
     Bildschirms. !important, weil app.js die Hoehe am Element setzt. */
  .ap-tabellenrahmen { max-height: none !important; overflow: visible; }
  .ap-tabellenleiste { display: none; }
}

/* ------------------------------------------------------------- Anmeldung */
/* login.html — echte Server-Anmeldung (server.js), kein Client-Vergleich
   mehr. Dieselben Karten-/Knopf-Bausteine wie im Rest der Seite. */
.ap-gate {
  position: fixed; inset: 0; z-index: 999; display: flex;
  align-items: center; justify-content: center;
  background: var(--bg-page);
}
.ap-gate-karte {
  width: 320px; display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--gutter-card); background: var(--surface-card);
  border: 1px solid var(--line-border); border-radius: var(--radius-card);
  box-shadow: var(--shadow-raised);
}
.ap-gate-karte h1 { font: var(--type-h3); margin-top: var(--sp-2); }
.ap-gate-hinweis { font-size: var(--fs-body-s); color: var(--text-muted); }
.ap-gate-feld { display: flex; flex-direction: column; gap: var(--sp-1); }
.ap-gate-feld label {
  font: var(--type-tick); letter-spacing: var(--ls-tick); text-transform: uppercase;
  color: var(--text-subtle);
}
.ap-gate-eingabe {
  font-family: var(--font-mono); font-size: var(--fs-body-s); line-height: 1;
  padding: 9px var(--sp-3); width: 100%;
  color: var(--text-body); background: var(--surface-inset);
  border: 1px solid var(--line-border); border-radius: var(--radius-control);
}
.ap-gate-eingabe:focus { outline: none; border-color: var(--focus-ring); }
/* .ap-knopf setzt "all: unset" und wirft damit auch das globale
   box-sizing:border-box weg (zurueck auf content-box, den Startwert) — ohne
   diese Zeile waere der Knopf um sein Polster (2 x sp-4) breiter als die
   100%, also breiter als die Eingabefelder daneben. */
.ap-gate-karte .ap-knopf {
  box-sizing: border-box; width: 100%; text-align: center;
  padding: 9px var(--sp-4); margin-top: var(--sp-2);
}
.ap-gate-fehler { font-size: var(--fs-body-s); color: var(--status-warn); }
