/* TB Web – Kundenportal von TRIOBILD.
 *
 * Farben, Schrift und Bausteine stammen aus TB BauCam (Mac-App und Webfassung)
 * und der CityCam-Landingpage. Nichts davon ist neu erfunden.
 *
 * Die eine Regel, die über allem steht und aus dem Stylesheet von TB BauCam
 * übernommen ist: Gelb steht für Bedienung und Herkunft, nie für Zustände.
 * Ob eine Kamera erreichbar ist, bleibt grün bzw. rot – sonst verwechselt man
 * Markenfarbe mit einer Meldung.
 */

/* ------------------------------------------------------------ Farbwerte */

:root {
  /* Pantone 3955 · CMYK 15/0/100/0 · RGB 210/215/0 – der verbindliche Wert
   * aus Farbwerte_Logo_TRIOBILD.docx. */
  --gelb: #d2d700;
  /* Das Wort neben der Wortmarke - heller als das Marken-Gelb, auf
   * ausdruecklichen Wunsch vom 15.09.2026 („ich moechte Web in einem
   * helleren Gelb"), und in beiden Themen derselbe Ton („auch wenn es
   * auf hell gestellt ist"). Ein eigener Wert und nicht `--gelb`:
   * Logo, Knoepfe und Ampeln behalten den verbindlichen Pantone-Ton. */
  --gelb-marke: #ecf200;
  --auf-gelb: #0f1208;
  /* Bahn und Kopf des Ladekringels – siehe `.ladekreis`. */
  --kreisbahn: rgba(255, 255, 255, .16);
  --kreisfarbe: #d2d700;

  /* Auf hellem Grund ist reines Gelb als Schrift unlesbar. Dann dieser Ton. */
  --gelb-schrift: #d2d700;
  /* Wind im Wetterverlauf (09.09.2026 gewünscht: „mit einer leicht rosa
   * Farbe"). Rosa, weil es weder mit dem Marken-Gelb der Temperatur noch
   * mit dem Blau des Regens zu verwechseln ist und keine der Statusfarben
   * belegt. Auf dunklem Grund darf es hell und weich sein. */
  --wind: #f0a6c8;
  --orange: #d75a1e;          /* Wetterstation, wie auf livebild.triobild.de */

  --tinte: #080808;          /* das Schwarz aus den Markenvorlagen */
  /* Anthrazit statt Schwarz – Wunsch vom 03.10.2026: „das schwarz ist zu
   * dunkel", danach noch „ein kleines bisschen heller". Bildflächen (`--grund-tief`) bleiben schwarz; um ein Foto
   * herum soll nichts grau schimmern. */
  --grund: #222428;
  --grund-tief: #000;
  /* Flächen liegen jetzt näher am Grund. Vorher hoben sich die Karten
   * deutlich ab und zerlegten jede Seite in Kästen; jetzt trägt die Linie
   * die Gliederung, nicht der Helligkeitssprung. */
  --karte: #2a2c30;
  --karte-hell: #313438;
  /* Feinere Linien: 1 px bleibt 1 px, aber sie drängt sich nicht mehr auf. */
  --linie: #3d4046;
  --linie-leise: #323439;
  --schrift: #e9eae5;
  --schrift-leise: #8f9188;
  --gut: #64c07a;
  --schlecht: #e0655b;
  --achtung: #d99a3c;
  /* Aliasse, damit dieselben Statusfarben überall aus einer Quelle kommen –
   * Rundgang, Lageplan und Maskeneditor griffen bis zum 01.09.2026 auf
   * `--akzent`/`--flaeche`, die es nie gab: Die Planpunkte hatten dadurch
   * im Dunkeln gar keinen Hintergrund. `--akzent` ist das Marken-Gelb,
   * `--flaeche` der tiefe Bildgrund. */
  --akzent: var(--gelb);
  --auf-akzent: var(--auf-gelb);
  --flaeche: var(--grund-tief);
  /* Schatten sind fast weg. Auf dunklem Grund tragen sie ohnehin wenig und
   * machen die Oberfläche schwer. */
  --schatten: 0 4px 16px rgba(0, 0, 0, .28);
  --schatten-klein: 0 1px 2px rgba(0, 0, 0, .12);
  /* Einer trägt weiter als die anderen: der Abspieler auf der
     Zeitrafferseite. Er soll über der Seite liegen und nicht in sie
     eingelassen sein (Wunsch des Betreibers vom 11.09.2026: „ein bisschen
     schwebend mit Schatten, dass es nicht so reingesetzt wirkt“). Auf
     dunklem Grund muss ein Schatten dafür weit und weich sein – ein enger
     wäre schlicht nicht zu sehen. */
  --schatten-schwebend: 0 28px 60px rgba(0, 0, 0, .62),
                        0 8px 20px rgba(0, 0, 0, .42);

  /* Dezente Verläufe statt flacher Flächen – kaum sichtbar, aber die
   * Seite bekommt Tiefe: oben minimal heller, wie Licht von oben. */
  --seitengrund: radial-gradient(120% 60% at 50% 0%,
                   #2b2e33 0%, var(--grund) 55%) fixed;
  --kartengrund: linear-gradient(180deg,
                   color-mix(in srgb, var(--karte-hell) 45%, var(--karte)),
                   var(--karte) 42%);
  --knopfgrund: linear-gradient(180deg, #dee402, #c9ce00);

  --titelschrift: Montserrat, "Helvetica Neue", system-ui, sans-serif;
  --textschrift: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --zahlschrift: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Größere Rundungen, weichere Anmutung – Knöpfe und Felder eine Stufe
   * darunter, sonst werden kleine Elemente zu Pillen. */
  --rund: 10px;
  --rund-gross: 18px;
  --breite: 1600px;
  color-scheme: dark;
}

:root[data-thema="hell"] {
  --gelb-schrift: #5f6300;
  /* Auf Weiß verschwindet das helle Rosa. Derselbe Farbton, tiefer
   * angesetzt – erkennbar als Rosa, aber lesbar. */
  --wind: #c25b8c;
  /* Auf Weiß braucht der Kringel eine dunkle Bahn und einen dunklen Kopf –
   * das helle Gelb verschwindet dort fast. */
  --kreisbahn: rgba(17, 19, 8, .15);
  --kreisfarbe: #5f6300;

  --tinte: #080808;
  --grund: #ffffff;
  --grund-tief: #0b0c0d;      /* Bildflächen bleiben schwarz, auch im Hellen */
  --karte: #fbfbf6;
  --karte-hell: #ffffff;
  --linie: #e8e9de;
  --linie-leise: #f2f3ea;
  --schrift: #121409;
  --schrift-leise: #6b6e5f;
  /* Grün und Bernstein sind am 09.09.2026 eine Spur dunkler geworden.
   * Auf Weiß gemessen: Grün stand bei 3,98 zu 1, Bernstein bei 4,46 — für
   * kleine Schrift verlangt die Norm 4,5. Betroffen waren die Merker in
   * den Listen („7 Baustellen", „ohne H"), also gerade die kurzen Wörter,
   * die man im Vorbeigehen liest. Jetzt 4,83 und 4,95. Im dunklen Thema
   * bleiben die helleren Werte oben stehen; dort ist der Grund dunkel. */
  --gut: #36804b;
  --schlecht: #c0392f;
  --achtung: #9c6410;
  --schatten: 0 12px 32px rgba(17, 19, 8, .10), 0 2px 6px rgba(17, 19, 8, .06);
  --schatten-klein: 0 1px 3px rgba(17, 19, 8, .07), 0 8px 24px rgba(17, 19, 8, .05);
  /* Im hellen Thema derselbe Zweck, aber zurückhaltender: Auf Weiß liest
     sich ein tiefer Schatten schnell als Schmutz. */
  --schatten-schwebend: 0 20px 44px rgba(17, 19, 8, .16),
                        0 5px 14px rgba(17, 19, 8, .09);
  --seitengrund: linear-gradient(180deg, #ffffff 0%, #f7f8f0 100%) fixed;
  --kartengrund: linear-gradient(180deg, #ffffff,
                   color-mix(in srgb, #ffffff 65%, var(--karte)) 60%);
  --knopfgrund: linear-gradient(180deg, #dee402, #c6cb00);
  color-scheme: light;
}

/* ----------------------------------------------------------- Grundlagen */

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--seitengrund), var(--grund);
  color: var(--schrift);
  font: 15px/1.6 var(--textschrift);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Überschriften waren halbfett. Auf dunklem Grund wirkt das schnell klobig –
 * dort trägt schon der Helligkeitsunterschied. Leichteres Gewicht, dafür
 * etwas engerer Buchstabenabstand: wirkt feiner, bleibt gut lesbar. */
h1, h2, h3, h4 {
  font-family: var(--titelschrift);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.25;
}
h1 { font-size: 1.55rem; }
h2 { font-size: 1.08rem; }
h1 { font-size: 1.35rem; margin: 0 0 .2rem; }
h2 { font-size: 1.05rem; margin: 0 0 .75rem; }
h3 { font-size: .95rem; margin: 0 0 .5rem; }

a { color: var(--gelb-schrift); text-decoration: none; }
a:hover { text-decoration: underline; }

.sinnbild { display: block; flex: 0 0 auto; }
.leise { color: var(--schrift-leise); }
.klein { font-size: .82rem; }
/* Orange Schrift für einzelne Wörter mitten im Text – „stillgelegt", „fehlt",
 * „keine gewählt". Orange und nicht rot: Es ist kein Fehler, nur ein Hinweis –
 * dieselbe Regel wie bei `.bildalter.alt`.
 *
 * `.warnt` gab es schon, aber nur eingesperrt in `.ueberblick` (siehe unten).
 * Die Farbe steht deshalb jetzt hier, die Pillenkante bleibt dort.
 *
 * Zwei ähnliche Wörter, zwei Aufgaben – nicht vermischen:
 *   `.warnt`   färbt Schrift, überall.
 *   `.warnung` ist die Ausprägung eines Bausteins: `.hinweis.warnung`,
 *              `.umrandet.warnung`. Allein an einem Element tut sie nichts. */
.warnt { color: var(--achtung); }
.zahlen { font-variant-numeric: tabular-nums; }
.dehnen { flex: 1 1 auto; }
.trenner { width: 1px; align-self: stretch; background: var(--linie); }

main {
  max-width: var(--breite);
  margin: 0 auto;
  /* Mehr Luft: Der Inhalt darf atmen, gerade oben unter der Kopfzeile. */
  padding: 2rem 1.75rem 5rem;
}

/* ------------------------------------------------------------ Kopfzeile */

.kopf {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Leicht durchscheinend statt massiv schwarz: Beim Blättern schimmert der
   * Inhalt darunter durch, die Leiste wirkt dünner, als sie ist. */
  background: color-mix(in srgb, var(--grund-tief) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--linie-leise);
}
:root[data-thema="hell"] .kopf {
  background: color-mix(in srgb, #fff 88%, transparent);
}

.kopfzeile {
  max-width: var(--breite);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: .65rem 1.5rem;
}

.marke { display: flex; align-items: baseline; gap: .5rem; flex: 0 0 auto; }
.marke:hover { text-decoration: none; }
.marke svg { height: 15px; width: auto; color: var(--gelb); display: block; }
:root[data-thema="hell"] .marke svg { color: #22240f; }
/* Das Wort nach der Wortmarke in `--gelb-marke`, nicht in Grau
   (Wunsch vom 15.09.2026).

   Zwei Anlaeufe davor, beide daneben: Erst der abgedunkelte Schriftton
   `--gelb-schrift`, der auf der hellen Anmeldeseite als dunkles Oliv neben
   dem dunklen TRIOBILD verschwand („ich sehe noch nichts"). Dann der
   verbindliche Pantone-Wert - richtig, aber dem Betreiber zu gedeckt.
   Jetzt ein eigener, hellerer Ton, in beiden Themen derselbe.

   Dass er auf Weiss heller wirkt als noetig, ist Absicht und keine
   Nachlaessigkeit: Das Wort wird nicht gelesen, sondern erkannt, und der
   Name des Links traegt „TB Web" fuer alle, die vorlesen lassen. */
.marke em {
  font-style: normal;
  font-family: var(--titelschrift);
  font-weight: 500;
  font-size: .9rem;
  letter-spacing: .08em;
  color: var(--gelb-marke);
}

/* Baustelle links neben den Modulen: erst der Ort, dann was man dort tut. */
.ortwahl { position: relative; flex: 0 0 auto; }
.ortknopf {
  display: flex;
  align-items: center;
  gap: .45rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  color: var(--schrift);
  border-radius: var(--rund);
  padding: .4rem .7rem;
  font: 500 .88rem var(--textschrift);
  cursor: pointer;
  max-width: 15rem;
}
.ortknopf:hover { border-color: var(--gelb); }
.ortknopf .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ortknopf .sinnbild { color: var(--schrift-leise); }

/* Name oben, Anschrift darunter – beides einzeilig und abgeschnitten, wenn
 * es zu lang wird. Der Knopf darf breiter werden, sobald eine Anschrift
 * dabeisteht: „DF0888 Chemofast Will…" über „Aachener Straße / Akira Okada
 * Straße" wäre in fünfzehn Zeichen zweimal nichts. */
.ortknopf:has(.ortzeilen small),
.ortfest:has(.ortzeilen small) { max-width: 22rem; }
.ortzeilen {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.2;
  min-width: 0;                 /* sonst schneidet das Abkürzen nicht */
  overflow: hidden;
}
.ortzeilen .name,
.ortzeilen small {
  display: block; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ortzeilen small {
  font-size: .76rem;
  font-weight: 400;
  color: var(--schrift-leise);
}

.ortfest {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-size: .88rem;
  font-weight: 500;
  color: var(--schrift-leise);
  white-space: nowrap;
}

.ortliste {
  position: absolute;
  top: calc(100% + .4rem);
  left: 0;
  min-width: 16rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  box-shadow: var(--schatten);
  padding: .3rem;
  z-index: 30;
  /* Rollt selbst. Die Liste hängt im klebenden Kopf: Beim Blättern der
     Seite bleibt sie mit ihm oben stehen, und bei über zwanzig Baustellen
     ragte sie auf dem Handy unter den Rand – die unteren Einträge waren
     nie zu erreichen (Celle, 06.09.2026). `dvh` rechnet die Leisten des
     Handy-Browsers mit; `vh` davor ist der Rückfall für ältere Browser. */
  max-height: calc(100vh - 8rem);
  max-height: min(calc(100dvh - 8rem), 40rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.ortliste a {
  display: block;
  padding: .45rem .6rem;
  border-radius: 4px;
  color: var(--schrift);
  font-size: .88rem;
}
.ortliste a:hover { background: var(--karte-hell); text-decoration: none; }
.ortliste a.hier { color: var(--gelb-schrift); font-weight: 600; }
.ortliste a small { display: block; color: var(--schrift-leise); font-size: .74rem; }

/* Modulleiste. Sieben Punkte – deshalb umbruchfähig, aber nie mittig
 * ausgerichtet: eine umgebrochene Leiste, die zentriert steht, wirkt wie ein
 * Absatz und nicht mehr wie eine Navigation. */
/* Die Modulleiste: **eine weiche Pille für den Bereich, in dem man ist.**
 *
 * Vorher grauer Text mit einem schwebenden gelben Strich darunter – und
 * genau dasselbe Bild trug die Unterleiste eine Zeile tiefer. Zwei Ebenen,
 * die gleich aussehen, sind keine zwei Ebenen; es sah aus wie zwei Reihen
 * vergessener Standardlinks (02.09.2026 bemängelt).
 *
 * Jetzt zwei verschiedene Sprachen: oben **Fläche**, unten **Text mit
 * Punkt**. Die Pille trägt drei Merkmale, nicht nur Farbe – Fläche,
 * Schriftgewicht und Farbe –, damit auch erkennbar bleibt, wo man ist, wenn
 * jemand Farben schlecht unterscheidet.
 *
 * Die Tönung wird aus `--gelb` **gemischt**, nicht als zweiter Farbwert
 * hingeschrieben: So stimmt sie in Hell und Dunkel von selbst, und es gibt
 * keine Variable, die in einem der beiden Themen fehlt.
 *
 * Kein Bezug zur Unterkante des Kopfes (ein Reiter, der auf der Trennlinie
 * aufsitzt, war der erste Entwurf): Auf dem Handy ist die Leiste ein
 * seitlich schiebbarer Streifen mit negativem Außenabstand, und ein
 * randbündiger Strich hätte dort nicht mehr gepasst. */
.module {
  display: flex;
  flex-wrap: wrap;
  gap: .15rem;
  margin-right: auto;
}
.module a, .module span {
  display: block;
  /* Dieselbe Rundung wie Knöpfe und Karten (`--rund`), **kein** voller
   * Kreisbogen: In TB Web ist sonst nichts eine 999-Pixel-Pille, und die
   * eine, die es war, fiel als Fremdkörper auf (02.09.2026: „sieht
   * verschoben aus, mach es stimmiger"). */
  /* Größer und schwerer als bisher (.875 → .95, 400 → 500). „Hebe die
   * Links des Hauptmenüs besser hervor" (09.09.2026): Sie standen in
   * derselben Größe da wie die Unterpunkte, und zwei Ebenen, die gleich
   * aussehen, sind für das Auge eine. */
  padding: .42rem .7rem;
  border-radius: var(--rund);
  font-size: .95rem;
  font-weight: 500;
  color: var(--schrift-leise);
  white-space: nowrap;
  transition: color .13s ease, background-color .13s ease;
}
.module a:hover {
  color: var(--schrift);
  /* Aus der Schriftfarbe gemischt, nicht aus einem grauen Festwert – im
     Hellen wird sie dunkel, im Dunklen hell, ohne zweite Regel. */
  background: color-mix(in srgb, var(--schrift) 8%, transparent);
  text-decoration: none;
}
.module a.hier {
  color: var(--gelb-schrift);
  font-weight: 600;
  /* Leiser als zuerst: 16 % waren auf Weiß ein Leuchtstift-Streifen. Zwölf
   * Prozent reichen, weil Gewicht und Schriftfarbe die Arbeit ohnehin
   * mitmachen – der Kontrast liegt weiter über dem Nötigen. */
  background: color-mix(in srgb, var(--gelb) 12%, transparent);
}
.module a.hier:hover { background: color-mix(in srgb, var(--gelb) 20%, transparent); }
/* Ein kleiner Zeiger unter dem offenen Punkt. Er sagt: Was hier gilt, geht
 * darunter weiter – und darunter steht die Unterleiste mit demselben Namen
 * davor. Das ist die „Verbindung zum oberen Hauptmenü" (09.09.2026).
 *
 * Als Dreieck am unteren Rand des Feldes, nicht als durchgezogene Linie:
 * Eine Linie über die ganze Breite sähe aus wie ein zweiter Rahmen. Das
 * Dreieck zeigt auf eine Stelle. */
.module a { position: relative; }
/* **Nur, wenn darunter wirklich etwas steht.** Nicht jeder Hauptpunkt hat
 * eine Unterleiste: „Projekte / Kameras" hat keine – und das ist die
 * meistbenutzte Seite der Verwaltung. Im Kundenportal gibt es überhaupt
 * keine. Dort zeigte das Dreieck ins Leere, also genau der Fall, den die
 * Zeile weiter unten für den Umbruch vermeiden will (09.09.2026 gefunden).
 *
 * `body:has(.unterleiste)` fragt danach im Stylesheet, statt die Vorlage
 * umzubauen: Kopfzeile und Unterleiste stehen in verschiedenen Bereichen
 * der Seite, ein gemeinsamer Merker hätte den halben Rahmen umgestellt. */
body:has(.unterleiste) .module a.hier::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -.42rem;
  width: 0;
  height: 0;
  transform: translateX(-50%);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid var(--gelb);
}
@media (max-width: 800px) {
  /* Umgebrochen steht der Punkt nicht mehr über seiner Unterleiste – dann
   * zeigt der Zeiger ins Leere und bleibt besser weg. */
  .module a.hier::after { display: none; }
}
.module a:focus-visible { outline: 2px solid var(--gelb); outline-offset: 2px; }
/* Nicht freigeschaltet: bleibt sichtbar, ist aber taub. */
.module span.aus { opacity: .32; cursor: not-allowed; }

/* ---- Das Personenmenü oben rechts (09.09.2026) -----------------------
 *
 * „Mein Passwort" und „Abmelden" standen als zwei Knöpfe in der Leiste.
 * Beides betrifft nicht die Arbeit, sondern den eigenen Zugang, und beides
 * braucht man selten – zwei Knöpfe dafür sind zwei zu viel. Jetzt ein
 * Personenzeichen, das nach unten aufklappt.
 *
 * Der Pfeil, den `<details>` sonst vor die Zusammenfassung setzt, ist weg
 * (`list-style: none` und die WebKit-Zeile dazu): Hier ist das Zeichen
 * selbst der Knopf. */
.kontomenue { position: relative; }
.kontomenue > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border: 1px solid var(--linie);
  border-radius: 999px;
  color: var(--schrift-leise);
  cursor: pointer;
  list-style: none;
  transition: color .13s ease, border-color .13s ease, background-color .13s ease;
}
.kontomenue > summary::-webkit-details-marker { display: none; }
.kontomenue > summary:hover,
.kontomenue[open] > summary {
  color: var(--schrift);
  border-color: var(--gelb);
  background: color-mix(in srgb, var(--gelb) 12%, transparent);
}
.kontomenue > summary:focus-visible { outline: 2px solid var(--gelb); outline-offset: 2px; }

/* Die Klappe hängt am rechten Rand des Zeichens und nicht am linken:
 * Sie steht ganz außen, und nach links ist Platz, nach rechts nicht. */
.kontoklappe {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  z-index: 60;
  min-width: 13rem;
  display: flex;
  flex-direction: column;
  padding: .35rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
}
/* Der Spalt von 0,4rem zwischen Zeichen und Klappe war für die Maus ein
 * Loch: Wer hinunterfuhr, war einen Augenblick auf keinem von beiden, das
 * Menü bemerkte ein Verlassen und ging weg, bevor man es erreichte
 * (09.09.2026 bemängelt). Dieses Aufsatzstück füllt den Spalt – man sieht
 * es nicht, die Maus schon. Etwas höher als der Abstand, damit auch ein
 * halber Punkt Rundung überdeckt ist. */
.kontoklappe::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: .55rem;
}
.kontower {
  margin: 0;
  padding: .45rem .6rem .55rem;
  border-bottom: 1px solid var(--linie-leise);
  display: flex;
  flex-direction: column;
  gap: .1rem;
}
.kontower strong { font-size: .88rem; }
/* Eine lange Adresse soll die Klappe nicht in die Breite ziehen. */
.kontower .klein { overflow-wrap: anywhere; }
.kontoklappe a,
.kontoklappe button {
  display: flex;
  align-items: center;
  /* Knöpfe stellen ihren Inhalt in TB Web sonst in die Mitte. Hier stehen
   * sie untereinander mit einem Link zusammen, und dann muss auch der
   * Knopf links anfangen – sonst sitzt „Abmelden" um achtzig Punkte
   * versetzt unter „Mein Passwort" (09.09.2026 bemängelt). */
  justify-content: flex-start;
  gap: .5rem;
  width: 100%;
  margin-top: .15rem;
  padding: .5rem .6rem;
  border: 0;
  border-radius: var(--rund);
  background: transparent;
  color: var(--schrift);
  font: inherit;
  font-size: .88rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.kontoklappe a:hover,
.kontoklappe button:hover { background: var(--karte-hell); }
.kontoklappe form { margin: 0; }
.kontoklappe .sinnbild { color: var(--schrift-leise); flex: none; }

/* Umbrechen darf die Werkzeugzeile immer, nicht erst unter 640 Punkten:
 * Auf der Kameraseite stehen hier Suchfeld, drei Knöpfe und der
 * Ansichtsumschalter, und die passen bei 900 Punkten Fensterbreite nicht
 * mehr nebeneinander – am 09.09.2026 nachgemessen, elf Punkte ragten
 * hinaus und die ganze Seite bekam dafür einen Querlauf. Auf breiten
 * Schirmen ändert die Zeile nichts, dort ist Platz genug. */
.kopfwerkzeuge {
  display: flex;
  align-items: center;
  gap: .5rem;
  row-gap: .45rem;
  flex: 0 1 auto;
  flex-wrap: wrap;
  min-width: 0;
}

/* Der Kundenkopf (mittel/rahmen.js, Klasse `kundenkopf`) trägt höchstens
 * drei Werkzeuge: den Weg ins Adminportal, das Thema und das Konto. Die
 * brechen nicht um – ein Kontoknopf allein in der zweiten Zeile sah
 * verrutscht aus (29.09.2026, „ordne das mal schöner"). Platz holt sich
 * die Zeile bei der Modulleiste, die in eine eigene Zeile wandern darf
 * (siehe unten). Die Werkzeugzeile der Verwaltung
 * (vorlagen/rahmen.html) ist nicht gemeint – dort stehen Suchfeld und
 * Knöpfe, und die müssen umbrechen dürfen (Regel oben). Ab 641, damit die
 * Handyregeln weiter unten unangetastet bleiben. */
@media (min-width: 641px) {
  .kundenkopf .kopfwerkzeuge { flex: 0 0 auto; flex-wrap: nowrap; }
}
/* Und wenn die Modulleiste trotzdem nicht passt, wandert sie in eine
 * eigene Zeile: `module-unten` setzt mittel/rahmen.js (`kopfOrdnen`),
 * sobald es die Leiste umgebrochen misst – dieselbe Form wie unter 900
 * Punkten, nur nach Bedarf statt nach Breite. */
@media (min-width: 901px) {
  .kundenkopf .kopfzeile.module-unten { flex-wrap: wrap; row-gap: .35rem; }
  .kundenkopf .kopfzeile.module-unten .module { order: 3; width: 100%; margin-right: 0; }
  .kundenkopf .kopfzeile.module-unten .kopfwerkzeuge { margin-left: auto; }
}

/* Der Umschalter hell/dunkel.
 *
 * Deutlicher als vorher: Er war ein 32 Punkte großes Kästchen mit einem
 * Sinnbild darin, das man suchen musste. Jetzt trägt er das Wort daneben und
 * ist so hoch wie die anderen Knöpfe der Kopfzeile – wer die Darstellung
 * wechseln will, soll nicht raten, wo. */
.thema {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  height: 34px;
  padding: 0 .7rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  background: none;
  color: var(--schrift);
  font: inherit;
  font-size: .86rem;
  cursor: pointer;
}
.thema .themazeichen { display: grid; place-items: center; color: var(--gelb); }
.thema:hover { border-color: var(--gelb); }

/* Zwei Zustände: Hell und Dunkel (08.09.2026; „System" ist weg).
 *
 * Die Verwaltungsseiten kommen fertig vom Server – dort stehen beide
 * Zeichen im Knopf und das Stylesheet blendet eines aus. So springt beim
 * Laden nichts um, was ein nachträglich zeichnendes Skript nicht vermeiden
 * könnte. Die Kundenseiten bauen ihren Kopf ohnehin im Browser; dort setzt
 * `rahmen.js` das Wort als Text, und `:empty` sorgt dafür, dass es nicht
 * doppelt erscheint.
 *
 * `[data-themawahl="system"]` steht hier weiter — bei jemandem, der die
 * Seite seit Tagen offen hat, kann die alte Marke noch am `<html>` hängen,
 * bis er neu lädt. Ohne diese Zeile stünde der Knopf dann ohne Zeichen da. */
.thema .themazeichen .sinnbild { display: none; }
:root[data-themawahl="hell"] .thema .sinnbild-sonne,
:root[data-themawahl="system"] .thema .sinnbild-sonne,
:root[data-themawahl="dunkel"] .thema .sinnbild-mond { display: block; }
:root[data-themawahl="system"] .thema .themawort:empty::after { content: "System"; }
:root[data-themawahl="hell"] .thema .themawort:empty::after { content: "Hell"; }
:root[data-themawahl="dunkel"] .thema .themawort:empty::after { content: "Dunkel"; }
/* Ohne Angabe – etwa wenn JavaScript aus ist – steht wenigstens etwas da. */
:root:not([data-themawahl]) .thema .sinnbild-automatik { display: block; }
:root:not([data-themawahl]) .thema .themawort:empty::after { content: "System"; }

/* Der Weg zurück ins interne Portal, sichtbar nur für die Verwaltung.
 *
 * Der Knopf „Kundenansicht" führt hinaus; ohne Gegenstück musste man den
 * Zurück-Pfeil des Browsers suchen oder die Adresse tippen. Ein Ausgang
 * ohne Eingang ist eine halbe Tür. */
.zurverwaltung { gap: .35rem; }
.zurverwaltung .sinnbild { margin-left: -.15rem; }
/* Auf dem Handy ist der Platz knapp – dort bleibt nur das Zeichen, aber der
   Knopf behält seine fingergerechte Größe. */
@media (max-width: 640px) {
  .thema { padding: 0 .55rem; }
  .thema .themawort { display: none; }
}

.kontowahl { position: relative; }
.kontoknopf {
  display: flex;
  align-items: center;
  gap: .5rem;
  background: none;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .3rem .5rem .3rem .35rem;
  color: var(--schrift);
  font: 500 .85rem var(--textschrift);
  cursor: pointer;
}
.kontoknopf:hover { border-color: var(--gelb); }
.kuerzel {
  display: grid;
  place-items: center;
  width: 24px; height: 24px;
  border-radius: 4px;
  background: var(--gelb);
  color: var(--auf-gelb);
  font: 600 .7rem var(--titelschrift);
}
.kontoliste {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  min-width: 15rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  box-shadow: var(--schatten);
  padding: .3rem;
  z-index: 30;
}
.kontoliste .wer { padding: .5rem .6rem; border-bottom: 1px solid var(--linie); margin-bottom: .3rem; }
.kontoliste .wer strong { display: block; font-size: .88rem; }
.kontoliste .wer span { color: var(--schrift-leise); font-size: .78rem; }
.kontoliste a, .kontoliste button {
  display: block;
  width: 100%;
  text-align: left;
  padding: .45rem .6rem;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--schrift);
  font: .88rem var(--textschrift);
  cursor: pointer;
}
.kontoliste a:hover, .kontoliste button:hover { background: var(--karte-hell); text-decoration: none; }

/* --------------------------------------------------------- Formularteile */

input, select, textarea {
  /* Deckend, nicht durchscheinend. Ein halbdurchsichtiger Hintergrund sah im
   * dunklen Thema gut aus – im hellen drückten Auswahlfelder und Zeitfelder
   * ihren eigenen grauen Grund durch und wirkten wie gesperrt. Die Feinheit
   * trägt hier der Rahmen, nicht die Füllung. */
  background: var(--karte-hell);
  color: var(--schrift);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .52rem .7rem;
  font: inherit;
  width: 100%;
  transition: border-color .13s ease, background-color .13s ease;
}
/* Ein abgeschaltetes Feld muss abgeschaltet aussehen.
 *
 * Fuer Knoepfe steht die Regel seit jeher zwei Bildschirme weiter oben
 * (`button:disabled`), fuer Felder gab es sie nicht: Ein `disabled`
 * <input> sah aus wie jedes andere. Wer hineinklickte, bekam keinen
 * Cursor, keine Eingabe und keine Erklaerung – das sieht nach kaputt aus
 * und ist keines.
 *
 * Aufgefallen am Passwortstand der Kameraseite (11.09.2026 gefragt): Dort
 * steht „Passwort gesetzt/akzeptiert" in einem Feld, das aussah, als
 * koennte man es ueberschreiben; der Knopf „Passwort aendern" daneben war
 * dadurch nicht als der Weg zu erkennen, der er ist.
 *
 * Gestrichelter Rahmen und leise Schrift statt eines grauen Kastens: Die
 * Form des Feldes bleibt, nur seine Zusage faellt weg. */
input:disabled, select:disabled, textarea:disabled {
  color: var(--schrift-leise);
  background: none;
  border-style: dashed;
  cursor: not-allowed;
}

/* Ein Feld, das nur einen Zustand zeigt und beim Anklicken das echte
 * Eingabefeld hervorholt — Kameraseite: „Passwort gesetzt/akzeptiert".
 *
 * Es sieht aus wie ein gesperrtes Feld, ist aber `readonly` und nicht
 * `disabled`. Der Unterschied ist der Grund fuer diese Regel: Ein
 * gesperrtes Feld schluckt den Mausklick vollstaendig — er kommt weder
 * beim Feld noch beim umgebenden <label> an (11.09.2026 im Browser
 * nachgemessen). Wer hineinklickte, bekam ueberhaupt keine Reaktion.
 *
 * Deshalb hier der Zeiger einer Schaltflaeche und ein Rahmen, der beim
 * Darueberfahren fest wird: Das Feld tut etwas, wenn man es anfasst. */
input.standfeld {
  color: var(--schrift-leise);
  background: none;
  border-style: dashed;
  cursor: pointer;
}
input.standfeld:hover { border-style: solid; color: var(--schrift); }

/* Ankreuzkaestchen und Auswahlknoepfe sind keine Felder, die eine Spalte
 * fuellen – sie sind 13 Punkte gross und bleiben es. Das `width: 100%`
 * darueber meint Text-, Zahlen- und Datumsfelder; auf ein Kaestchen
 * angewandt zieht es das Kaestchen ueber die ganze Zeile und schiebt die
 * Beschriftung daneben aus ihrem Feld heraus.
 *
 * Das ist zweimal passiert, bevor diese Zeile da war: auf der Downloadseite
 * schob es „Datum und Uhrzeit" ueber den Suchen-Knopf (09.09.2026), und in
 * `freieseite_bearbeiten.html` steht seit laengerem ein `style="width:auto"`
 * von Hand am Kaestchen – der Notnagel fuer dasselbe Uebel. Jedes Mal wurde
 * es einzeln geflickt (`.ankreuz`, `.tafelfuss`, …), nie hier.
 *
 * VORSICHT bei neuen Regeln: Diese hier ist so genau wie `.irgendwas input`
 * (eine Klasse, ein Typ). Wer weiter unten `.irgendwas input { width: … }`
 * schreibt und dort ein Kaestchen hat, ueberstimmt sie allein durch die
 * spaetere Stelle in der Datei. Dann dort ausdruecklich
 * `:not([type="checkbox"]):not([type="radio"])` anhaengen – so wie es
 * `.suchzeile input` tut. */
input[type="checkbox"], input[type="radio"] { width: auto; }
/* Und Schieberegler sind es auch nicht.
 *
 * Ein Regler ist ein Bedienelement, kein Eingabefeld: Polster, Rahmen und
 * Fuellung der Regel oben legen sich um ihn und machen ihn zu einem Kasten
 * von 347 mal 35 Punkten statt 129 mal 16 (10.09.2026 nachgemessen). In
 * `verpixelung_foto.html` fuellte er damit das ganze Label, und die Zahl
 * daneben rutschte in eine dritte Zeile – das ist die „verschobene
 * Darstellung" der Werkzeugleiste.
 *
 * Fuers Video war das schon einmal geflickt worden
 * (`.videoflaeche-werkzeuge input[type="range"]`, weiter unten), fuers Foto
 * nicht. Dritter Fall derselben Sorte nach den Ankreuzkaestchen – deshalb
 * hier an der Wurzel und nicht wieder je Seite. */
input[type="range"] {
  width: auto;
  padding: 0;
  border: 0;
  background: none;
}
/* Ausser dort, wo ein Regler bewusst eine Feldspalte fuellt: `.feld` ist
 * eine Spalte (`flex-direction: column`), die Beschriftung steht darueber,
 * und der Regler soll die volle Breite nehmen. */
.feld > input[type="range"] { width: 100%; }
input:hover, select:hover, textarea:hover { border-color: var(--schrift-leise); }
/* Platzhalter sind Beispiele, kein Inhalt.
 *
 * Standardmäßig zeichnet der Browser sie fast so kräftig wie echten Text –
 * dann hält man ein leeres Feld für ein ausgefülltes und traut sich nicht,
 * hineinzuschreiben. Deutlich blasser und leicht kursiv: sofort als Beispiel
 * zu erkennen. */
input::placeholder, textarea::placeholder {
  color: var(--schrift-leise);
  opacity: .45;
  font-style: italic;
}
:root[data-thema="hell"] input::placeholder,
:root[data-thema="hell"] textarea::placeholder { opacity: .55; }

/* Kein dicker Rahmen beim Anklicken – eine feine gelbe Linie genügt und
 * bleibt im Bild, statt es zu sprengen. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--gelb);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gelb) 18%, transparent);
}
label { display: block; }
.feld { display: flex; flex-direction: column; gap: .3rem; margin-bottom: .8rem; }
.feld > span { color: var(--schrift-leise); font-size: .82rem; }
/* Ein Feld in einer Reihe muss schrumpfen dürfen (09.09.2026 nachgemessen).
 *
 * Ein Flex-Element hat von Haus aus `min-width: auto` und weigert sich,
 * schmaler zu werden als sein Inhalt. Der Inhalt ist hier ein `<input>`,
 * und ein Eingabefeld ohne Breitenangabe ist rund vierhundert Punkte breit
 * — zwanzig Zeichen, so steht es in der Norm. Auf der Kameraseite ragte
 * das Formular dadurch auf einem 375-Punkte-Schirm achtundneunzig Punkte
 * aus dem Bild, und das Feld „Unser Name" war rechts abgeschnitten.
 *
 * `min-width: 0` gibt die Weigerung auf, `width: 100%` am Eingabefeld
 * füllt danach genau die Spalte. */
/* Eine Reihe Felder nebeneinander, die auf schmalen Geräten umbricht.
   Die Regel stand bis zum 12.09.2026 nur unter `.aufgabeformular`; seit es
   die Seite „Zeitraffer individuell" gibt, braucht sie zwei Formulare, und
   eine Klasse, die je nach Elternteil etwas anderes tut, ist eine Falle. */
.feldreihe { display: flex; gap: 1rem; flex-wrap: wrap; }
.feldreihe > .feld { flex: 1 1 12rem; }
.wahlreihe > .feld, .feldreihe > .feld { min-width: 0; }
.feld > input, .feld > select, .feld > textarea { max-width: 100%; min-width: 0; }
/* Eine Mehrfachauswahl misst Safari an ihrer längsten Zeile und hält sie
   trotz `max-width` – mit fester Breite folgt sie dem Feld (21.09.2026). */
.feld > select[multiple] { width: 100%; }

button, .knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  /* Leichter Verlauf – der Knopf wirkt gewölbt statt gestanzt. Die
   * Farbe bleibt das Marken-Gelb; Varianten überschreiben `background`
   * komplett, deshalb erbt sich hiervon nichts Falsches. */
  background: var(--knopfgrund);
  color: var(--auf-gelb);
  border: 0;
  border-radius: var(--rund);
  padding: .58rem 1.1rem;
  /* 600 war fett genug für eine Baustelle, aber nicht fein. 550 gibt es
   * nicht überall, deshalb 500 plus etwas Buchstabenabstand – das liest
   * sich ruhiger und bleibt auf gelbem Grund gut zu erkennen. */
  font: 500 .875rem var(--textschrift);
  letter-spacing: .005em;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color .13s ease, border-color .13s ease,
              color .13s ease, opacity .13s ease, transform .12s ease;
}
button:hover, .knopf:hover { filter: brightness(1.08); text-decoration: none; }
button:active:not(:disabled), .knopf:active { transform: scale(.985); }
/* Auch die Stauchung beim Drücken ist Bewegung. Sie steht hier bei jedem
   Knopf der ganzen Anwendung – und war als einzige nie abgeschaltet, wo
   jemand Bewegung abbestellt hat (gefunden am 11.09.2026 beim Prüfen der
   Zeitrafferkacheln, die sie erben). Die Farbe des Knopfes wechselt
   weiterhin, der Druck bleibt also spürbar. */
@media (prefers-reduced-motion: reduce) {
  button:active:not(:disabled), .knopf:active { transform: none; }
}
button:disabled { opacity: .45; cursor: default; filter: none; }

.knopf.umrandet, button.umrandet {
  background: none;
  border: 1px solid var(--linie);
  /* Vorher `--schrift` – auf hellem Grund wirkten diese Knöpfe dadurch blass
   * und wie gesperrt. Etwas gedeckter, aber klar lesbar. */
  color: var(--schrift);
  font-weight: 450;
}
.knopf.umrandet:hover, button.umrandet:hover {
  border-color: var(--gelb);
  color: var(--gelb-schrift);
  filter: none;
}
/* Ein umrandeter Knopf, der einen Zustand anzeigt statt ihn nur anzubieten:
 * „Verpixelung ist AUS" bei Livebild und Wiedergabe. Ohne diese Regel sah der
 * eingeschaltete Knopf genauso aus wie der ausgeschaltete – der einzige
 * Unterschied war das Wörtchen „ist". Bei einem Schalter, der das
 * unverpixelte Kamerabild freigibt, ist das zu wenig.
 *
 * Orange, nicht gelb: Gelb steht in diesem Stylesheet für Bedienung und
 * Herkunft, nie für einen Zustand. Und nicht rot – es ist kein Fehler,
 * sondern eine bewusste Entscheidung, die im Protokoll steht. */
.knopf.umrandet.warnung, button.umrandet.warnung {
  border-color: var(--achtung);
  color: var(--achtung);
  background: color-mix(in srgb, var(--achtung) 12%, transparent);
  font-weight: 600;
}
.knopf.umrandet.warnung:hover, button.umrandet.warnung:hover {
  border-color: var(--achtung);
  color: var(--achtung);
  background: color-mix(in srgb, var(--achtung) 20%, transparent);
}

.knopf.flach, button.flach {
  background: none;
  color: var(--schrift-leise);
  font-weight: 500;
  padding: .4rem .55rem;
}
.knopf.flach:hover, button.flach:hover {
  color: var(--gelb-schrift);
  background: var(--karte);
  filter: none;
}
/* „Löschen" und „Trennen". In Ruhe grau wie jeder flache Knopf – in einer
 * Liste mit fünfzehn Sicherungen wären fünfzehn rote Knöpfe nur Lärm. Beim
 * Überfahren und beim Ansteuern mit der Tastatur wird er rot: genau dann,
 * wenn jemand kurz davor ist zu drücken.
 *
 * Ohne diese Regel wurde „Löschen" beim Überfahren **gelb** – dieselbe Farbe
 * wie „Speichern" daneben. */
/* Auch ein umrandeter Lösch-Knopf (Beleg-Betrachter) wird erst beim
 * Überfahren rot – in Ruhe so leise wie seine Nachbarn. */
.knopf.umrandet.loeschen:hover, button.umrandet.loeschen:hover,
.knopf.umrandet.loeschen:focus-visible, button.umrandet.loeschen:focus-visible {
  color: var(--schlecht);
  border-color: var(--schlecht);
  background: color-mix(in srgb, var(--schlecht) 10%, transparent);
}
.knopf.flach.loeschen:hover, button.flach.loeschen:hover,
.knopf.flach.loeschen:focus-visible, button.flach.loeschen:focus-visible {
  color: var(--schlecht);
  background: color-mix(in srgb, var(--schlecht) 10%, transparent);
}
.knopf.wetter { background: var(--orange); color: #fff; }

.ankreuz {
  display: flex;
  align-items: center;
  gap: .5rem;
  cursor: pointer;
  font-size: .88rem;
}
.ankreuz input { width: auto; flex: 0 0 auto; accent-color: var(--gelb); }

/* Schalter für „an / aus" – ein Ankreuzfeld sagt nicht deutlich genug,
 * dass hier eine Freigabe erteilt wird. */
.schalter { display: flex; align-items: center; gap: .7rem; cursor: pointer; }
.schalter input { position: absolute; opacity: 0; width: 0; height: 0; }
.schalter .bahn {
  flex: 0 0 auto;
  width: 38px; height: 22px;
  border-radius: 999px;
  background: var(--linie);
  border: 1px solid var(--linie);
  position: relative;
  transition: background .15s;
}
.schalter .bahn::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--schrift-leise);
  transition: transform .15s, background .15s;
}
.schalter input:checked + .bahn { background: var(--gelb); border-color: var(--gelb); }
.schalter input:checked + .bahn::after { transform: translateX(16px); background: var(--auf-gelb); }
.schalter input:focus-visible + .bahn { outline: 2px solid var(--gelb); outline-offset: 2px; }

/* -------------------------------------------------------------- Hinweise */

.hinweis {
  border-left: 3px solid var(--gelb);
  background: var(--karte);
  padding: .7rem 1rem;
  border-radius: 0 4px 4px 0;
  margin: 0 0 1rem;
}
.hinweis.gut { border-color: var(--gut); }
.hinweis.schlecht { border-color: var(--schlecht); }
/* Die dritte Ausprägung, die es sechsmal in den Vorlagen gab und hier nicht:
 * „Noch kein Postausgang", „Sie sehen das unverpixelte Kamerabild". Ohne sie
 * trugen diese Kästen die gelbe Kante des neutralen Hinweises – der Leser
 * konnte eine Warnung nicht von einer Auskunft unterscheiden.
 * Wie bei `.einrichtkasten.dringend`: orange, nicht rot. */
.hinweis.warnung { border-color: var(--achtung); }

/* Jede Fläche mit erfundenen Daten schreibt das sichtbar dazu. Ein
 * ausgedachter Messwert, den jemand für echt hält, ist schlimmer als eine
 * leere Fläche. Die Regel stammt aus TB BauCam. */
.beispielhinweis {
  /* Kein `display: flex`.
     
     Der Kasten enthält Fließtext mit einem Sinnbild davor und einzelnen
     hervorgehobenen Wörtern darin. In einem Flex-Kasten wird **jedes**
     Textstück zwischen zwei Elementen ein eigener Block: Auf einem breiten
     Bildschirm fiel das nicht auf, auf einem Handy zerfiel der Satz in
     „Das  Bild  ist ein  Platzhalter". Fließtext gehört in den normalen
     Textfluss; das Sinnbild reiht sich mit `vertical-align` ein. */
  display: block;
  margin-top: 2rem;
  padding: .6rem .9rem;
  border: 1px dashed var(--linie);
  border-radius: var(--rund);
  color: var(--schrift-leise);
  font-size: .82rem;
}
.beispielhinweis strong { color: var(--schrift); font-weight: 600; }
.beispielhinweis .sinnbild { vertical-align: -.2em; margin-right: .35rem; }

.leer {
  color: var(--schrift-leise);
  padding: 4rem 1rem;
  text-align: center;
  border: 1px dashed var(--linie);
  border-radius: var(--rund-gross);
}
.leer .sinnbild { margin: 0 auto .8rem; color: var(--linie); }

/* ------------------------------------------------------------- Anmeldung */

.anmeldeseite {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 2rem 1.25rem 4rem;
}
.anmeldekarte { width: 100%; max-width: 23rem; }
.anmeldekarte .marke { justify-content: center; margin-bottom: 2rem; }
.anmeldekarte .marke svg { height: 22px; }
.anmeldekarte form {
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  padding: 1.75rem;
}
.anmeldekarte h1 { font-size: 1.15rem; margin-bottom: 1.25rem; }
.anmeldekarte button[type="submit"] { width: 100%; margin-top: .6rem; }
.anmeldefuss {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1rem;
  font-size: .82rem;
}
.anmeldeseite .thema { position: fixed; top: 1.25rem; right: 1.25rem; }

.demokasten {
  margin-top: 1.25rem;
  border: 1px dashed var(--linie);
  border-radius: var(--rund);
  padding: .8rem .9rem;
  font-size: .8rem;
  color: var(--schrift-leise);
}
.demokasten strong { color: var(--schrift); display: block; margin-bottom: .4rem; }
.demokasten button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 1px solid var(--linie);
  color: var(--schrift);
  font-weight: 400;
  padding: .35rem .5rem;
  margin-top: .3rem;
  font-size: .78rem;
}
.demokasten button { white-space: normal; line-height: 1.4; }
.demokasten button:hover { border-color: var(--gelb); filter: none; }
.demokasten code { font-family: var(--zahlschrift); color: var(--gelb-schrift); }

/* ------------------------------------------------------ Seitenkopf innen */

.seitenkopf {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.75rem;
}
.seitenkopf .titel { margin-right: auto; }
/* Sinnbilder sind `display: block` – in einem Rücklink muss das Zeichen neben
 * der Schrift stehen, nicht darüber. */
.zurueck {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font-size: .8rem;
  font-weight: 500;
  margin-bottom: .2rem;
}
.seitenkopf p { margin: 0; color: var(--schrift-leise); font-size: .88rem; }

/* ------------------------------------------------------ Baustellenwahl */

.ortkacheln {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
  gap: 1.25rem;
}
.ortkachel {
  display: block;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  overflow: hidden;
  color: inherit;
  transition: box-shadow .15s ease-out, border-color .15s ease-out;
}
.ortkachel:hover {
  text-decoration: none;
  box-shadow: var(--schatten);
  border-color: rgba(210, 215, 0, .6);
}
.ortkachel .bild { display: block; height: 11rem; background: #000; }
.ortkachel .bild img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ortkachel .text { padding: .9rem 1rem 1rem; }
.ortkachel h2 { margin: 0 0 .15rem; font-size: 1rem; }
.ortkachel .ort { color: var(--schrift-leise); font-size: .82rem; }
.ortkachel .zeile {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-top: .7rem;
  padding-top: .7rem;
  border-top: 1px solid var(--linie);
  font-size: .78rem;
  color: var(--schrift-leise);
}

/* ------------------------------------------------------------- Bildbühne
 *
 * Der Aufbau von livebild.triobild.de: Kopf mit Name und Wortmarke, darunter
 * eine schmale Zeile mit Live-Punkt und Zeitstempel, dann das Bild auf
 * Schwarz, unten die Bedienleiste. Bauarchiv, BauCam, Live Video und
 * Wiedergabe teilen sich diese Bühne.
 */

.buehne {
  display: grid;
  grid-template-columns: 22rem minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
.buehne.ohne-spalte { grid-template-columns: minmax(0, 1fr); }

.tafel {
  background: var(--kartengrund);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten-klein);
  overflow: hidden;
}

.tafelkopf {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .9rem 1.15rem;
  border-bottom: 1px solid var(--linie-leise);
}
.tafelkopf .sinnbild { color: var(--gelb); }
.tafelkopf h1, .tafelkopf h2 { margin: 0; font-size: 1rem; }
.tafelkopf .wortmarke { margin: 0 auto; }
/* Ein Suchfeld im Tafelkopf (09.09.2026). Schmal und ohne eigenen Knopf:
 * Es filtert beim Tippen, ein „Suchen" gäbe es nichts zu drücken. Die
 * Breite ist begrenzt, damit der Kopf eine Zeile bleibt; darunter wächst
 * es auf die volle Breite, weil daneben ohnehin nichts mehr steht. */
.tafelkopf .tafelsuche {
  flex: 0 1 16rem;
  min-width: 8rem;
  padding: .32rem .6rem;
  font-size: .84rem;
}
@media (max-width: 640px) {
  .tafelkopf { flex-wrap: wrap; }
  .tafelkopf .tafelsuche { flex: 1 1 100%; }
}
.tafelkopf .wortmarke svg { height: 17px; width: auto; color: var(--schrift); display: block; }

.umschalter {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 7px;
}
/* Auch als Verweise, nicht nur als Knöpfe: Wo die Wahl in der Adresse
   steht, ist sie als Lesezeichen zu merken und übersteht ein Neuladen. */
.umschalter button, .umschalter a {
  background: none;
  color: var(--schrift-leise);
  font-weight: 500;
  font-size: .86rem;
  padding: .3rem .8rem;
  border-radius: 5px;
  text-decoration: none;
  line-height: 1.5;
}
.umschalter button.an, .umschalter a.an {
  background: var(--gelb); color: var(--auf-gelb); font-weight: 600;
}
.umschalter button:not(.an):hover, .umschalter a:not(.an):hover {
  color: var(--schrift); background: none; filter: none;
}

.standzeile {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .45rem 1.15rem;
  border-bottom: 1px solid var(--linie-leise);
  background: var(--grund-tief);
  font-size: .78rem;
  color: var(--schrift-leise);
}
:root[data-thema="hell"] .standzeile { background: var(--karte-hell); }
.standzeile .dateiname {
  margin: 0 auto;
  font-family: var(--zahlschrift);
  font-size: .76rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.livepunkt { display: inline-flex; align-items: center; gap: .4rem; color: var(--schrift); }
.livepunkt::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--schlecht);
  animation: pochen 2.4s ease-in-out infinite;
}
@keyframes pochen { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }
@media (prefers-reduced-motion: reduce) { .livepunkt::before { animation: none } }
/* Der Demo-Zwischenstand (24.09.2026): derselbe Platz wie „Live“, aber ein
 * ruhiger Punkt in der Markenfarbe – nichts pocht, nichts ist „ausgefallen“. */
.demopunkt { display: inline-flex; align-items: center; gap: .4rem; color: var(--schrift); }
.demopunkt::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--gelb-schrift);
}

/* Die Bildfläche legt sich um das Bild, nicht umgekehrt.
 *
 * Vorher war sie so breit wie die Tafel und schwarz hinterlegt – bei jedem
 * Bild, das nicht zufällig dieses Verhältnis hatte, standen links und rechts
 * schwarze Streifen. Unsere Kameras liefern 4:3, 16:9 und bei den
 * Doppelsensoren 32:9; ein festes Maß passt für höchstens eines davon.
 * `fit-content` braucht das Verhältnis gar nicht erst zu kennen. */
.bildflaeche {
  position: relative;
  display: grid;
  place-items: center;
  background: #000;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  overflow: hidden;
}
.bildflaeche img, .bildflaeche video {
  display: block;
  max-width: 100%;
  max-height: 68vh;
  /* `width`/`height: auto` sind die Vorgabe – hier stehen sie trotzdem, und
     zwar aus einem gemessenen Grund.
     
     Die Fläche ist ein Raster (`display: grid`). Darin wird ein Bild ohne
     ausdrückliche Breite auf die volle Spaltenbreite gezogen, und erst danach
     schneidet `max-height` die Höhe ab. Ergebnis: Der Kasten behält die volle
     Breite, das Bild darin wird **in die Breite gezerrt**. An einem 1600x900-
     Bild in einem 1393 Punkte breiten Fenster gemessen: angezeigt 1393x612,
     also 2,28 : 1 statt 1,78 : 1 – 28 % zu breit. Menschen und Gebäude sahen
     gedrungen aus, und niemand konnte sagen, woran es lag.
     
     `justify-self: center` half nicht (gemessen: unverändert 2,28), `object-fit`
     auch nicht – das ändert nur den Inhalt, nicht den Kasten. Erst die
     ausdrückliche Breite nimmt dem Raster die Dehnung ab: dann 1088x612. */
  width: auto;
  height: auto;
}
/* Solange kein Bild da ist, gibt es nichts, woran sich die Fläche halten
 * könnte – dann doch ein Mindestmaß, sonst klappt die Tafel zusammen. */
.bildflaeche.laedt, .bildflaeche:has(.platzhalter) { width: 100%; min-height: 18rem; }

/* Die Lupe: rundes Glas mit dem vergrößerten Bildausschnitt darunter.
 * Das Glas ist reine Anzeige (`pointer-events: none`), der Zeiger sagt
 * über der Fläche, dass es etwas zu vergrößern gibt. */
/* Kein Zeiger über dem Glas (18.09.2026): Das Lupensymbol des Zeigers
 * stand genau in der Mitte des Glases und störte den Blick. */
.lupe-an { cursor: none; }
.lupenglas {
  position: absolute;
  width: 210px;
  height: 210px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 3;
  background-repeat: no-repeat;
  background-color: #000;
  border: 1px solid rgba(255, 255, 255, .5);
  box-shadow: 0 14px 44px rgba(0, 0, 0, .45),
              inset 0 1px 2px rgba(255, 255, 255, .35);
}

/* „Vergrößerung“ (18.09.2026): ein Knopf, darüber die Wahl Zoom oder Lupe. */
.vergroesserung { position: relative; display: inline-block; }
.vergroesserung .wahlmenue {
  position: absolute;
  bottom: calc(100% + .4rem);
  right: 0;
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .4rem;
  min-width: 15rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: .7rem;
  box-shadow: 0 14px 40px rgba(0, 0, 0, .35);
  z-index: 6;
}
.vergroesserung .wahlmenue[hidden] { display: none; }
.vergroesserung .wahlmenue button { justify-content: flex-start; text-align: left; width: 100%; }

/* Der Zoom (18.09.2026): Die Fläche wird zum Rahmen, das Bild darin wird
 * per transform vergrößert und verschoben. `touch-action: none`, damit
 * der Finger das Bild zieht und nicht die Seite. */
.zoom-an { overflow: hidden; cursor: zoom-in; touch-action: none; user-select: none; }
.zoom-an img { will-change: transform; backface-visibility: hidden; }
/* Der Sprung von Stufe zu Stufe gleitet (20.09.2026): Ein Tippen ist eine
   Entscheidung, und die darf man sehen. Die Klasse setzt mittel/zoom.js nur
   für diesen Sprung – beim Ziehen und Kneifen muss das Bild am Finger
   kleben, dort wäre jeder Übergang ein Nachlaufen. */
.zoom-an img.sanft { transition: transform .16s cubic-bezier(.22, .61, .36, 1); }
@media (prefers-reduced-motion: reduce) {
  .zoom-an img.sanft { transition: none; }
}
.zoom-an.gezoomt { cursor: grab; }
.zoom-an.zieht { cursor: grabbing; }
.zoomstufe {
  position: absolute;
  left: .7rem;
  bottom: .6rem;
  z-index: 3;
  padding: .15rem .55rem;
  border-radius: 999px;
  font-size: .8rem;
  color: #fff;
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(4px);
  pointer-events: none;
}

/* Datum und Uhrzeit der Aufnahme, dezent im Bild unten rechts – eine
 * halbdurchsichtige Pille mit Weichzeichner dahinter, damit sie auf
 * hellem wie dunklem Bildinhalt lesbar bleibt, ohne zu schreien. */
.bildstempel {
  position: absolute;
  right: .7rem;
  bottom: .6rem;
  z-index: 2;
  padding: .28rem .7rem;
  border-radius: 999px;
  background: rgba(20, 20, 22, .45);
  color: #fff;
  /* Dieselbe Schrift wie der Stempel, den bildstempel.py ins Foto setzt –
   * DIN Alternate, wo es sie gibt; sonst die Titelschrift. */
  font-family: "DIN Alternate", var(--titelschrift);
  font-weight: 700;
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  white-space: pre;
  backdrop-filter: blur(10px) saturate(1.4);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  pointer-events: none;
  white-space: nowrap;
}
.bildstempel.links { right: auto; left: .7rem; }
.bildstempel:empty { display: none; }
/* Zeigt das Video eigene Bedienelemente (`controls` – wiedergabe.html setzt
 * es, sobald das Bild da ist), liegen die unten: Zeitregler und Vollbildknopf
 * genau in der Ecke der Pille. Auf einem 190 Punkte hohen Handybild wäre
 * das ein Drittel des Bildes voller Schrift. Dann nach oben; links/rechts
 * (`.links`, stempel_ecke) bleibt wie es ist. */
.bildflaeche video[controls] ~ .bildstempel { bottom: auto; top: .6rem; }
/* Solange die Wartedecke liegt, läuft nichts – eine Uhrzeit darüber
 * behauptet einen Stand, den es noch nicht gibt. */
.bildflaeche:has(.ueberdeckung) .bildstempel { display: none; }

/* Der Vergleichsschieber legt seine Bilder absolut übereinander und hat
 * deshalb keine eigene Größe – er verlangt `width: 100%`. In einer Fläche mit
 * `fit-content` sind 100 % von „so breit wie der Inhalt“ genau null, und die
 * Tafel blieb leer. Hier also volle Breite. */
.bildflaeche:has(.vergleich) { width: 100%; }

/* Vollbild: alle Begrenzungen der Seite aufheben.
 *
 * `width: fit-content` und `max-height: 68vh` sind für die Tafel richtig –
 * dort soll neben dem Bild noch die Bedienung Platz haben. Im Vollbild sind
 * sie falsch: Die Fläche schmiegte sich weiter ans Bild, und das Bild blieb
 * bei 68 % der Bildschirmhöhe stehen. Beides muss hier ausdrücklich zurück,
 * sonst erbt der Vollbildmodus die Maße der Seite.
 *
 * Zwei Schreibweisen, weil ältere Safari-Fassungen nur die mit `-webkit-`
 * kennen und eine Regel mit einer unbekannten Auswahl ganz verwerfen. */
.bildflaeche:fullscreen {
  width: 100%;
  height: 100%;
  max-width: none;
  margin: 0;
  display: grid;
  place-items: center;
  background: #000;
}
/* `width/height: auto` war hier falsch: Ein Bild, das kleiner ist als der
 * Bildschirm, blieb dann in seiner **natürlichen** Größe stehen. Ein Standbild
 * ist 1600 Punkte breit, ein Bildschirm 1728 – das Bild klebte mitten in einer
 * schwarzen Fläche, und Vollbild war keines.
 *
 * `100 %` mit `contain` füllt dagegen aus, was da ist, und behält dabei die
 * Form des Bildes: keine gestreckten Menschen. */
.bildflaeche:fullscreen img,
.bildflaeche:fullscreen video {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: contain;
}
.bildflaeche:-webkit-full-screen {
  width: 100%;
  height: 100%;
  max-width: none;
  margin: 0;
  display: grid;
  place-items: center;
  background: #000;
}
.bildflaeche:-webkit-full-screen img,
.bildflaeche:-webkit-full-screen video {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: contain;
}

/* Der Vergleichsschieber im Vollbild.
 *
 * Er kann die Regel oben nicht mitbenutzen: Seine beiden Bilder liegen
 * absolut übereinander und richten sich nach dem **Kasten**, nicht umgekehrt.
 * Wächst der Kasten nicht mit, bleibt der Schieber bei 68 % der Seitenhöhe
 * stehen – mitten auf einem schwarzen Bildschirm.
 *
 * Gerechnet wird wie außerhalb, nur ohne die Rücksicht auf die Bedienleiste:
 * volle Höhe, und wenn das breiter würde als der Bildschirm, richtet sich die
 * Höhe nach der Breite. Der Kasten behält dabei die Form des Bildes – sonst
 * liefe der Griff wieder neben der Schnittkante her.
 *
 * **Zwei Regeln, nicht eine Auswahlliste.** Kennt ein Browser eine
 * Pseudoklasse in der Liste nicht, wirft er die ganze Regel weg – und
 * Firefox kennt `:-webkit-full-screen` nicht (die Nachsicht für
 * `-webkit-`-Namen gilt nur für Pseudo-**Elemente** mit zwei Doppelpunkten).
 * Genau so stand es hier bis zum 02.09.2026: Der Schieber blieb in Firefox
 * bei 68 % mitten auf dem schwarzen Vollbild, obwohl der Kommentar oben
 * den Fehler für gelöst erklärte. Die Nachbarregeln sind seit jeher
 * getrennt; diese beiden waren es nicht. */
.bildflaeche:fullscreen .vergleich {
  height: min(100vh, calc(100vw * 9 / 16));
  width: auto;
  max-width: 100vw;
  max-height: 100vh;
}
.bildflaeche:-webkit-full-screen .vergleich {
  height: min(100vh, calc(100vw * 9 / 16));
  width: auto;
  max-width: 100vw;
  max-height: 100vh;
}

/* Was im Vollbild nichts zu suchen hat: Startdecke und Ladehinweis liegen
 * über dem Bild und würden es verdecken, sobald es läuft. */
.bildflaeche:fullscreen .startdecke { position: absolute; inset: 0; }
.bildflaeche:-webkit-full-screen .startdecke { position: absolute; inset: 0; }

/* Ein Video, das für sich allein ins Vollbild geht (Zeitraffer), soll den
 * Bildschirm ebenfalls ausfüllen. */
video:fullscreen { width: 100%; height: 100%; max-height: none; }
video:-webkit-full-screen { width: 100%; height: 100%; max-height: none; }
.bildflaeche .platzhalter {
  display: grid;
  justify-items: center;
  gap: .8rem;
  padding: 4rem 2rem;
  text-align: center;
  color: var(--schrift-leise);
}
.bildflaeche .platzhalter .sinnbild { color: rgba(139, 141, 133, .45); }
.bildflaeche .platzhalter p { margin: 0; max-width: 24rem; }

/* Blättern durch die Archivbilder unter dem Bild – mittig, damit die beiden
 * Richtungen gleich weit weg liegen. */
.blaetterzeile {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .8rem;
  flex-wrap: wrap;
  padding: .7rem 1rem;
  border-top: 1px solid var(--linie);
}
.blaetterzeile #blaetterstand {
  min-width: 9rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* Über dem Bild statt darunter (10.09.2026). Die Standzeile darüber bringt
 * schon einen unteren Strich mit – ohne diese Regel stünden hier zwei
 * Ein-Punkt-Linien übereinander. */
.blaetterzeile.oben {
  border-top: 0;
  border-bottom: 1px solid var(--linie);
  padding: .55rem 1rem;
}
/* Blätterknöpfe in der Standzeile (19.09.2026, Bauarchiv): „früher",
   Bildnummer und „später" standen in einer eigenen Zeile unter der
   Standzeile – eine Zeile Höhe für drei Angaben, die neben Live-Punkt und
   Zeitstempel Platz haben. Zwei Dehner links und rechts halten die Gruppe
   in der Mitte, die Ränder behalten Live-Punkt und Zeitstempel. Die Zeile
   bleibt luftig: etwas mehr Höhe als die reine Textzeile, weniger als
   beide Zeilen zusammen. */
.standzeile.mit-blaettern { padding-block: .4rem; }
.standzeile .blaettern {
  display: flex;
  align-items: center;
  gap: .8rem;
}
.standzeile .blaettern #blaetterstand {
  min-width: 7.5rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) {
  /* Schmal reicht die Breite nicht für fünf Angaben: Die Gruppe rückt
     mittig in eine zweite Zeile, Live-Punkt und Zeitstempel bleiben oben. */
  .standzeile.mit-blaettern { flex-wrap: wrap; row-gap: .4rem; }
  .standzeile .blaettern {
    flex: 1 1 100%; order: 9; justify-content: center;
    flex-wrap: wrap; gap: .4rem .6rem;
  }
  .standzeile .blaettern #blaetterstand { min-width: 0; }
}

.tafelfuss {
  display: flex;
  align-items: center;
  gap: .65rem;
  flex-wrap: wrap;
  padding: .9rem 1.15rem;
  border-top: 1px solid var(--linie-leise);
}
.tafelfuss .klein { display: inline-flex; align-items: center; gap: .4rem; }
.tafelfuss .rechts { margin-left: auto; display: flex; gap: .5rem; flex-wrap: wrap; }
/* Auswahlfelder erben sonst `width: 100%` aus dem Formularteil und sprengen
 * die Bedienleiste auf eine eigene Zeile. */
/* Auflösungswahl: drei Knöpfe nebeneinander, der gewählte leuchtet. Eine
 * Auswahlliste versteckt, was möglich ist – hier soll man auf einen Blick
 * sehen, was es gibt und was gerade läuft. */
.groessenwahl { display: inline-flex; gap: 0; }
.groessenwahl button {
  border-radius: 0;
  border: 1px solid var(--linie);
  border-right-width: 0;
  background: none;
  color: var(--schrift-leise);
  font-size: .85rem;
  padding: .35rem .8rem;
}
.groessenwahl button:first-child { border-radius: var(--rund) 0 0 var(--rund); }
.groessenwahl button:last-child {
  border-radius: 0 var(--rund) var(--rund) 0;
  border-right-width: 1px;
}
.groessenwahl button:hover { color: var(--schrift); filter: none; }
.groessenwahl button.an {
  background: var(--gelb);
  /* `--auf-gelb`, nicht `--gelb-schrift`: Letzteres ist das Gelb für Text auf
   * dunklem Grund – auf gelbem Knopf wäre das gelb auf gelb. */
  color: var(--auf-gelb);
  border-color: var(--gelb);
  font-weight: 600;
}

.tafelfuss select, .tafelfuss input, .spaltenkopf select { width: auto; font-size: .85rem; padding: .35rem .5rem; }

/* Kameraauswahl als Streifen unter dem Bild – bei zwei Kameras ist ein
 * Auswahlmenü zu viel, bei acht wird eine Kachelwand zu laut. */
.kamerastreifen {
  display: flex;
  gap: .5rem;
  overflow-x: auto;
  padding: .8rem 1rem;
  border-top: 1px solid var(--linie);
  scrollbar-width: thin;
}
.kameraknopf {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: .65rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .4rem .85rem .4rem .4rem;
  color: var(--schrift-leise);
  font: 500 .82rem var(--textschrift);
  cursor: pointer;
  transition: border-color .12s ease, transform .14s ease;
}
/* Die Vorschau war 44 × 26 Punkte groß – auf einem Baustellenbild mit
   Gerüst und Kran erkennt man darauf nichts, und genau dafür ist sie da:
   zu zeigen, welche Kamera das ist (12.09.2026, „etwas größer und
   schöner"). 96 × 54 ist 16:9 und lässt drei Knöpfe nebeneinander stehen,
   ohne dass der Streifen zu scrollen anfängt. */
.kameraknopf .kamerabild {
  position: relative;
  flex: 0 0 auto;
  display: block;
  line-height: 0;
}
.kameraknopf img {
  width: 96px;
  height: 54px;
  object-fit: cover;
  border-radius: calc(var(--rund) - 3px);
  display: block;
  background: #000;
}
/* Der Lebenspunkt sitzt in der Ecke der Vorschau – er gehört zu diesem
   Bild. Der dunkle Ring hält ihn auch über einem hellen Himmel lesbar. */
.kameraknopf .kamerabild .punkt {
  position: absolute;
  top: 5px;
  left: 5px;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, .6);
}
.kameraknopf .wer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
}
.kameraknopf .wer strong {
  font-weight: 600;
  font-size: .88rem;
  color: var(--schrift);
  white-space: nowrap;
}
.kameraknopf:hover {
  color: var(--schrift);
  border-color: var(--schrift-leise);
  filter: none;
  transform: translateY(-2px);
}
.kameraknopf.an {
  border-color: var(--gelb);
  color: var(--gelb-schrift);
  background: var(--karte);
}
.kameraknopf.an .wer strong { color: var(--gelb-schrift); }
/* Wer keine Bewegung will, bekommt keine – der Rahmen zeigt die Wahl auch
   ohne das Anheben. */
@media (prefers-reduced-motion: reduce) {
  .kameraknopf:hover { transform: none; }
}

/* Die Kameras einer Baustelle als **Kacheln** unter dem Bild (20.09.2026).
 *
 * Auf den Kundenseiten – BauCam, Bauarchiv, Live, Wiedergabe, Zeitraffer –
 * steckt der Streifen jetzt in `.kamerawahl` (Baustein `TBSymbole.kamerawahl`):
 * darüber eine Zeile, was das ist und wie viele es sind; darunter die
 * Kameras als Kacheln, so viele je Reihe, wie hineinpassen, der Rest in der
 * nächsten Reihe. Nichts wird seitlich geschoben. Der Betreiber: „das
 * erkennt man nicht, dass da weitere Kameras sind … nicht waagerecht
 * scrollen … Kacheln größer … schreib drüber, was da ist".
 *
 * Der nackte `.kamerastreifen` oben bleibt für die Film- und Bilderleisten
 * der öffentlichen Seite – dort ist das Schieben gewollt. */
.kamerawahl {
  padding: .9rem 1rem 1rem;
  border-top: 1px solid var(--linie);
}
.kamerawahl-titel {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin: 0 0 .65rem;
  font-size: .9rem;
}
.kamerawahl-titel strong { font-weight: 600; color: var(--schrift); }
.kamerawahl .kamerastreifen {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: .8rem;
  overflow: visible;
  padding: 0;
  border-top: 0;
}
.kamerawahl .kameraknopf {
  flex-direction: column;
  align-items: stretch;
  gap: .5rem;
  padding: .5rem;
  font-size: .85rem;
  color: var(--schrift);
}
/* Die Vorschau füllt die Kachel – 16:9 wie die Bühne, auch wenn keine
   Vorschau kommt (dann bleibt die dunkle Fläche, der Name steht darunter). */
.kamerawahl .kameraknopf .kamerabild {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: calc(var(--rund) - 3px);
  overflow: hidden;
}
/* Absolut in die Kachel gelegt statt `height: 100%` (21.09.2026): Safari
   auf dem iPhone rechnet die Prozenthöhe in einem Kasten, dessen Höhe nur
   aus `aspect-ratio` kommt, auf 0 – die Vorschau war geladen, aber
   unsichtbar, und die Kacheln blieben schwarz. Chrome war davon nicht
   betroffen. Ein absolut gesetztes Kind füllt den Kasten in beiden. */
.kamerawahl .kameraknopf img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
}
.kamerawahl .kameraknopf .kamerabild .punkt {
  width: 10px;
  height: 10px;
  top: 7px;
  left: 7px;
}
.kamerawahl .kameraknopf .wer { padding-inline: .2rem; gap: .15rem; }
.kamerawahl .kameraknopf .wer strong { white-space: normal; font-size: .95rem; }
/* Die gewählte Kamera: ein kräftiger Rahmen, kein zweiter Ton. */
.kamerawahl .kameraknopf.an {
  border-color: var(--gelb);
  box-shadow: inset 0 0 0 1px var(--gelb);
}

.punkt { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.punkt.gut { background: var(--gut); }
.punkt.schlecht { background: var(--schlecht); }
.punkt.unbekannt { background: rgba(139, 141, 133, .5); }

/* --------------------------------------------------------- Archivspalte */

.spalte { display: flex; flex-direction: column; max-height: calc(100vh - 8rem); }
/* Die Bilderleiste reicht bis zur Unterkante der Bühne (23.09.2026
 * gewünscht: „die leiste bündig bis an die unterkante der rechten
 * darstellung"). `align-items: start` auf der Bühne ließ jede Spalte bei
 * ihrem eigenen Inhalt enden – links blieb darunter eine Lücke, während
 * rechts Wetterzeile und Knöpfe weiterliefen.
 *
 * Gestreckt wird **nur die Spalte**, nicht die Bühne: Ist die Leiste einmal
 * höher als das Bild, soll die rechte Tafel nicht künstlich mitwachsen und
 * unter den Knöpfen leeren Grund zeigen. `max-height` bleibt die Decke,
 * damit die Leiste an einem langen Tag weiter für sich rollt, und das
 * Raster nimmt den gewonnenen Platz auf (`min-height: 0`, sonst wächst ein
 * rollender Kasten im Flexkasten über seine Grenze hinaus).
 *
 * Am Telefon gilt das nicht: Dort steht die Leiste schmal daneben, ohne
 * Deckel und ohne eigenes Rollen (Regel in der 640er-Abfrage). */
@media (min-width: 641px) {
  /* `max-height: none`, weil der Deckel `calc(100vh - 8rem)` genau die
   * Lücke erzeugte: Die Leiste hörte eine Bildschirmhöhe unter dem Kopf
   * auf, die Tafel daneben lief weiter. Gestreckt ist die Spalte so hoch
   * wie die Tafel und keinen Punkt höher; gerollt wird weiterhin im
   * Raster, nur eben erst, wenn mehr Bilder da sind als Platz. */
  /* Nur die **Bilderleiste** des Archivs, erkennbar am Raster darin. Die
   * Wiedergabe hat in derselben Spalte eine Liste der Aufnahmen: Nimmt man
   * ihr den Deckel, rollt sie nicht mehr für sich, und die Seite wurde
   * 4345 Punkte lang (gemessen 23.09.2026). */
  .buehne > .tafel.spalte:has(.bildraster) { align-self: stretch; max-height: none; }
  /* `flex-basis: 0` und nicht `auto`: Mit `auto` zählte der Inhalt des
   * Rasters zur natürlichen Höhe der Spalte, und an einem Tag mit vielen
   * Aufnahmen wäre die Leiste höher geworden als die Tafel daneben – die
   * Lücke stünde dann rechts. Mit Basis 0 gibt die Tafel die Höhe vor, das
   * Raster nimmt den Rest und rollt für sich, sobald mehr Bilder da sind
   * als Platz. */
  .spalte > .bildraster { flex: 1 1 0; min-height: 0; }
}
.spaltenkopf {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .9rem 1.15rem;
  border-bottom: 1px solid var(--linie-leise);
}
.spaltenkopf .sinnbild { color: var(--gelb); }
.spaltenkopf h2 { margin: 0; font-size: .95rem; margin-right: auto; }
.spaltenkopf input[type="date"] { width: auto; font-size: .82rem; padding: .35rem .5rem; }

.bildraster {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .6rem;
  padding: .8rem;
  overflow-y: auto;
  scrollbar-width: thin;
  /* Feste Reihenhöhe, und zwar aus einem bestimmten Grund: Die Spalte hat eine
   * begrenzte Höhe, damit sie für sich scrollt. Damit steht auch die Höhe des
   * Rasters fest – und ein Raster mit fester Höhe drückt Reihen der Größe
   * `auto` zusammen, bis alles hineinpasst. Weil die Kacheln `overflow: hidden`
   * tragen, ist ihre Mindesthöhe null: Sie fielen zu Streifen zusammen. */
  align-content: start;
  grid-auto-rows: 6.9rem;
}
.bildraster button {
  position: relative;
  /* Muss `flex` sein, nicht `block`: Ein <button> legt seinen Inhalt sonst in
   * eine anonyme Box, die nicht von einem Bild mit `aspect-ratio` mitwächst –
   * die Kacheln standen dann als Streifen da. Gilt genauso für die Filmliste. */
  display: flex;
  flex-direction: column;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--rund);
  overflow: hidden;
  /* Nicht mehr schwarz (19.09.2026, „dezenter“): Der Rand um das Bild und
     die Zeile mit dem Zeitstempel darunter waren als schwarzer Balken
     schwerer als das Bild selbst. Jetzt der helle Kartenton: im Hellen
     weiß auf der cremefarbenen Tafel, im Dunklen ein Grau eine Stufe
     über der Karte – `--grund-tief` bleibt absichtlich schwarz. */
  background: var(--karte-hell);
  cursor: pointer;
}
.bildraster button { height: 100%; }
.bildraster button:hover { filter: none; border-color: var(--schrift-leise); }
.bildraster button.an { border-color: var(--gelb); }
/* `contain`, nicht `cover`: Die Reihenhöhe steht fest, die Spaltenbreite nicht –
 * das Kästchen hat also fast nie das Verhältnis des Bildes, und `cover`
 * schnitt links und rechts ab. Unten rechts steht seit dem 02.09.2026 unser
 * Zeitstempel im Bild; der war dann halb weg. Lieber ein schmaler schwarzer
 * Rand als ein Bild, dem etwas fehlt. */
.bildraster img { display: block; width: 100%; height: 5.4rem; object-fit: contain; flex: 0 0 auto; }
.bildraster .stempel {
  display: block;
  flex: 0 0 auto;
  padding: .22rem .4rem;
  color: var(--schrift-leise);
  font-size: .66rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.bildraster button.an .stempel { color: var(--schrift); }

/* ------------------------------------------------------------ Zeitraffer */

/* Vergleich im Bauarchiv: je Seite ein Tag und eine Uhrzeit. */
.vglwahl {
  display: flex;
  align-items: flex-end;
  gap: 1.2rem;
  flex-wrap: wrap;
  padding: .8rem 1rem;
  border-top: 1px solid var(--linie);
}
.vglseite { display: flex; align-items: center; gap: .5rem; }
.vglseite .marke {
  font-size: .82rem;
  font-weight: 600;
  color: var(--schrift-leise);
  min-width: 3.4rem;
}
.vglseite input, .vglseite select {
  width: auto;
  font-size: .85rem;
  padding: .35rem .5rem;
  height: 2.2rem;
  box-sizing: border-box;
}
.vglwahl .rechts { margin-left: auto; }

/* Startseite: Baustellen mit ihren Kameras.
 *
 * Oben der Name der Baustelle, darunter je Kamera eine Kachel mit dem Bild,
 * das auch unter BauCam steht. Die Kacheln sind bewusst groß – man soll die
 * Baustelle erkennen, nicht einen Namen entziffern. */
.baustellenblock { margin-bottom: 2rem; }

/* Umbrechen darf die Zeile (09.09.2026 nachgemessen): Name, Anschrift,
 * interner Name und die Kamerazahl standen in einer Reihe, die auf einem
 * 375-Punkte-Schirm 412 Punkte breit wurde — die Übersicht des Kunden nahm
 * dadurch die ganze Seite mit nach rechts. Auf breiten Schirmen ändert die
 * Zeile nichts. `min-width: 0` an den Textteilen, damit eine lange
 * Anschrift umbricht statt zu schieben. */
.baustellenkopf {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem;
  row-gap: .3rem;
  padding: 0 0 .7rem;
  margin-bottom: .9rem;
  border-bottom: 1px solid var(--linie);
}
.baustellenkopf > span, .baustellenkopf h2 { min-width: 0; overflow-wrap: anywhere; }
.baustellenkopf h2 { margin: 0; font-size: 1.1rem; }
.baustellenkopf .sinnbild { color: var(--gelb); flex: 0 0 auto; }
.baustellenkopf .dehnen { flex: 1 1 auto; }

.kamerakacheln {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
  gap: 1rem;
}

.kamerakachel {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  overflow: hidden;
  background: var(--karte);
  color: var(--schrift);
  transition: border-color .12s ease, transform .12s ease;
}
.kamerakachel:hover {
  border-color: var(--gelb);
  transform: translateY(-2px);
}
/* Im Raster müssen die Kacheln gleich hoch sein, sonst steht die Reihe
 * schief. Ein festes 16:9-Fenster, und das Bild **füllt** es – die Vorschau
 * darf beschneiden, das ganze Bild ist einen Klick entfernt. Nur die sehr
 * breiten Panoramen (32:9) bekämen so nur ihre Mitte gezeigt; sie werden
 * per Skript als `breit` gekennzeichnet und ganz eingepasst.
 * Solange das Bild lädt, schimmert die Fläche leise, statt schwarz zu
 * starren – so sieht man: hier kommt gleich etwas. */
.kamerakachel .bild {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: linear-gradient(100deg, var(--karte) 40%,
              var(--karte-hell) 50%, var(--karte) 60%);
  background-size: 300% 100%;
  animation: schimmer 1.6s ease-in-out infinite;
}
@keyframes schimmer {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}
/* Das Bild liegt **über** dem Fenster, nicht darin: `aspect-ratio` ist nur
 * ein Wunsch, den ein 4:3-Bild im Fluss überstimmen würde – die Kachel wüchse
 * dann doch in die Höhe. Herausgenommen aus dem Fluss bestimmt allein das
 * Fenster die Größe. */
.kamerakachel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* `breit` kam vom Skript, nachdem das Bild geladen war (über 2,4:1).
 * `eingepasst` kommt aus dem Verhältnis, das das GERÄT meldet, und steht
 * schon beim Zeichnen fest – dieselbe Regel wie in der Liste und in der
 * Verwaltungsübersicht. Beide bewirken dasselbe. */
.kamerakachel img.breit,
.kamerakachel img.eingepasst { object-fit: contain; background: #000; }

.kamerakachel .fuss {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .7rem .85rem;
  font-weight: 500;
}
.kamerakachel .fuss .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
.kamerakachel:hover .fuss .sinnbild { color: var(--gelb); }
.kamerakachel .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Zeitrafferliste.
 *
 * Das Bild bestimmt seine Höhe selbst. Vorher stand hier `height: 6.5rem` mit
 * `object-fit: cover` – bei einer Kachel, die breiter als 16:9 ist, schnitt
 * das oben und unten ab, und man sah von der Baustelle nur einen Streifen.
 *
 * Die Beschriftung liegt jetzt unter dem Bild statt als schwarzer Balken
 * darüber: Das Bild bleibt ganz, und die Liste wirkt ruhiger.
 */
.filmliste {
  display: flex;
  flex-direction: column;
  gap: .9rem;
  padding: .9rem;
  overflow-y: auto;
}
/* Seit dem 11.09.2026 ist die Zeitrafferseite einspaltig, und die flache
   Liste steht dort in einer Tafel über die volle Breite statt in einer 22rem
   schmalen Spalte. Als Spalte gezeichnet wären das Vorschaubilder in
   Plakatgröße – deshalb hier ein Raster, das sich die Breite teilt. */
.buehne.ohne-spalte .filmliste {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  align-content: start;
}
.filmliste button {
  flex: 0 0 auto;                        /* sonst quetscht die Spalte die Vorschau platt */
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 0;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  overflow: hidden;
  background: var(--karte);
  cursor: pointer;
  text-align: left;
  transition: border-color .12s ease, transform .12s ease;
}
.filmliste button:hover {
  filter: none;
  border-color: var(--schrift-leise);
  transform: translateY(-1px);
}
.filmliste button.an { border-color: var(--gelb); }
/* Der Gesamtfilm (Klasse `gross`, seiten/zeitraffer.html seit 06.09.2026)
   steht oben und soll sich von den Monatsknöpfen abheben – die Klasse gab
   es, die Regel nicht (Prüfer-Fund 06.09.2026). */
.filmliste button.gross { border-width: 2px; }
.filmliste button.gross .beschriftung { font-size: .9rem; font-weight: 600; }

.filmliste img {
  display: block;
  width: 100%;
  height: auto;                          /* ganzes Bild, nichts beschnitten */
  background: #000;
  flex: 0 0 auto;
}

.filmliste .beschriftung {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .55rem .7rem;
  color: var(--schrift);
  font-size: .82rem;
  font-weight: 500;
  line-height: 1.3;
}
.filmliste button.an .beschriftung { color: var(--gelb-schrift); }
.filmliste .beschriftung .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
.filmliste button.an .beschriftung .sinnbild { color: var(--gelb); }
.filmliste .beschriftung .dauer {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  color: var(--schrift-leise);
  flex: 0 0 auto;
}

/* Die Filmwahl der Zeitrafferseite (11.09.2026).
 *
 * Warum es die Filmliste oben nicht mehr tut: Eine Kamera sammelt im Jahr
 * einen Gesamtfilm, zwölf Monate, zweiundfünfzig Wochen und bis zu vierzehn
 * Tagesfilme – rund achtzig Einträge. Als flache Knopfreihe in einer 22rem
 * schmalen Spalte war das keine Übersicht, sondern eine Rolltreppe. Der
 * Betreiber hat deshalb einen Aufbau bestellt, der von oben nach unten
 * schmaler wird: der Gesamtfilm allein, darunter die Monate als Kacheln, die
 * Wochen erst beim Aufklappen eines Monats, die Tagesfilme getrennt ganz
 * unten. Eine Unterteilung nach Jahren gibt es nicht – die Jahreszahl steht
 * an der Kachel selbst (geänderte Vorgabe vom selben Tag).
 *
 * Das Raster braucht Breite – eine Reihe Kacheln mit Vorschaubild
 * passt in keine Seitenspalte. Die Seite ist darum einspaltig
 * (`buehne ohne-spalte`): oben der Abspieler, darunter die Wahl. Die
 * Filmliste bleibt trotzdem stehen: Kameras, die noch über den
 * Livebild-Dienst laufen, liefern nur Tagesfilme und keine `art` – für sie
 * bleibt es bei der flachen Liste wie bisher.
 */
/* Der Abspieler steht oben und wird nach einem Tipp in die Wahl angesprungen
   (seiten/zeitraffer.html). Die Kopfzeile klebt (`.kopf`, `position: sticky`)
   und verdeckte ihn dabei zur Hälfte; dieser Abstand hält ihren Platz frei.
   Auf dem Handy ist sie höher – siehe weiter unten. */
.buehne:has(.filmwahl) > .tafel:first-child { scroll-margin-top: 5rem; }

.filmwahl { display: flex; flex-direction: column; gap: 1.2rem; padding: 1.15rem; }
.filmwahl .abschnitt { display: flex; flex-direction: column; gap: .6rem; }
.filmwahl .abschnittskopf {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .82rem;
  font-weight: 500;
  color: var(--schrift-leise);
}
.filmwahl .abschnittskopf .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
/* Der Hinweis auf die Frist der Tagesfilme steht in derselben Zeile wie die
   Überschrift, nicht darunter: Er gehört zur Sache, nicht zur Liste. */
.filmwahl .abschnittskopf .frist { margin-left: auto; text-align: right; }

/* Der Gesamtfilm: eine breite Karte, allein in der ersten Reihe. Es gibt
   immer genau einen, er wird monatlich überschrieben, und er ist das, was
   ein Bauherr herzeigt – gesucht werden soll er nicht. */
/* `gap`, `justify-content`, `white-space` und die gelbe Farbe kommen aus der
   allgemeinen Knopfregel weiter oben – eine Kachel ist aber kein Knopf mit
   Beschriftung, sondern ein Bild mit Text darunter. Deshalb stehen sie hier
   ausdrücklich noch einmal. */
/* ------------------------------------------------ Bühne der Zeitrafferseite
 *
 * Links die Monate, rechts der Abspieler – so gewünscht am 11.09.2026:
 * „oben immer den Gesamtzeitraffer … das Fenster rückst du nach rechts …
 * auf der linken Seite die Monatszeitraffer … und unten drunter die
 * Wochenzeitraffer.“
 *
 * Warum der Abspieler im Quelltext trotzdem zuerst steht: Er ist die
 * Hauptsache. Auf dem Handy liegt beides untereinander, und dort gehört er
 * nach oben – die Reihenfolge im Quelltext entscheidet das, die Spalte
 * entscheidet `grid-column`. Eine Spalte, die im Quelltext vorn stünde und
 * per Raster nach hinten wanderte, verdrehte auch die Tastaturreihenfolge.
 *
 * Die linke Spalte ist schmaler als die 22rem der allgemeinen `.buehne`:
 * Die Monatskacheln sollen darin zu zweit nebeneinander stehen, mit
 * Vorschaubild. */
.zr-buehne {
  display: grid;
  grid-template-columns: minmax(13rem, 20rem) minmax(0, 1fr);
  gap: 1.4rem;
  align-items: start;
  /* Luft für den Schatten des Abspielers – ohne sie sähe er unten
     abgeschnitten aus, und genau das soll er nicht. */
  margin-bottom: 1.4rem;
}
.zr-buehne > .zr-schwebt { grid-column: 2; grid-row: 1; }
/* Die Wochen rechts unter den Abspieler, die Monatsspalte links über beide
   Zeilen. Vorher lag unter dem Abspieler eine schwarze Fläche, so hoch wie
   die Monatsspalte lang war – bei einer Kamera mit zwölf Monaten ein halber
   Bildschirm (12.09.2026: „das ist ja verlorener Platz"). */
.zr-buehne > .zr-wochen { grid-column: 2; grid-row: 2; }
.zr-buehne > .zr-monate { grid-column: 1; grid-row: 1 / span 2; }
/* Kameras am alten Livebild-Dienst kennen nur Tagesfilme: keine Monate,
   nichts für die linke Spalte. Dann bekommt der Abspieler die ganze Breite. */
.zr-buehne.allein { grid-template-columns: minmax(0, 1fr); }
.zr-buehne.allein > .zr-schwebt { grid-column: 1; }

/* Das schwebende Fenster. Kein Rahmen, dafür ein weit tragender Schatten:
   Ein Rahmen bindet die Fläche an die Seite, ein Schatten hebt sie ab. */
.zr-schwebt {
  border-color: transparent;
  box-shadow: var(--schatten-schwebend);
}

/* Die Monatsspalte: engere Kacheln als in der vollen Breite, damit auch in
   13rem noch zwei nebeneinander stehen – mit Vorschaubild und Monatsnamen. */
.zr-monate .filmwahl { padding: .9rem; gap: .9rem; }
.zr-monate .monatsblock {
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  gap: .5rem;
}
/* Der Gesamtfilm steht hier als Rückweg, nicht als Hauptdarsteller – er
   läuft ja schon nebenan. In der schmalen Spalte liegt sein Bild deshalb
   über dem Text statt daneben: 14rem Vorschau neben Text ginge sich hier
   nicht aus, und so sieht er aus wie die Monatskacheln darunter. */
.zr-monate .gesamtknopf { flex-direction: column; align-items: stretch; }
.zr-monate .gesamtknopf img { flex: 0 0 auto; width: 100%; }
.zr-monate .gesamtknopf .text { padding: .55rem .7rem; gap: .15rem; }
.zr-monate .gesamtknopf .name { font-size: .95rem; }

/* Die Fläche des Abspielers – so groß wie der Film, der darin laufen wird.
 *
 * Bis zum 12.09.2026 galt das nur auf dem Handy. Am Rechner blieb es bei
 * `width: fit-content` und `max-height: 68vh` am Video: Das Standbild stand
 * dann kleiner in einer breiten schwarzen Fläche, links und rechts Rand –
 * und sobald der Film lief, sprang er auf eine andere Größe. „Mach die
 * Vorschau gleich so groß wie die Fläche zum Anschauen, die sich dann
 * öffnet."
 *
 * Also: volle Breite der Tafel, die Form aus `--bildform`, sobald der Film
 * seine Maße gemeldet hat (`loadedmetadata`), sonst 16:9. Damit hat die
 * Fläche vor dem Abspielen dieselbe Größe wie danach – nichts springt.
 *
 * `object-fit: contain` und nicht `cover`: Ein Film wird hier nie
 * beschnitten. Deckt sich seine Form mit `--bildform` – und das tut sie,
 * die Zahl kommt ja von ihm –, bleibt ohnehin kein Rand.
 *
 * `grid-template-rows: minmax(0, 1fr)` ist Pflicht: Sonst richtet sich die
 * Rasterzeile nach dem Inhalt statt nach der Fläche, und `height: 100%` am
 * Video meint die Zeile (dieselbe Lehre wie in der Handy-Regel unten).
 *
 * `max-height: 72vh` ist die Notbremse für flache Fenster: Ohne sie ragte
 * der Film auf einem 16:10-Bildschirm unten aus dem Bild. */
.zr-schwebt .bildflaeche {
  width: 100%;
  aspect-ratio: var(--bildform, 16 / 9);
  max-height: 72vh;
  grid-template-rows: minmax(0, 1fr);
}
.zr-schwebt .bildflaeche video,
.zr-schwebt .bildflaeche img {
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: contain;
}

/* Das Wochenfeld: dieselbe Polsterung wie die Monatsspalte, damit beide
   Kanten fluchten. Die Reihe darin bricht um – bei fünf Wochen und der
   halben Seitenbreite stehen sie in zwei Zeilen statt in einer langen. */
.zr-wochen .filmwahl { padding: .9rem; }
/* Je Monat eine Gruppe: Name, Zahl, dann die Reihe. Der gelbe Streifen
   bleibt dem aufgeklappten Monat vorbehalten – bei vierzehn Gruppen wären
   vierzehn gelbe Streifen kein Hinweis mehr, sondern ein Muster. */
.zr-wochen .wochenmonat + .wochenmonat { margin-top: .9rem; }
.zr-wochen .wochenmonatname {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  margin: 0 0 .4rem .2rem; font-size: .9rem;
}
.zr-wochen .wochenreihe { border-left-width: 1px; border-left-color: var(--linie); }
.zr-wochen .wochenmonat.offen .wochenreihe {
  border-left-width: 3px; border-left-color: var(--gelb);
}

/* Der Streifen unter der Bühne: heimatlose Filme und Tagesfilme. Er
   bleibt eingesetzt, statt ebenfalls zu schweben – über der Seite liegt
   genau ein Fenster, sonst hebt sich nichts mehr ab. Die Wochenreihe
   braucht hier keinen gelben Streifen mehr, der sie an eine Kachel bindet:
   Der Monat steht in der Überschrift daneben. */
.zr-unten .wochenreihe { border-left-width: 1px; border-left-color: var(--linie); }

@media (max-width: 900px) {
  /* Untereinander: erst der Abspieler (er steht im Quelltext zuerst), dann
     die Monate, dann die Wochen. */
  .zr-buehne { grid-template-columns: minmax(0, 1fr); }
  .zr-buehne > .zr-schwebt,
  .zr-buehne > .zr-wochen,
  .zr-buehne > .zr-monate { grid-column: 1; grid-row: auto; }
  .zr-monate .monatsblock {
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  }
}

.gesamtknopf {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  width: 100%;
  padding: 0;
  border: 2px solid var(--linie);
  border-radius: var(--rund);
  background: var(--karte);
  color: var(--schrift);
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  white-space: normal;
  transition: border-color .12s ease, transform .14s ease, box-shadow .14s ease;
}
.gesamtknopf:hover {
  filter: none;
  border-color: var(--schrift-leise);
  transform: translateY(-2px);
}
.gesamtknopf.an { border-color: var(--gelb); }
/* Keine feste Höhe und kein `cover`: Bei einer Kachel, die breiter als 16:9
   ist, schnitt das oben und unten ab – dieselbe Lehre wie bei der Filmliste. */
.gesamtknopf img {
  flex: 0 0 14rem;
  width: 14rem;
  height: auto;
  display: block;
  background: #000;
}
.gesamtknopf .text {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .9rem 1.1rem;
  min-width: 0;
}
.gesamtknopf .name {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--schrift);
}
.gesamtknopf.an .name { color: var(--gelb-schrift); }
.gesamtknopf .name .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
.gesamtknopf.an .name .sinnbild { color: var(--gelb); }

/* Eine durchgehende Reihe Monatskacheln, jüngster zuerst (11.09.2026).
   Es sind nicht mehr zwölf Felder je Jahr, sondern so viele Kacheln, wie es
   Monatsfilme gibt – wie viele davon nebeneinander stehen, entscheidet die
   Breite und nicht eine feste Zahl.

   Die aufgeklappte Wochenreihe spannt über alle Spalten und steht damit
   immer unter ihrem Monat, ohne dass die Seite die Spaltenzahl kennen muss.
   Sie schiebt die Kacheln dahinter in die nächste Zeile; in der Zeile des
   offenen Monats bleibt rechts eine Lücke. Die ist uns lieber als eine in
   JavaScript geratene Spaltenzahl, die bei jeder Fensterbreite anders
   falsch läge – vorher standen feste Vierergruppen hier, und auf dem Handy
   mit zwei Kacheln nebeneinander saß die Wochenreihe mitten in der Reihe. */
.monatsblock {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
  gap: .7rem;
}
.monatskachel {
  /* Für die Wochenmarke in der Ecke – siehe unten. */
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0;
  padding: 0;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  background: var(--karte);
  color: var(--schrift);
  overflow: hidden;
  text-align: left;
  white-space: normal;
  cursor: pointer;
  transition: border-color .12s ease, transform .14s ease, box-shadow .14s ease;
}
.monatskachel:hover { filter: none; border-color: var(--schrift-leise); transform: translateY(-2px); }
.monatskachel.an { border-color: var(--gelb); }
.monatskachel.offen { border-color: var(--schrift-leise); }
.monatskachel.an.offen { border-color: var(--gelb); }
.monatskachel img { display: block; width: 100%; height: auto; background: #000; flex: 0 0 auto; }
.monatskachel .beschriftung {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  padding: .5rem .6rem;
  font-size: .82rem;
  font-weight: 500;
  color: var(--schrift);
  line-height: 1.3;
}
.monatskachel.an .beschriftung { color: var(--gelb-schrift); }
.monatskachel .beschriftung .klein { font-weight: 400; color: var(--schrift-leise); }

/* Wie viele Wochen in diesem Monat liegen – und ob sie gerade offen stehen.
 *
 * Warum in der Ecke und nicht in der Textzeile: Dort stand die Angabe bis
 * zum 11.09.2026, und „2.673 Aufnahmen · 4 Wochen ⌄" passt in einer 10,5rem
 * breiten Kachel nicht auf eine Zeile. Sie brach um, die aufgeklappte Kachel
 * wurde eine Zeile höher als ihre Nachbarn, und die Reihe stand schief.
 *
 * Die Marke liegt auf dem Vorschaubild, also auf einem Foto und nicht auf
 * einer Fläche des Themas. Deshalb hier ausnahmsweise feste Farben: Schwarz
 * mit Weiß darauf liest sich über jedem Bild, im hellen wie im dunklen
 * Thema. Eine Farbmarke würde je nach Thema mal auf hellem, mal auf dunklem
 * Foto verschwinden. */
.monatskachel .wochenmarke {
  position: absolute;
  top: .4rem;
  right: .4rem;
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .1rem .45rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, .62);
  color: #fff;
  font-size: .72rem;
  font-weight: 500;
  line-height: 1.5;
  /* Der Klick gehört der Kachel, nicht der Marke. */
  pointer-events: none;
}

/* Die Wochen eines Monats. Bis zum 13.09.2026 stand hier „erst beim
   Aufklappen, nie alle zweiundfünfzig auf einmal" – seither stehen sie
   alle da, aber je Monat in einer eigenen Reihe (`.wochenmonat`), und nur
   die des aufgeklappten Monats trägt den gelben Streifen. */
.wochenreihe {
  /* Über die ganze Breite des Monatsrasters, siehe `.monatsblock`. */
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  padding: .7rem .8rem;
  border: 1px solid var(--linie);
  /* Der gelbe Streifen links bindet die Reihe an den aufgeklappten Monat –
     sonst steht sie da wie ein eigener Abschnitt. */
  border-left: 3px solid var(--gelb);
  border-radius: var(--rund);
  /* Ein Farbton, der in beiden Themen als Vertiefung liest, statt zweier
     Regeln, die im Dunkeln und im Hellen auseinanderlaufen können. */
  background: color-mix(in srgb, var(--schrift-leise) 9%, transparent);
}

/* Ein kleiner Filmknopf: für die Wochen und für die Tagesfilme. Ohne
   Vorschaubild – bei einer feststehenden Kamera zeigt jede Woche dieselbe
   Ansicht, das Bild unterscheidet nichts und kostet Mobilfunkdaten. */
.kleinfilm {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  max-width: 100%;
  padding: .42rem .7rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  background: var(--karte);
  color: var(--schrift);
  font-size: .82rem;
  font-weight: 500;
  /* Ein Wochentitel mit Aufnahmezahl ist auf 375 Punkten breiter als der
     Bildschirm – er darf im Knopf umbrechen, statt hinauszustehen. */
  white-space: normal;
  text-align: left;
  cursor: pointer;
  transition: border-color .12s ease, transform .14s ease, box-shadow .14s ease;
}
.kleinfilm:hover {
  filter: none;
  border-color: var(--schrift-leise);
  transform: translateY(-2px);
}
.kleinfilm.an { border-color: var(--gelb); color: var(--gelb-schrift); }
.kleinfilm .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
.kleinfilm.an .sinnbild { color: var(--gelb); }
.kleinfilm .zahl {
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  color: var(--schrift-leise);
}
.tagesliste { display: flex; flex-wrap: wrap; gap: .5rem; }

/* Der Kopf über dem Abspieler (11.09.2026).
 *
 * Anlass ist ein Satz des Betreibers: „Man weiß ja gar nicht, was man sich
 * anschaut da oben.“ Er hatte recht. Über dem Video stand der Dateititel in
 * Kopfzeilengröße – „Gesamt“, „KW 36 · September 2026“ – und darunter eine
 * Zeile mit Dateiname, Größe und Datum. Das beschreibt eine Datei. Es sagt
 * nicht, was für ein Film das ist und welchen Zeitraum er zeigt.
 *
 * Jetzt steht dort in ganzen Worten „Monatsfilm · August 2026“, groß genug,
 * um es vom Stuhl aus zu lesen, und die Zahlen rücken klein darunter. Der
 * Kopf sitzt an der Stelle der früheren `.standzeile` und trägt deren
 * Aussehen weiter – dunkler Streifen, feine Linie darunter –, damit die Seite
 * dieselbe bleibt. Auf den übrigen Seiten steht `.standzeile` unverändert;
 * nur die Zeitrafferseite hat sie hier abgelöst.
 */
.zr-kopf {
  display: flex;
  align-items: flex-end;
  gap: .9rem;
  padding: .8rem 1.15rem .85rem;
  border-bottom: 1px solid var(--linie-leise);
  background: var(--grund-tief);
}
:root[data-thema="hell"] .zr-kopf { background: var(--karte-hell); }
.zr-kopf .zr-was { min-width: 0; }

/* Art und Zeitraum in einer Zeile, aber umbruchfähig: „Wochenfilm · KW 36,
   31.08.–06.09.2026“ ist auf 375 Punkten länger als der Bildschirm, und ein
   Kopf, der die Antwort abschneidet, wäre keine. */
.zr-art {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 .1rem;
  margin: 0;
  font-family: var(--titelschrift);
  font-size: 1.3rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--schrift);
}
/* Die Art trägt die Farbe, der Zeitraum die Ruhe: Zuerst liest man, was für
   ein Film das ist, dann wann.

   Das Trennzeichen hängt an der Art und erscheint nur, wenn ein Zeitraum
   folgt (`:has`) – beim Gesamtfilm einer Kamera, deren Filme kein lesbares
   Datum tragen, steht dann nur „Gesamtfilm“ da.

   Warum nicht, wie bis zum 11.09.2026, vor dem Zeitraum: Die Überschrift
   bricht auf schmalen Geräten um, und dann stand der Punkt allein am Anfang
   der zweiten Zeile („· KW 36, 31.08.–06.09.2026“). Am Ende der ersten Zeile
   stört er nicht. */
.zr-art .zr-marke { color: var(--gelb-schrift); }
.zr-art:has(.zr-zeit) .zr-marke::after {
  content: "·";
  margin: 0 .5rem;
  color: var(--schrift-leise);
  font-weight: 400;
}

/* Die Fakten darunter. Die Punkte zwischen ihnen setzt die Vorlage nicht mit,
   sondern sie stehen hier – so entsteht keiner vor dem ersten und keiner
   hinter dem letzten, gleichgültig wie viele Angaben ein Film hergibt. */
.zr-fakten {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  margin: .28rem 0 0;
  font-size: .8rem;
  color: var(--schrift-leise);
}
/* Der Punkt hängt hinten an, nicht vorn am nächsten Stück: Die Zeile bricht
   um, und ein vorangestellter Punkt rutschte an den Anfang der neuen Zeile
   (11.09.2026). Hinten am Ende einer Zeile liest er sich wie ein Komma. */
.zr-fakten span:not(:last-child)::after {
  content: "·";
  margin: 0 .45rem;
  color: var(--schrift-leise);
}
/* „2.673 Aufnahmen“ – die Antwort auf die häufigste Rückfrage des Bauherrn,
   und zwar über dem Film und nicht in einer Fußnote. Steht nur da, wo die
   Zahl bekannt ist; bei Tagesfilmen und beim alten Livebild-Dienst steht sie
   nirgends, und dann steht hier nichts. */
.zr-fakten .aufnahmen {
  font-variant-numeric: tabular-nums;
  color: var(--schrift);
  font-weight: 500;
}
/* Der Dateiname ganz rechts und leise: eine technische Angabe, die man sucht,
   wenn man sie braucht, und die sonst niemanden angeht. */
.zr-kopf .dateiname {
  margin-left: auto;
  flex: 0 1 auto;
  font-family: var(--zahlschrift);
  font-size: .72rem;
  color: var(--schrift-leise);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Der mitlaufende Streifen unter dem Abspieler.
 *
 * Drei Punkte hoch und über die ganze Tafelbreite – er soll nicht bedient
 * werden, dafür ist die Leiste im Video da. Er soll aus der Entfernung sagen,
 * wo im Film man ist, auch wenn die Leiste sich gerade ausgeblendet hat.
 *
 * Der Anteil kommt als `--anteil` aus der Seite (seiten/zeitraffer.html); den
 * weichen Übergang zwischen den rund vier Meldungen je Sekunde macht die
 * Überblendung hier, nicht ein Zeitgeber dort. */
.zr-balken {
  position: relative;
  height: 3px;
  background: color-mix(in srgb, var(--schrift-leise) 20%, transparent);
  overflow: hidden;
}
.zr-balken::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--anteil, 0%);
  background: var(--gelb);
  transition: width .25s linear;
}

/* Der große Abspielknopf verschwindet, sobald das Bild läuft, und kommt beim
   Anhalten zurück. `[hidden]` allein reichte dafür nicht: Die Regel weiter
   oben setzt `display: grid`, und die schlägt die des Browsers. */
.abspielknopf[hidden] { display: none; }

/* Die gewählte Kachel ist nicht nur umrandet, sondern liegt in einem weichen
   gelben Ring. Bei acht Monatskacheln nebeneinander war die Linie allein zu
   wenig – man suchte, welche gemeint ist. */
.gesamtknopf.an,
.monatskachel.an,
.kleinfilm.an,
.filmliste button.an {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gelb) 20%, transparent);
}

/* Beim Filmwechsel blendet der Kopf um, statt zu springen. Die Bewegung ist
   klein und kurz; sie soll nur sagen, dass hier etwas anderes steht als eben
   noch. Sie läuft nicht bei jedem Zeichnen – die Seite entscheidet das und
   setzt `zr-frisch` nur, wenn wirklich ein anderer Film im Kopf steht. */
@keyframes zr-einblenden {
  from { opacity: 0; transform: translateY(.35rem); }
  to   { opacity: 1; transform: none; }
}
.zr-kopf.zr-frisch { animation: zr-einblenden .3s ease-out; }

/* Die Wochenreihe fährt auf, statt zu erscheinen – dann sieht man, dass sie
   zu dem Monat darüber gehört und nicht als eigener Abschnitt dazukommt.
   Auch sie läuft nur beim Aufklappen selbst (`zr-auffahrt`, gesetzt in
   seiten/zeitraffer.html), nicht bei jedem Klick auf eine Woche darin.

   `--hoehe` ist die wirkliche Höhe der Reihe; die Seite misst sie, bevor sie
   die Klasse setzt. Das ist der ganze Unterschied zwischen „fährt auf“ und
   „springt auf“: Ein großzügig geschätzter Deckel wäre nach einem Zehntel der
   Zeit erreicht, und die restlichen neun Zehntel liefen an einer Reihe ab,
   die längst offen dasteht. Fehlt das Maß – gemessen wird im Browser, und
   auch das kann einmal nichts hergeben –, gilt der Deckel darunter; dann
   klappt es auf, statt aufzufahren, und das ist kein Schaden. */
@keyframes zr-auffahren {
  from { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0;
         transform: translateY(-.3rem); }
  to   { opacity: 1; max-height: var(--hoehe, 40rem);
         padding-top: .7rem; padding-bottom: .7rem;
         transform: none; }
}
.wochenreihe.zr-auffahrt {
  overflow: hidden;
  animation: zr-auffahren .28s ease-out;
}

/* Wenn der Film nicht kommt. Rot umrandet wie jede Störung im Haus, aber
   ohne Fläche: Die Zeile steht unter dem Standbild, das liegen bleibt. */
.zr-stoerung {
  margin: 0;
  padding: .55rem .8rem;
  border-top: 1px solid var(--linie);
  border-left: 3px solid var(--schlecht);
  font-size: .85rem;
  color: var(--schrift);
}

/* Wer keine Bewegung will, bekommt keine – und verliert dabei nichts. Der
   Streifen bleibt stehen (er ist eine Angabe, keine Zierde), er springt nur
   in Stufen statt zu gleiten; die Kacheln behalten Rahmen und Ring, nur das
   Anheben entfällt. */
@media (prefers-reduced-motion: reduce) {
  .zr-kopf.zr-frisch,
  .wochenreihe.zr-auffahrt { animation: none; }
  .zr-balken::after { transition: none; }
  .gesamtknopf:hover,
  .monatskachel:hover,
  .kleinfilm:hover,
  .filmliste button:hover { transform: none; }
}

@media (max-width: 640px) {
  /* Der Dateiname ist eine technische Angabe und auf 350 Punkten ohnehin nur
     noch „2026-0…mp4“. Art, Zeitraum und die Zahlen bleiben stehen. */
  .zr-kopf .dateiname { display: none; }
  .zr-kopf { padding: .7rem .8rem .75rem; }
  .zr-art { font-size: 1.08rem; }

  /* Engere Kacheln, damit auf 375 Punkten noch zwei nebeneinander stehen –
     eine einzelne Kachel über die ganze Breite wäre ein Plakat je Monat. */
  .monatsblock {
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
    gap: .5rem;
  }
  .filmwahl { gap: .9rem; padding: .7rem; }
  /* Das Bild des Gesamtfilms über den Text statt daneben: 14rem Bild und
     daneben eine Spalte Text ergeben auf 375 Punkten zwei enge Streifen. */
  .gesamtknopf { flex-direction: column; align-items: stretch; }
  .gesamtknopf img { flex: 0 0 auto; width: 100%; }
  .gesamtknopf .text { padding: .6rem .8rem; }
  .gesamtknopf .name { font-size: .95rem; }
}

/* Handy: Abspieler zuerst, Liste danach – als Zeilen, nicht als Kacheln.
 *
 * Unter 1100 Punkten wird die Bühne einspaltig, und die Liste stand in der
 * Reihenfolge des HTML oben: ein 26rem hoher Scrollkasten voller
 * Vorschaubilder, die bei einer Kamera alle gleich aussehen – und erst
 * darunter das Video. Auf 375 Punkten begann es bei etwa 690 von 812.
 * `order` dreht die beiden Rasterfelder; der Kamerastreifen sitzt in der
 * Abspieler-Tafel (wie auf BauCam, Bauarchiv, Live, Wiedergabe) und wandert
 * mit. Nur die Zeitraffer-Bühne (erkannt an ihrer Filmliste): Die Spalten
 * von Bauarchiv und Wiedergabe tragen Datumswahl und Bildraster, das ist
 * eine eigene Frage. */
@media (max-width: 640px) {
  .buehne:has(.filmliste) > .tafel:not(.spalte),
  .buehne:has(.abschnittsliste) > .tafel:not(.spalte) {
    order: -1;
    /* Beim Sprung zum Abspieler nach einem Tipp in die Liste (siehe
       zeitraffer.html) bleibt der klebende Kopf oben frei. */
    scroll-margin-top: 10rem;
  }
  /* Dasselbe für die Filmwahl, die seit dem 11.09.2026 unter dem Abspieler
     steht: Auf dem Handy ist die Kopfzeile höher als die 5rem oben. */
  .buehne:has(.filmwahl) > .tafel:first-child { scroll-margin-top: 10rem; }
  /* Kein Kasten im Kasten: Die Liste ist so lang, wie sie ist – die Seite
     blättert, nicht ein Feld darin. */
  .buehne:has(.filmliste) .spalte { max-height: none; }
  .filmliste { gap: .4rem; padding: .6rem; }
  /* Eine Zeile je Film: Abspielzeichen, Titel, Größe. Das Vorschaubild
     zeigt bei einer Kamera jeden Monat dieselbe Ansicht – es unterscheidet
     nichts und kostet je Film rund 200 Punkte Höhe und Mobilfunkdaten
     (`loading="lazy"` holt ein ausgeblendetes Bild gar nicht erst). */
  .filmliste img { display: none; }
  .filmliste .beschriftung {
    flex: 1 1 auto;          /* füllt die 44 Punkte des Knopfs, Text mittig */
    padding: .6rem .8rem;
    font-size: .9rem;
  }
  .filmliste button:hover { transform: none; }
  /* Wo der Dateiname auf dem Handy weggeht, steht seit dem 11.09.2026 beim
     Kopf über dem Abspieler (`.zr-kopf .dateiname`). Die Zeitrafferseite
     trägt keine `.standzeile` mehr – die Regel, die hier stand, zielte ins
     Leere und ist deshalb weg. */

  /* Bauarchiv: die kleinen Bilder **links** neben dem großen, wie am
     Schreibtisch – nur enger (Wunsch vom 02.09.2026, „nicht unten"). Eine
     Leiste von 7,4rem mit einer Aufnahme je Reihe und der Uhrzeit darunter;
     rechts das Bild mit Kopf und Knöpfen. Die Leiste blättert für sich. */
  .buehne:has(.bildraster) {
    grid-template-columns: 7.4rem minmax(0, 1fr);
    gap: .5rem;
    align-items: start;
  }
  .buehne:has(.bildraster) .spalte { max-height: none; overflow: visible; }
  .buehne:has(.bildraster) .spaltenkopf {
    flex-direction: column; align-items: stretch; gap: .3rem; padding: .45rem .4rem;
  }
  .buehne:has(.bildraster) .spaltenkopf h2,
  .buehne:has(.bildraster) .spaltenkopf > .sinnbild { display: none; }
  .buehne:has(.bildraster) .datumsknopf {
    width: 100%; justify-content: center; padding: .3rem .3rem; font-size: .72rem;
  }
  .buehne:has(.bildraster) .datumsknopf .sinnbild { display: none; }
  /* Der Kalender ist breiter als die Leiste – er legt sich über das Bild. */
  .buehne:has(.bildraster) .kalender { left: 0; width: min(18rem, calc(100vw - 2rem)); z-index: 6; }
  .buehne:has(.bildraster) .bildraster {
    grid-template-columns: 1fr;
    grid-auto-rows: max-content;
    gap: .35rem;
    padding: .4rem;
    max-height: calc(100vh - 13rem);
  }
  .buehne:has(.bildraster) .bildraster img { height: auto; aspect-ratio: 16 / 9; }
  .buehne:has(.bildraster) .bildraster .stempel { font-size: .6rem; padding: .15rem .25rem; text-align: center; }
  /* Datum weg, Uhrzeit bleibt – das Datum steht oben im Knopf. */
  .buehne:has(.bildraster) .bildraster .tagteil { display: none; }
  .buehne:has(.bildraster) .tafelkopf { flex-wrap: wrap; gap: .3rem .5rem; padding: .5rem .6rem; }
  .buehne:has(.bildraster) .tafelkopf h1 { font-size: .92rem; }
  .buehne:has(.bildraster) .standzeile { padding: .35rem .6rem; font-size: .68rem; }
  /* Blättern am Telefon (23.09.2026, „Die Anordnung passt nicht"): Neben
     der Bildleiste hat die Tafel rund 245 Punkte – „früher", „Bild 8 von
     135" und „später" brachen um, und „später" stand allein in einer
     dritten Zeile. Jetzt eine Reihe, die nie bricht: Pfeil, Zähler, Pfeil;
     die Wörter fallen weg, der Titel am Knopf bleibt. */
  .buehne:has(.bildraster) .standzeile .blaettern {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: .5rem; flex-wrap: nowrap;
  }
  .buehne:has(.bildraster) .standzeile .blaettern .wort { display: none; }
  .buehne:has(.bildraster) .standzeile .blaettern button {
    padding: .3rem .7rem; min-width: 2.6rem; justify-content: center;
  }
  .buehne:has(.bildraster) .standzeile .blaettern #blaetterstand { white-space: nowrap; }
  .buehne:has(.bildraster) .tafelfuss { padding: .4rem .5rem; }
}

/* Wartefläche über dem Video.
 *
 * Vorher lag sie halbdurchsichtig über einem Abspieler, dessen Bedienleiste
 * schon da war, aber nichts tat – Knopf, Regler und Zeitleiste schienen durch
 * und überlagerten den Text. Das sah nach Fehler aus, nicht nach Warten.
 *
 * Jetzt: Die Bedienleiste kommt erst mit dem Bild (siehe wiedergabe.html),
 * dahinter steht das Standbild der Stelle, und darüber liegt ein weicher
 * Schleier mit Ring und zwei Zeilen. Der Schleier ist immer dunkel, deshalb
 * ist die Schrift hier fest hell – sonst wäre sie im hellen Thema grau auf
 * grau. */
.ueberdeckung {
  position: absolute; inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: .75rem;
  padding: 1.5rem;
  text-align: center;
  background: radial-gradient(ellipse at center,
              rgba(10, 11, 12, .82) 0%, rgba(10, 11, 12, .94) 100%);
  backdrop-filter: blur(2px);
  color: rgba(255, 255, 255, .72);
}
.ueberdeckung p { margin: 0; max-width: 24rem; position: relative; }

/* ---------------------------------------------------------- Warten
 *
 * Ein schwarzer Kasten mit einem Kringel sagt „hier ist nichts". Dabei
 * liegt ein aktuelles Bild dieser Kamera längst vor – dieselbe Datei, die
 * BauCam zeigt. Weich und abgedunkelt dahinter sagt es „die Kamera steht,
 * das Video kommt gleich", und der Blick hat etwas, worauf er ruhen kann.
 *
 * Deshalb ist die Decke hier viel dünner als sonst: Abgedunkelt wird das
 * **Bild**, nicht die Fläche darüber. Sonst käme zweimal Dunkelheit
 * übereinander, und das Bild wäre wieder nicht zu sehen.
 */
.ueberdeckung.warten {
  background: radial-gradient(ellipse at center,
              rgba(10, 11, 12, .30) 0%, rgba(10, 11, 12, .78) 100%);
  backdrop-filter: none;
  overflow: hidden;
}
/* **Nicht `> *`.** Das Bild liegt absolut, und `position: relative` hier
 * hätte es zurück in den Fluss geholt: Es wurde zur ersten Zeile des
 * Rasters und schob Kringel und Text an den unteren Rand. Genau so sah es
 * beim ersten Anlauf aus. */
.ueberdeckung.warten > *:not(.wartegrund) { position: relative; z-index: 1; }

.ueberdeckung.warten p,
.ueberdeckung.warten .hauptzeile {
  /* Über einem Bild braucht Schrift eine Kante, sonst verschwindet sie in
   * einer hellen Stelle des Bildes. */
  text-shadow: 0 1px 3px rgba(0, 0, 0, .85), 0 0 14px rgba(0, 0, 0, .5);
}

.wartegrund {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* Der Maßstab schneidet die Ränder weg, die das Weichzeichnen ausfranst –
   * ohne ihn läge ein heller Saum um das ganze Bild. */
  transform: scale(1.1);
  /* Stärker abgedunkelt und weniger weich als beim ersten Anlauf: Das Bild
   * soll den Blick tragen, nicht mit der Schrift darüber konkurrieren. */
  filter: blur(9px) brightness(.34) saturate(.8);
}


/* Der Balken misst nichts – er sagt „es geht voran".
 *
 * In sechs Sekunden auf neunzig Prozent, dann kriecht er. Wie lange die
 * Kamera braucht, weiß vorher niemand; gemessen wurden 5,8 Sekunden bis
 * zum ersten Videostück. Ein Balken, der bei hundert Prozent stehen bleibt,
 * während nichts kommt, wäre eine Lüge – dieser bleibt ehrlich unfertig,
 * bis das Bild wirklich da ist. */
.wartebalken {
  width: min(19rem, 74%); height: 5px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .06);
  overflow: hidden;
  margin-top: .45rem;
}
/* Zwei Lagen übereinander, und beide haben eine Aufgabe.
 *
 * Die **Breite** sagt, wie weit es ist – ehrlich unfertig, siehe oben.
 * Das **wandernde Licht** sagt, dass überhaupt noch etwas passiert. Das
 * war der Mangel des ersten Anlaufs (03.09.2026 gemeldet): In der
 * Kriechphase ändert sich die Breite über zwanzig Sekunden um acht
 * Prozent – für das Auge steht der Balken still, und ein stehender Balken
 * liest sich wie ein hängendes Programm. Das Licht läuft weiter, ohne
 * einen Fortschritt zu behaupten, den es nicht gibt.
 *
 * Der Lichtstreifen liegt als zweiter Hintergrund über der Füllung; nur
 * seine Position wird bewegt, nicht die des Farbverlaufs darunter. */
.wartebalken::after {
  content: "";
  display: block; height: 100%; width: 0;
  border-radius: inherit;
  background:
    linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .5) 50%,
                    transparent 100%) -40% 0 / 45% 100% no-repeat,
    linear-gradient(90deg, rgba(214, 220, 61, .5), var(--gelb));
  box-shadow: 0 0 10px rgba(210, 215, 0, .45);
  animation: warteschritt 24s cubic-bezier(.15, .85, .2, 1) forwards,
             wartelicht 1.5s linear infinite;
}
@keyframes warteschritt {
  0%   { width: 0; }
  25%  { width: 90%; }     /* die ersten sechs Sekunden */
  100% { width: 98%; }     /* danach nur noch kriechen */
}
@keyframes wartelicht {
  to { background-position: 140% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* Kein Wandern, kein Wachsen – aber sichtbar, dass etwas läuft. */
  .wartebalken::after {
    animation: none; width: 40%;
    background: linear-gradient(90deg, rgba(214, 220, 61, .5), var(--gelb));
  }
}

.ueberdeckung .hauptzeile {
  color: #fff;
  font-size: 1.02rem;
  font-weight: 500;
  letter-spacing: .01em;
}
.ueberdeckung .klein { font-size: .84rem; line-height: 1.45; }
.ueberdeckung .sinnbild { color: var(--gelb); }

/* Der Ring dreht sich – ohne das sieht ein Aufbau, der ein paar Sekunden
 * dauert, aus wie ein Programm, das nichts tut. Ein starres Sinnbild genügt
 * dafür nicht. */
/* Der Ladekringel steht an drei verschiedenen Orten, und die haben nicht
 * dieselbe Farbe:
 *
 * * in einer Karte (`.leer` – Downloads, Wetterstation) – die folgt dem
 *   gewählten Thema, ist im Hellen also weiß;
 * * in einer Bildfläche (`.platzhalter`, `.ueberdeckung`) – die bleibt
 *   **auch im hellen Thema schwarz**, weil dort Kamerabilder stehen.
 *
 * Fest verdrahtet war `rgba(255,255,255,.16)` mit gelbem Kopf: auf Schwarz
 * richtig, auf einer weißen Karte unsichtbar. Deshalb zwei Variablen, die
 * am Ort gesetzt werden – die Regel selbst bleibt eine.
 */
/* ------------------------------------------------------- Tagesverlauf
 *
 * Ein Balken je Tag. Vorher ein `<svg>` mit `preserveAspectRatio="none"`:
 * Auf einem breiten Bildschirm wurde es auf mehr als das Doppelte in die
 * Breite gezogen und auf 170 Pixel gedrückt – die Balken wurden zu
 * Stummeln, die Beschriftung in die Breite verzerrt.
 *
 * Die Höhe wächst mit dem Fenster, aber in Grenzen: Ein Diagramm, das den
 * halben Bildschirm füllt, ist so wenig lesbar wie eines von 170 Pixeln.
 */
.tagesverlauf { display: flex; flex-direction: column; gap: .35rem; }
.tagesreihe {
  display: flex;
  /* **`stretch`, nicht `flex-end`.** Mit `flex-end` schrumpfen die Spalten
   * auf Inhaltshöhe – und „45 Prozent davon" ist null. Die Säulen blieben
   * unsichtbar. Nach unten gedrückt werden sie in der Spalte selbst. */
  align-items: stretch;
  gap: 2px;

  height: clamp(140px, 22vh, 260px);
  border-bottom: 1px solid var(--linie);
  padding-bottom: 1px;
}
.tagesachse { display: flex; gap: 2px; }
.tagesreihe .tagesspalte,
.tagesachse .tagesspalte {
  flex: 1 1 0;
  min-width: 0;                  /* sonst wachsen breite Beschriftungen */
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.tagesachse .tagesspalte small {
  font-size: .72rem;
  color: var(--schrift-leise);
  white-space: nowrap;
}
.saeule {
  width: 100%;
  min-height: 2px;               /* ein Tag mit Nutzung bleibt sichtbar */
  border-radius: 3px 3px 0 0;
  background: var(--gelb);
}
.tagesspalte.wochenende .saeule { background: var(--schrift-leise); opacity: .55; }
.tagesspalte.ohne .saeule {
  /* Null ist nicht wenig: kein Mindestbalken, nur ein Strich auf der Achse. */
  min-height: 0; height: 1px !important;
  background: var(--linie);
}
.tagesspalte:hover .saeule { filter: brightness(1.15); }

/* Ein gekappter Tag: Er ragt über die Achse hinaus, und man soll es sehen.
 * Die gezackte Oberkante sagt „hier geht es weiter"; der Wert steht daneben,
 * damit niemand raten muss, wie weit. */
.tagesspalte.gekappt .saeule {
  position: relative;
  border-radius: 0;
  -webkit-mask-image: linear-gradient(#000 0 0);
  background:
    repeating-linear-gradient(-45deg,
      var(--gelb) 0 4px, color-mix(in srgb, var(--gelb) 55%, transparent) 4px 8px)
      0 0 / 100% 10px no-repeat,
    var(--gelb);
}
.tagesspalte.gekappt.wochenende .saeule {
  background:
    repeating-linear-gradient(-45deg,
      var(--schrift-leise) 0 4px, transparent 4px 8px) 0 0 / 100% 10px no-repeat,
    var(--schrift-leise);
}
.tagesspalte.gekappt .saeule em {
  position: absolute; left: 50%; top: 12px;
  transform: translateX(-50%) rotate(-90deg);
  transform-origin: center;
  font-style: normal; font-size: .68rem; font-weight: 600;
  color: var(--auf-gelb);
  white-space: nowrap;
}


.ladekreis {
  width: 38px; height: 38px;
  border: 3px solid var(--kreisbahn);
  border-top-color: var(--kreisfarbe);
  border-radius: 50%;
  animation: kreisel .85s linear infinite;
}
/* Wo es dunkel bleibt, egal welches Thema gewählt ist. */
.bildflaeche .ladekreis,
.ueberdeckung .ladekreis,
.platzhalter .ladekreis {
  --kreisbahn: rgba(255, 255, 255, .18);
  --kreisfarbe: #d2d700;
}

@keyframes kreisel { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ladekreis { animation-duration: 2.4s; }
}

/* Aufnahmeknopf: ruhiger Punkt, im Betrieb rot und pulsend. Rot bleibt hier
 * dem Zustand vorbehalten – wie das Gelb sonst der Bedienung. */
.aufnahme { gap: .5rem; }
.aufnahme .punkt {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--schrift-leise);
  flex: 0 0 auto;
}
.aufnahme.laeuft { border-color: #c0392b; color: #e05a4a; }
.aufnahme.laeuft .punkt { background: #e04030; animation: blinken 1.1s ease-in-out infinite; }
@keyframes blinken { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .aufnahme.laeuft .punkt { animation: none; }
}

.abspielknopf {
  position: absolute;
  display: grid;
  place-items: center;
  width: 74px; height: 74px;
  border-radius: 50%;
  background: rgba(210, 215, 0, .92);
  color: var(--auf-gelb);
  border: 0;
  cursor: pointer;
}
.abspielknopf:hover { filter: brightness(1.1); }

/* ------------------------------------------------------------ Wiedergabe */

.zeitleiste {
  position: relative;
  height: 46px;
  margin: 0 1rem 1rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  overflow: hidden;
  cursor: pointer;
}
.zeitleiste .abschnitt {
  position: absolute;
  top: 8px;
  height: 18px;
  background: rgba(210, 215, 0, .55);
  border-radius: 2px;
}
.zeitleiste .abschnitt.bewegung { background: rgba(215, 90, 30, .6); }
.zeitleiste .marke {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: var(--schrift);
}
.zeitleiste .stunden {
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  display: flex;
  justify-content: space-between;
  padding: 0 .3rem;
  font-size: .62rem;
  color: var(--schrift-leise);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.abschnittsliste { list-style: none; margin: 0; padding: 0; overflow-y: auto; }
.abschnittsliste li { flex: 0 0 auto; }
.abschnittsliste li {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .55rem .8rem;
  border-bottom: 1px solid var(--linie-leise);
  font-size: .85rem;
}
.abschnittsliste li:hover { background: var(--karte-hell); }
.abschnittsliste .spanne { display: flex; flex-direction: column; margin-right: auto; }
.abschnittsliste .spanne strong { font-variant-numeric: tabular-nums; font-weight: 600; }
.abschnittsliste .spanne span { color: var(--schrift-leise); font-size: .74rem; }
.ausloeser {
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 999px;
  padding: .1rem .55rem;
  font-size: .7rem;
  font-weight: 600;
  color: var(--orange);
}
.ausloeser.dauer { color: var(--schrift-leise); }

/* ------------------------------------------------------------- Downloads */

.tabelle { width: 100%; border-collapse: collapse; font-size: .9rem; }
.tabelle th {
  text-align: left;
  color: var(--schrift-leise);
  font-weight: 500;
  font-size: .76rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--linie);
  padding: .55rem .85rem;
}
/* Mehr Luft in den Zellen: Eine Tabelle mit fünf Reihen soll nicht
 * gedrängt wirken. Die Trennlinie tritt zurück, der Zeilen-Hover trägt
 * die Gliederung. */
.tabelle td { padding: .7rem .85rem; border-bottom: 1px solid var(--linie-leise); vertical-align: middle; }
.tabelle tr:hover td { background: var(--karte-hell); }
.tabelle .rechts { text-align: right; }
/* Eine stillgelegte Person tritt zurück, verschwindet aber nicht – sie hat
 * ihre Zahlen ja erarbeitet. Derselbe Kniff wie bei `.personenliste
 * li.gesperrt` und `.kontaktkarte[data-still]`. */
.tabelle tr.stillgelegt { opacity: .55; }

/* Eine Zelle, in der Zahl und Einheit zusammengehören. „460,4" in einer
   Zeile und „GB" in der nächsten liest sich als zwei Angaben; bei einer
   Tabelle voller Größen springt das Auge dann bei jeder zweiten Zeile
   (13.09.2026, Seite „Speicher"). */
.nichtumbrechen { white-space: nowrap; }

/* Die Zeile, die zu der darüber gehört: die Kameras unter ihrer Baustelle
   (Seite „Speicher"). Ohne Trennlinie nach oben, sonst sähe es aus wie
   ein eigener Eintrag. */
.tabelle tr.unterzeile td { border-top: none; }

/* Die Aufnahmen eines Tages als Zeichengrund („Verpixelung Foto",
   14.09.2026). Eine waagerecht rollende Reihe kleiner Kacheln, je Kachel
   das Bild und darunter die Uhrzeit.

   Die Kachel ist ein Knopf, und die Hausknöpfe sind `inline-flex` in einer
   Zeile – ohne diese Regeln standen Bild und Uhrzeit NEBENEINANDER, und
   die Uhrzeit las sich als Beschriftung des nächsten Bildes. Deshalb
   Spaltenrichtung, kein Knopfgrund, kein Knopfabstand; gewählt ist, was
   den gelben Rahmen trägt. */
.grundreihe {
  display: flex; gap: .5rem; overflow-x: auto;
  padding: .5rem .2rem .6rem; scrollbar-width: thin;
}
.grundkachel {
  flex: 0 0 auto; flex-direction: column; align-items: center; gap: .15rem;
  padding: 3px; border: 2px solid transparent; border-radius: 8px;
  background: none; color: var(--schrift-leise);
  font: 500 .74rem var(--textschrift); letter-spacing: 0; line-height: 1.3;
}
.grundkachel:hover { filter: none; border-color: var(--linie); }
.grundkachel.an { border-color: var(--gelb); color: var(--gelb-schrift); }
.grundkachel img { display: block; width: 104px; height: auto; border-radius: 5px; }
.grundkachel .uhr { font-variant-numeric: tabular-nums; }

/* Der Fuß unter dem Zeichenbild („Verpixelung Foto", 14.09.2026).

   Ein Abstandsmaß für den ganzen Block statt vier verschiedener
   Inline-Ränder: Der Knopf bekommt Luft nach oben, die Auskunftszeilen
   darunter stehen in gleichem Abstand, und die Bildauswahl setzt sich mit
   einer Linie ab. Vorher klebte alles in einem 90 Punkte hohen Streifen
   aneinander. */
.bildfuss {
  margin-top: 1.1rem;
  display: flex; flex-direction: column; gap: .85rem;
}
/* Knopf und sein Erklärsatz gehören zusammen – der Satz steht unter dem
   Knopf, linksbündig mit ihm. Nebeneinander zog er den Knopf an den Rand
   einer breiten Reihe und riss auf schmalen Schirmen um. */
.knopfmitsatz { display: flex; flex-direction: column; align-items: flex-start; gap: .4rem; }
.knopfmitsatz > span { max-width: 34rem; }
.bildstand { display: flex; flex-direction: column; gap: .3rem; }
.bildstand p { margin: 0; }
.bildstand p:empty { display: none; }
.grundwahl {
  margin-top: 1.3rem; padding-top: 1.1rem;
  border-top: 1px solid var(--linie);
}

/* Ein Knopf IM Eingabefeld – das Auge am Passwort (14.09.2026).

   Der Rahmen traegt die Stellung, das Feld bekommt rechts so viel Platz,
   dass der Text nicht unter dem Knopf verschwindet. Der Knopf selbst legt
   den Hausstil ab: kein gelber Grund, keine Polsterung, keine Wölbung – er
   ist ein Zeichen im Feld und kein Knopf neben dem Feld. */
.feldmitknopf { position: relative; display: block; }
.feldmitknopf > input { padding-right: 2.6rem; }
.augenknopf {
  position: absolute; top: 0; bottom: 0; right: .28rem;
  width: 2.1rem; padding: 0; border: 0; border-radius: var(--rund);
  background: none; color: var(--schrift-leise);
  display: inline-flex; align-items: center; justify-content: center;
}
.augenknopf:hover { filter: none; color: var(--schrift); background: none; }
.augenknopf[aria-pressed="true"] { color: var(--gelb-schrift); }
.augenknopf:focus-visible { outline: 2px solid var(--gelb); outline-offset: -2px; }

.balken {
  position: relative;
  height: 6px;
  width: 8rem;
  background: var(--linie);
  border-radius: 3px;
  overflow: hidden;
}
.balken i { position: absolute; inset: 0 auto 0 0; background: var(--gelb); border-radius: 3px; }
.fertig { display: inline-flex; align-items: center; gap: .35rem;
          color: var(--gut); font-weight: 600; font-size: .8rem; }

/* Der Fortschritt eines Rechenlaufs auf „Verpixelung Foto anpassen"
 * (11.09.2026). Ein Balken, die Zahl daneben, rechts die Restzeit – in
 * einer Zeile, weil er unter einer Knopfreihe steht und nicht ihr Thema
 * ist. Der Balken darf hier breiter werden als die 8rem von `.balken`:
 * Bei 14.000 Bildern soll man ein Prozent auch sehen. */
.rechnenfortschritt {
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  margin: .7rem 0 0;
}
.rechnenfortschritt .balken { width: 14rem; height: 8px; flex: 0 1 14rem; }
.rechnenfortschritt .balken i { transition: width .4s ease; }
.rechnenfortschritt strong { font-variant-numeric: tabular-nums;
                             font-size: .85rem; min-width: 3.2rem; }

/* ---------------------------------------------------------- Wetterstation
 *
 * Eine Kachel je Modul der Station – Basisstation, Außenmodul, Regenmesser,
 * Windmesser –, darin die Größen, die dieses Modul misst. Aufbau wie in der
 * Netatmo-App, damit sich beides deckt.
 */

.modulkacheln {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.modulkachel {
  display: flex;
  flex-direction: column;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  padding: .9rem 1rem 1rem;
}
/* Heißt `messkopf` und nicht `kopf`: So heißt die Kopfzeile der Seite, und die
 * trägt schwarzen Grund samt `position: sticky`. */
.modulkachel .messkopf {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .88rem;
  margin-bottom: .8rem;
}
.modulkachel .messkopf .sinnbild { color: var(--schrift-leise); }
.modulkachel .messkopf strong { font-weight: 600; }
.batterie { display: flex; }
.batterie.gut { color: rgba(100, 192, 122, .55); }
.batterie.schlecht { color: var(--schlecht); }

/* Ein stummes Modul bleibt stehen und wird nicht ausgeblendet: Dass ein Wert
 * fehlt, ist selbst eine Auskunft – und eine Null hieße Windstille. */
.modulkachel.stumm { border-style: dashed; }
.modulkachel .keine {
  margin: 0;
  padding: .9rem 0;
  color: var(--schrift-leise);
  font-style: italic;
  font-size: .9rem;
}

.werteliste { display: flex; flex-wrap: wrap; gap: .9rem 1.4rem; }
.messwert { display: flex; flex-direction: column; }
.messwert .zahl {
  display: flex;
  align-items: center;
  gap: .35rem;
  font: 600 1.5rem/1.1 var(--titelschrift);
  font-variant-numeric: tabular-nums;
}
.messwert .zahl small { font-size: .78rem; font-weight: 500; color: var(--schrift-leise); }
.messwert .was { font-size: .74rem; color: var(--schrift-leise); margin-top: .15rem; }

/* Ampel für CO₂ – grün, gelb, rot. Das ist ein Zustand, deshalb ausdrücklich
 * nicht die Hausfarbe. */
.ampel { width: 9px; height: 9px; border-radius: 50%; display: block; }
.ampel.gut { background: var(--gut); }
.ampel.achtung { background: var(--achtung); }
.ampel.schlecht { background: var(--schlecht); }
/* „aus" ist ruhend oder erledigt – kein Zustand, der eine Farbe verdient
 * (05.09.2026, Tafel „Betrieb"). Derselbe Wert wie `.punkt.unbekannt`;
 * Vorlage und Skript setzen nur `ampel` plus das Zustandswort, kein
 * Inline-Stil. */
.ampel.aus { background: rgba(139, 141, 133, .5); }

.modulfuss { margin-top: auto; padding-top: .8rem; font-size: .74rem; color: var(--schrift-leise); }

.verlauf { display: block; width: 100%; height: 30px; margin-top: .6rem; }

/* Anleitung, die dauerhaft auf der Seite steht.
 *
 * Nicht als Hinweis, der nach dem ersten Mal verschwindet: Wer eine Station
 * alle paar Wochen einbindet, weiß es beim zweiten Mal nicht mehr. Also
 * bleibt sie da – ruhig genug, dass sie im Weg nicht stört. */
.anleitung .schritte { padding-left: 1.2rem; margin: .6rem 0 0; }
.anleitung .schritte > li {
  margin-bottom: 1.1rem;
  padding-left: .3rem;
  line-height: 1.55;
}
.anleitung .schritte > li > strong { color: var(--schrift); }
.anleitung .schritte p { margin: .3rem 0 .4rem; }
.anleitung .schritte ol[type="a"] {
  padding-left: 1.1rem;
  margin: .4rem 0 0;
  color: var(--schrift-leise);
  font-size: .88rem;
}
.anleitung .schritte ol[type="a"] li { margin-bottom: .35rem; }
.anleitung .linkfeld { margin-top: .3rem; }

/* Werte, die zur Technik gehören, nicht zum Wetter: die Basisstation im
 * Bauwagen. Eine Zeile statt einer Kachel – da, aber untergeordnet. */
.nebenwerte {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  flex-wrap: wrap;
  padding: .7rem 1rem;
  margin-bottom: 1.25rem;
  border: 1px solid var(--linie-leise);
  border-radius: var(--rund);
  background: var(--karte);
}
.nebenwerte > .klein { display: inline-flex; align-items: center; gap: .4rem; }
.nebenwerte em { font-style: normal; opacity: .7; }
.nebenwert {
  display: inline-flex;
  align-items: baseline;
  gap: .25rem;
  font-variant-numeric: tabular-nums;
}
.nebenwert strong { font-weight: 500; font-size: .95rem; }
.nebenwert small { color: var(--schrift-leise); font-size: .72rem; }
.nebenwert .was { color: var(--schrift-leise); font-size: .78rem; margin-left: .2rem; }
.nebenwert .ampel { margin-right: .1rem; }

.diagrammtafel {
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  padding: 1rem 1.1rem 1.25rem;
  margin-bottom: 1.25rem;
}
.diagrammkopf { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem; flex-wrap: wrap; }
.diagrammkopf h2 { margin: 0; margin-right: auto; font-size: .95rem; }

.urteile { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; }
.urteil {
  display: flex;
  gap: .75rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-left: 3px solid var(--gut);
  border-radius: 0 var(--rund) var(--rund) 0;
  padding: .85rem 1rem;
}
.urteil.achtung { border-left-color: var(--achtung); }
.urteil.schlecht { border-left-color: var(--schlecht); }
.urteil > .sinnbild { color: var(--schrift-leise); margin-top: .1rem; }
.urteil strong { display: block; font-size: .88rem; margin-bottom: .15rem; }
.urteil p { margin: 0; font-size: .82rem; color: var(--schrift-leise); }

#dbild { display: block; width: 100%; height: auto; }
#dbild .dtitel {
  font: 600 13px var(--titelschrift);
  fill: var(--schrift);
}
#dbild .dachse {
  font: 11px var(--textschrift);
  fill: var(--schrift-leise);
  font-variant-numeric: tabular-nums;
}

/* Berührung am Diagramm: Waagerecht ziehen liest ab, senkrecht bleibt
 * Scrollen – `pan-y` wie bei `.kurven` im Buchhaltungs-Überblick. Ohne die
 * Angabe nimmt iOS jede Bewegung als Scrollen, und ein langer Druck öffnet
 * die Textauswahl. */
#dhuelle {
  touch-action: pan-y;
  user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Feste Ablesezeile über dem Diagramm statt eines schwebenden Kastens.
 * In Ruhe die letzte Messung des Fensters, beim Darüberfahren oder Ziehen
 * der Punkt unter dem Zeiger – und nie über der Kurve. Auf dem Handy war
 * der Kasten 180×145 Punkte groß, das Diagramm 312×124: Er verdeckte zwei
 * Drittel dessen, was er erklären sollte. Feste Mindesthöhe, damit beim
 * ersten Ablesen nichts springt. Keine Hausfarbe: Auskunft, nicht
 * Bedienung. */
.ablesezeile {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .3rem 1.1rem;
  min-height: 2.7rem;
  margin: 0 0 .7rem;
  padding: .55rem .85rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  font-variant-numeric: tabular-nums;
}
.ablesezeile .wann { font-size: .78rem; color: var(--schrift-leise); min-width: 8.5rem; }
.ablesewert { display: inline-flex; align-items: baseline; gap: .2rem; white-space: nowrap; }
.ablesewert strong { font: 600 1.02rem/1.1 var(--titelschrift); }
.ablesewert small { font-size: .72rem; color: var(--schrift-leise); }
.ablesewert .was { font-size: .74rem; color: var(--schrift-leise); margin-left: .15rem; }

@media (max-width: 640px) {
  /* Zeit in die erste Zeile; darunter die vier Werte in einer Reihe. Die
   * Namen entfallen – „°C · % · mbar · mm“ ist eindeutig, und mit Namen
   * brauchte die Zeile rund 560 Punkte, das wären zwei Umbrüche. */
  .ablesezeile .wann { flex: 0 0 100%; }
  .ablesewert .was { display: none; }
  /* Blätterzeile: 13rem Mindestbreite + zwei Pfeile + „Zu heute“ sind
   * rund 370 Punkte, die Karte hat 312 – ohne Umbruch ragte der Knopf
   * hinaus. */
  .zeitblaetter { flex-wrap: wrap; }
  .zeitblaetter strong { min-width: 0; flex: 1 1 auto; }
  /* Technikkasten: Kopf allein in die erste Zeile, Werte in einer Reihe.
   * 1,4rem Spaltenabstand war zugleich 1,4rem Zeilenabstand – 139 Punkte
   * hoch für vier kleine Zahlen. */
  .nebenwerte { gap: .45rem 1rem; }
  .nebenwerte > .klein { flex: 0 0 100%; }
}

.diagrammtafel {
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  padding: 1rem 1.1rem 1.25rem;
  margin-bottom: 1.25rem;
}
.diagrammkopf { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem; }
.diagrammkopf h2 { margin: 0; margin-right: auto; font-size: .95rem; }
.legende { display: flex; gap: 1rem; flex-wrap: wrap; font-size: .78rem; color: var(--schrift-leise); }
.legende span { display: inline-flex; align-items: center; gap: .35rem; }
.legende i { width: 10px; height: 3px; border-radius: 2px; display: block; }

.urteile { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; }
.urteil {
  display: flex;
  gap: .75rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-left: 3px solid var(--gut);
  border-radius: 0 var(--rund) var(--rund) 0;
  padding: .85rem 1rem;
}
.urteil.achtung { border-left-color: var(--achtung); }
.urteil.schlecht { border-left-color: var(--schlecht); }
.urteil strong { display: block; font-size: .88rem; margin-bottom: .15rem; }
.urteil p { margin: 0; font-size: .82rem; color: var(--schrift-leise); }

/* ----------------------------------------------------------------- Konto */

/* `min(100%, 21rem)`: Auf 320 Punkte breiten Geräten war die Spur 336
 * Punkte breit und lief 42 über den Rand – auf allen fünfzehn Seiten mit
 * Karten. Ändert nur etwas, wo der Kasten schmaler als 21rem ist. */
.karten { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr)); gap: 1.5rem; align-items: start; }
/* Lange Zeichenketten ohne Leerzeichen dürfen umbrechen (09.09.2026).
 *
 * Pfade, Kennungen und Adressen stehen in `<code>` und `<kbd>` — und ein
 * Pfad wie /Volumes/kamera_original/26005_regensburg1/kamera1 hat keine
 * Stelle, an der ein Browser von sich aus umbricht. Auf der Kameraseite
 * ragte er dadurch auf einem 375-Punkte-Schirm einundsiebzig Punkte aus
 * dem Bild. Für `.angaben dd` und `.merkmal` steht dieselbe Regel seit dem
 * 02.09.2026 weiter unten; sie kannte diese beiden Elemente nur nicht. */
code, kbd, samp { overflow-wrap: anywhere; }

/* Ein Quelltext zum Abschreiben oder Kopieren (16.09.2026, das Skript fuer
   die Shelly-Wetterstation). Waagerecht rollen statt umbrechen: Eine
   umgebrochene Zeile Programmtext sieht aus wie zwei, und wer sie so
   abtippt, bekommt sie so. */
.quelltext {
  margin: 0;
  padding: .9rem 1rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 8px;
  overflow-x: auto;
  max-height: 32rem;
  overflow-y: auto;
  font-size: .82rem;
  line-height: 1.5;
  tab-size: 2;
}
.quelltext code { overflow-wrap: normal; white-space: pre; }

/* Ein Feld im Raster muss schrumpfen dürfen.
 *
 * Rasterzellen haben, wie Flex-Elemente, von Haus aus `min-width: auto`:
 * Sie weigern sich, schmaler zu werden als ihr Inhalt. Der Inhalt ist hier
 * eine Karte mit einem Eingabefeld darin, und ein `<input>` ohne
 * Breitenangabe ist rund vierhundert Punkte breit — zwanzig Zeichen, so
 * steht es in der Norm. Auf der Kameraseite ragte das Formular dadurch auf
 * einem 375-Punkte-Schirm achtundneunzig Punkte aus dem Bild
 * (09.09.2026 nachgemessen). */
.karten > * { min-width: 0; }

/* „Mein Konto": Karten sehr verschiedener Hoehe (15.09.2026).

   Das Raster `.karten` richtet jede Zeile an ihrer hoechsten Karte aus.
   Auf dieser Seite stehen „Ihre Angaben" mit fuenf Zeilen neben „Ihre
   Freigaben" mit neun Bloecken - unter der kurzen Karte klaffte dann eine
   halbe Bildschirmhoehe Leere, und die naechste Reihe begann weit unten.
   Der Betreiber nannte es „verschoben", und genau so sah es aus.

   Spalten statt Raster: Die Karten fliessen und fuellen die Spalte, bis
   sie voll ist. Es gibt keine Zeilen mehr, also auch keine Zeilenhoehe,
   an der sich etwas ausrichten muesste. Gelesen wird spaltenweise von
   oben nach unten - fuer eine Sammlung unabhaengiger Karten ist das die
   natuerliche Ordnung.

   `.karten` selbst bleibt unberuehrt: Diese Klasse tragen dreissig
   Seiten, und die meisten haben Karten aehnlicher Hoehe, wo das Raster
   richtig ist. */
/* `display: block` muss sein: `.karten` ist ein Gitter, und ein
   Gitterbehaelter ignoriert `column-count` stillschweigend - die Regel
   stand da und wirkte nicht. */
.kontokarten { display: block; column-gap: 1.5rem; }
@media (min-width: 62rem) { .kontokarten { column-count: 3; } }
@media (min-width: 46rem) and (max-width: 61.99rem) { .kontokarten { column-count: 2; } }
.kontokarten > .karte {
  /* Eine Karte darf nicht mitten im Text auf die naechste Spalte rutschen. */
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin: 0 0 1.5rem;
  /* `inline-block` statt `block`: Sonst reissen manche Browser die Karte
     trotz `break-inside` an ihrer oberen Kante auf. */
  display: inline-block;
  width: 100%;
}

/* Auf dem Handy untereinander — auch dort, wo eine Vorlage zwei Spalten
 * fest vorgibt (09.09.2026 nachgemessen).
 *
 * Zehn Vorlagen schreiben `style="grid-template-columns:minmax(0,1.4fr)
 * minmax(0,1fr)"` direkt ans Element, um am Schreibtisch ein breiteres und
 * ein schmaleres Feld nebeneinander zu stellen. Das ist dort richtig. Auf
 * 375 Punkten werden daraus zwei Spalten von 190 und 136 Punkten: Auf dem
 * Postausgang ragte der Inhalt 24 Punkte aus dem Bild, auf den anderen
 * quetschte er sich in Streifen.
 *
 * `!important` steht hier bewusst — es ist der einzige Weg, eine Angabe am
 * Element zu überstimmen, und genau die soll überstimmt werden. Der Wähler
 * greift nur bei den festen Zweispaltern (`minmax(0,`), nicht bei den
 * mitwachsenden Kachelrastern (`repeat(auto-fit,minmax(9rem,1fr))`) — die
 * ordnen sich von selbst. */
@media (max-width: 700px) {
  .karten[style*="minmax(0,"] { grid-template-columns: 1fr !important; }
}
.karte {
  background: var(--kartengrund);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten-klein);
  padding: 1.6rem 1.7rem 1.7rem;
}
.karte > h2 { display: flex; align-items: center; gap: .5rem; }
.karte > h2 .sinnbild { color: var(--gelb); }

/* Zwischenüberschrift in einer Karte – EIN Aussehen statt elf Inline-Stilen
 * (10.09.2026). Auf der Kamera-Auskunft standen dieselben grauen Versalien
 * elfmal in der Vorlage, jedes Mal mit einem anderen Rand: mal .6rem, mal
 * 1rem, mal 1.2rem, mal 0. Die Abschnitte lagen dadurch verschieden weit
 * auseinander, und die Seite wirkte unruhig, ohne dass man sagen konnte,
 * woran es liegt.
 *
 * Etwas mehr Luft nach oben als vorher (1.4rem): Eine Zwischenüberschrift
 * trennt zwei Gedanken, und der Abstand ist das Einzige, woran man das
 * sieht – Größe und Farbe hat sie ja gerade nicht. */
.karte h3.unterpunkt {
  margin: 1.4rem 0 .45rem;
  font-size: .78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--schrift-leise);
}
/* Steht sie als Erstes in ihrem Kasten, bringt der Kasten den Abstand schon
 * mit – dann keinen zweiten obendrauf.
 *
 * `:first-child` und NICHT `:first-of-type`: Letzteres meint „die erste h3
 * unter ihren Geschwistern", und da jede dieser Überschriften in einem
 * eigenen Kasten sitzt, ist jede die erste – die Regel hätte den Abstand
 * überall wieder weggenommen (10.09.2026 nachgemessen: alle acht standen
 * auf 6,4 statt 22,4 Punkten, also enger als vorher statt luftiger). */
.karte h3.unterpunkt:first-child { margin-top: 0; }

dl.angaben { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1.25rem; margin: 0; }
dl.angaben dt { color: var(--schrift-leise); font-size: .84rem; }
dl.angaben dd { margin: 0; font-size: .9rem; }
/* Untereinander statt nebeneinander. Wo die Beschriftung selbst ein Satz
   ist („Bereichsüberwachung (Einbruch)") und der Wert mehrere Zeilen hat
   („Mo-Fr 00:00-05:30, 20:30-24:00 · Sa …"), nimmt die linke Spalte fast
   die ganze Breite, und der Wert bricht Buchstabe für Buchstabe um – in
   einer schmalen Karte unlesbar (06.09.2026 an der Alarm-Karte gesehen).
   Hier steht der Name über seinem Wert, wie auf einem Steckbrief. */
dl.angaben.untereinander { display: block; }
dl.angaben.untereinander dt { font-size: .9rem; color: var(--schrift); font-weight: 500; }
dl.angaben.untereinander dt.leise { color: var(--schrift-leise); font-weight: 400; }
dl.angaben.untereinander dd { margin: .15rem 0 .9rem; }
dl.angaben.untereinander dd:last-child { margin-bottom: 0; }

/* Wochenraster eines Scharfschaltplans (06.09.2026): sieben Zeilen über
   24 Stunden, gefüllt heißt scharf – dieselbe Darstellung, die Hikvision
   und Dahua in ihren eigenen Oberflächen zeigen. Die Balken kommen in
   Prozent aus `geraete._wochenraster`, das Stundengitter aus dem
   Hintergrund: So braucht eine Zeile ein Element statt achtundvierzig. */
.wochenraster { margin: .45rem 0 .6rem; max-width: 34rem; }
.wochenraster .skala {
  position: relative; height: 1rem; margin-left: 1.9rem;
  font-size: .68rem; color: var(--schrift-leise);
}
.wochenraster .skala b { position: absolute; font-weight: 400; transform: translateX(-50%); }
.wochenraster .skala b:first-child { transform: none; }
.wochenraster .skala b:last-child { transform: translateX(-100%); }
.wochenraster .zeile { display: flex; align-items: center; gap: .4rem; margin-bottom: 2px; }
.wochenraster .tag { width: 1.5rem; font-size: .72rem; color: var(--schrift-leise); }
.wochenraster .spur {
  position: relative; flex: 1 1 auto; height: 13px; border-radius: 3px;
  background:
    repeating-linear-gradient(90deg, transparent 0,
      transparent calc(100% / 24 - 1px), var(--linie) calc(100% / 24 - 1px),
      var(--linie) calc(100% / 24)),
    var(--karte-hell);
}
.wochenraster .spur i { position: absolute; top: 0; bottom: 0; background: var(--gelb); border-radius: 2px; }

/* Eine Erkennung mit Wochenraster steht eingeklappt (07.09.2026, seit dem
   10.09.2026 auch die eingeschalteten – Wunsch des Betreibers zur
   Bewegungserkennung).

   Deshalb heisst die Klasse jetzt `einklappbar`. `abgeschaltet` steht in
   den Waehlern daneben, damit aeltere Stellen nicht stumm ihr Aussehen
   verlieren – gemeint ist beides dasselbe.

   Ihr Wochenraster ist gut zweihundert Punkte hoch und zeigt einen
   Zeitplan, nach dem nichts geschieht. Drei solche Raster machten die
   Alarm-Karte um siebenhundert Punkte länger, ohne etwas zu sagen.

   Zu sehen bleibt die Zeile mit dem Zeitfenster; das Bild dazu kommt beim
   Anklicken. Dieselbe Zeichensprache wie bei den Kontaktkarten: das
   Winkelzeichen dreht sich beim Öffnen, das eigene Dreieck des Browsers
   bleibt aus. Der Hinweis „Zeitplan zeigen" steht nur, solange zu ist –
   offen wäre er eine Aufforderung zu etwas bereits Getanem. */
.abgeschaltet > summary,
.einklappbar > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
}
.abgeschaltet > summary::-webkit-details-marker,
.einklappbar > summary::-webkit-details-marker { display: none; }
.abgeschaltet > summary::before,
.einklappbar > summary::before {
  content: "›"; position: absolute; margin-left: -.85rem;
  color: var(--schrift-leise); transition: transform .12s ease;
}
.abgeschaltet[open] > summary::before,
.einklappbar[open] > summary::before { transform: rotate(90deg); }
.abgeschaltet > summary:hover,
.einklappbar > summary:hover { color: var(--schrift); }
.abgeschaltet > summary .mehr,
.einklappbar > summary .mehr { font-size: .8rem; color: var(--schrift-leise); }
.abgeschaltet[open] > summary .mehr,
.einklappbar[open] > summary .mehr { display: none; }

/* Die Stärkevorschau auf der Fotoverpixelung (07.09.2026): links
   unverändert, rechts weichgezeichnet, ein Ausschnitt in Originalgröße.
   `max-width` auf die eigene Breite des Bildes (560), damit es nicht
   hochgerechnet wird – ein hochskalierter Ausschnitt sähe weicher aus, als
   er ist, und die Vorschau soll gerade nicht schmeicheln. */
.staerkevorschau { margin: .9rem 0 .2rem; }
.staerkevorschau img {
  display: block; max-width: min(560px, 100%); height: auto;
  border-radius: 8px; border: 1px solid var(--linie); margin-top: .4rem;
}
.staerkevorschau img:not([src]) { display: none; }
.staerkevorschau img[src] { cursor: zoom-in; }

/* Das ganze Bild über alles gelegt (08.09.2026). Der Ausschnitt in 1:1
   sagt, WIE stark verwischt wird; er sagt nicht, WO. Ein Klick auf die
   Vorschau legt darum dasselbe noch einmal über das ganze Bild.
   `[hidden]` allein reichte hier nicht: Ein `display:flex` schlägt das
   eingebaute `display:none` der Browser, deshalb steht es ausdrücklich da. */
.ganzschau {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: .7rem; padding: 1.4rem;
  background: rgba(0, 0, 0, .86);
}
.ganzschau[hidden] { display: none; }
.ganzschau img {
  max-width: 100%; max-height: calc(100vh - 8rem); height: auto;
  border-radius: 6px; box-shadow: 0 12px 40px rgba(0, 0, 0, .6);
}
.ganzschau p { color: #fff; margin: 0; max-width: 62rem; text-align: center; }
.ganzschau .schliessen {
  position: absolute; top: .9rem; right: 1.1rem;
  background: #fff; color: #111;
}

/* Die Ampel „Fotoverpixelung" (07.09.2026). Vier Farben, und die Farbe
   trägt nie die Aussage allein: Neben jedem Punkt steht das Wort („geht
   nicht", „in Ordnung"), und die Zeile ist links durch einen Balken
   gekennzeichnet. Wer Rot und Grün nicht unterscheidet – jeder zwölfte
   Mann –, liest dieselbe Auskunft. */
.ampelpunkt {
  width: .7rem; height: .7rem; border-radius: 50%; flex: none;
  display: inline-block;
}
.ampelpunkt.rot { background: var(--schlecht); }
.ampelpunkt.gelb { background: var(--achtung); }
.ampelpunkt.gruen { background: var(--gut); }
.ampelpunkt.grau { background: var(--linie); }

.ampelzeile {
  border-left: 3px solid var(--linie);
  padding: .55rem 0 .55rem .9rem;
  margin-bottom: .8rem;
}
.ampelzeile:last-child { margin-bottom: 0; }
.ampelzeile.rot { border-left-color: var(--schlecht); }
.ampelzeile.gelb { border-left-color: var(--achtung); }
.ampelzeile.gruen { border-left-color: var(--gut); }
/* „Das ist kein Fehler" – Vermerk und Knopf in einer Ampelzeile
 * (11.09.2026). Beides bewusst klein und unter dem Befund: Es ist eine
 * Randbemerkung zu einer Zeile, nicht ihr Inhalt. */
.ampelbestaetigen {
  display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
  margin: .45rem 0 0;
}
.ampelbestaetigen input {
  flex: 1 1 16rem; max-width: 26rem; font-size: .8rem; padding: .35rem .55rem;
}
.ampelbestaetigt {
  display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
  margin: .45rem 0 0; font-size: .82rem; color: var(--gut);
}
.ampelbestaetigt .leise { color: var(--schrift-leise); }
.ampelbestaetigt form { margin: 0; }

.ampelkopf {
  display: flex; align-items: center; gap: .55rem; flex-wrap: wrap;
  margin-bottom: .2rem;
}
.ampelname { font-weight: 500; }
.ampelbefund { margin: 0; font-size: .92rem; }
.ampeltext { margin: .25rem 0 0; max-width: 60rem; }
/* Die Anleitung ist das, wofür der Betreiber die Seite öffnet – sie steht
   deshalb auf eigenem Grund und nicht gedämpft wie ein Nebensatz. */
.ampeltun {
  margin: .5rem 0 0; padding: .5rem .7rem; max-width: 60rem;
  background: var(--karte-hell); border-radius: var(--rund-klein, 6px);
  font-size: .88rem;
}
.ampelkachel.rot .lagenzahl { color: var(--schlecht); }
.ampelkachel.gelb .lagenzahl { color: var(--achtung); }
.ampelkachel.gruen .lagenzahl { color: var(--gut); }
.graueliste > summary { cursor: pointer; font-size: .9rem; }

/* Foto- und Videoverpixelung als zwei Punkte in einer Karte (07.09.2026).
   Eine dünne Linie zwischen ihnen, kein zweiter Kasten: Es ist dieselbe
   Sache für zwei Wege, und zwei Karten nebeneinander legten nahe, man
   müsse sich für eine entscheiden. Der Stand steht gedämpft unter seinem
   Knopf, damit man auf einen Blick sieht, welche Seite schon eingerichtet
   ist – vorher galt die eine Zeile darunter nur fürs Foto. */
.verpixelteil + .verpixelteil {
  margin-top: 1.1rem; padding-top: 1.1rem; border-top: 1px solid var(--linie);
}
.verpixelteil h3 { margin: 0 0 .25rem; }
.verpixelteil > p.klein { margin: 0 0 .7rem; }
.verpixelteil > p.stand { margin: .7rem 0 0; }

/* Eine Karte, die zu viel Inhalt für eine schmale Spalte hat, nimmt die
   ganze Breite.

   Innerhalb der Mauer (columns) hiess das `column-span: all`. Das gilt
   weiter für kurze Karten – aber nur für kurze. Eine Karte von mehreren
   tausend Punkten Höhe zerbricht den Spaltensatz: Was hinter ihr steht,
   rückt nach vorn, und an ihrer Stelle bleibt ein leerer Kasten von ihrer
   Höhe stehen (07.09.2026 an der Kamera-Auskunft bemängelt). So eine Karte
   gehört nicht in den Spaltensatz, sondern dahinter – dann steht sie als
   gewöhnlicher Block da und braucht nur noch ihren Abstand. */
.karten.mauer > .karte.breit { column-span: all; -webkit-column-span: all; }
.karte.breit { margin: 0 0 1.5rem; }

.freigabeliste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.freigabeliste li { display: flex; align-items: center; gap: .55rem; font-size: .88rem; }
.freigabeliste .sinnbild { color: var(--gut); }
/* Die Ampel vor dem Namen auf der Baustellenseite (29.09.2026): gelb,
 * solange die Zugangsdaten nicht verschickt sind. Die Regel darüber färbt
 * jedes Zeichen der Liste grün, deshalb hier mit eigener Klasse und
 * höherem Gewicht – ein Inline-Stil am Elternelement kam dagegen nicht an. */
.freigabeliste .ampel { display: inline-flex; flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; }
.freigabeliste .ampel .sinnbild { width: 16px; height: 16px; }
.freigabeliste .ampel.gelb .sinnbild { color: var(--gelb); }
/* Die Zugangszeile auf der Baustellenseite in EINER Zeile (29.09.2026):
 * Ampel, Name, ⓘ, Stand, Knopf – bricht am Handy hinter dem ⓘ um, die
 * Ampel bleibt dabei an der Namenszeile ausgerichtet. */
.freigabeliste li.zugangszeile { flex-wrap: wrap; align-items: center; gap: .35rem .5rem; }
.freigabeliste li.zugangszeile > a:first-of-type { font-weight: 500; }
.freigabeliste li.zugangszeile .mehr { color: var(--schrift-leise); }
.freigabeliste li.zugangszeile .knopf { margin-left: auto; }
.freigabeliste li.aus { color: var(--schrift-leise); }
.freigabeliste li.aus .sinnbild { color: var(--linie); }

/* Die Nutzungskarte auf der Personenseite (29.09.2026). Vier Zahlen in
 * einer Reihe, darunter Balken je Modul: Länge nach Anzahl, die Menge
 * daneben, weil hundert Seitenaufrufe und ein Gigabyte Download zwei
 * verschiedene Dinge sagen. Am Handy zwei Zahlen je Reihe. */
.nutzungskarte .zugangsdaten { display: grid; grid-template-columns: auto 1fr; gap: .2rem .9rem; margin: 0 0 .9rem; font-size: .88rem; }
.nutzungskarte .zugangsdaten dt { color: var(--schrift-leise); }
.nutzungskarte .zugangsdaten dd { margin: 0; }
.nutzungskarte .kennzahlen { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .5rem; }
.nutzungskarte .kennzahlen > div { display: flex; flex-direction: column; gap: .1rem; padding: .55rem .7rem; border: 1px solid var(--linie-leise); border-radius: var(--rund); min-width: 0; }
.nutzungskarte .kennzahlen .zahlen { font-size: 1.25rem; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nutzungskarte .unterpunkt { margin-top: 1rem; }
.nutzungskarte .modulbalken, .nutzungskarte .kameraliste, .nutzungskarte .vorgangsliste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
.nutzungskarte .modulbalken li { display: grid; grid-template-columns: 6.5rem 1fr auto; align-items: center; gap: .6rem; font-size: .85rem; }
.nutzungskarte .modulbalken .balken { display: block; height: 8px; border-radius: 4px; background: var(--karte-hell); overflow: hidden; }
.nutzungskarte .modulbalken .balken i { display: block; height: 100%; background: var(--gelb); border-radius: 4px; }
.nutzungskarte .kameraliste li, .nutzungskarte .vorgangsliste li { display: flex; gap: .6rem; justify-content: space-between; }
.nutzungskarte .vorgangsliste li { justify-content: flex-start; }
.nutzungskarte .vorgangsliste li span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) {
  .nutzungskarte .kennzahlen { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .nutzungskarte .modulbalken li { grid-template-columns: 5rem 1fr auto; }
}

/* ----------------------------------------------------------------- Admin */

.adminleiste {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}
.adminleiste input[type="search"] { width: 16rem; }

.merkmal {
  display: inline-block;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 999px;
  padding: .1rem .55rem;
  font-size: .72rem;
  font-weight: 600;
  color: var(--schrift-leise);
  margin: 1px;
}
.merkmal.an { color: var(--gelb-schrift); border-color: rgba(210, 215, 0, .5); }
/* Die Zahlen in der Baustellenübersicht (10.09.2026): je Farbe eine kleine
 * Marke. Sie ersetzt vier Wörter durch eine Zahl – der Satz daneben sagt
 * ohnehin, worum es geht. */
.merkmal.rot { color: var(--schlecht); border-color: var(--schlecht); }
.merkmal.gelb { color: var(--achtung); border-color: var(--achtung); }
.merkmal.gruen { color: var(--gut); border-color: var(--gut); }
.merkmal.grau { color: var(--schrift-leise); }

/* -------------------------------------- Fotoverpixelung je Baustelle
 *
 * Eine Zeile je Baustelle, die schlechteste zuerst. Der farbige Balken
 * links ist derselbe Gedanke wie bei `.ampelzeile` darunter – nur schmaler,
 * weil hier zwanzig Zeilen untereinander stehen und nicht fünf. */
.ortsampel { list-style: none; margin: 0; padding: 0; }
.ortszeile {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  flex-wrap: wrap;
  padding: .5rem .2rem .5rem .8rem;
  border-left: 3px solid var(--linie);
  border-bottom: 1px solid var(--linie-leise);
}
.ortszeile:last-child { border-bottom: 0; }
.ortszeile.rot { border-left-color: var(--schlecht); }
.ortszeile.gelb { border-left-color: var(--achtung); }
.ortszeile.gruen { border-left-color: var(--gut); }
.ortszeile .ampelpunkt { align-self: center; }
.ortsname { font-weight: 600; font-size: .9rem; min-width: 14rem; }
.ortssatz { color: var(--schrift-leise); font-size: .84rem; flex: 1 1 18rem; }
.ortszahlen { display: flex; gap: .25rem; flex: none; }

.gruppe {
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .8rem 1rem 1rem;
  margin-bottom: 1rem;
}
.gruppe > legend, .gruppe > h3 {
  font-size: .82rem;
  color: var(--schrift-leise);
  font-family: var(--textschrift);
  font-weight: 500;
  padding: 0 .3rem;
  margin: 0 0 .6rem;
}
.wahlreihe { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }
.unterwahl {
  margin: .5rem 0 .8rem 1.6rem;
  padding-left: .9rem;
  border-left: 2px solid var(--linie);
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.1rem;
}
.schalterliste { display: flex; flex-direction: column; gap: 0; }
/* Jede Zeile hat Luft und eine feine Trennlinie zur nächsten – so liest man
 * Schalter für Schalter, statt eine Wand aus Text zu sehen. */
.schalterliste .zeile { display: flex; align-items: center; gap: 1rem; padding: .8rem 0; }
.schalterliste .zeile + .zeile { border-top: 1px solid var(--linie-leise); }
.schalterliste .zeile:first-child { padding-top: .2rem; }
.schalterliste .zeile:last-child { padding-bottom: .2rem; }
.schalterliste .zeile.zeitfenster { align-items: flex-end; flex-wrap: wrap; gap: .9rem; }
/* Eine Zeile, die zu dem Punkt darüber gehört (08.09.2026). Auf der
 * Kameraseite stehen unter „Einstellung Verpixelung Video" die drei
 * Schalter, die allein das Livebild betreffen. Eingerückt und mit einem
 * feinen Strich an der linken Kante: So sieht man, wozu sie gehören, ohne
 * dass daraus eine zweite Karte mit eigener Überschrift wird. Der Strich
 * bekommt eine ruhige Farbe – er ordnet, er warnt nicht. */
.schalterliste .zeile.untergruppe {
  padding-left: 1rem;
  border-left: 2px solid var(--linie);
}
.schalterliste .zeile.untergruppe + .zeile:not(.untergruppe) {
  margin-top: .2rem;
}
.schalterliste .zeile .beschriftung { margin-right: auto; }
.schalterliste .zeile strong { display: block; font-size: .88rem; font-weight: 600; }
.schalterliste .zeile span { font-size: .78rem; color: var(--schrift-leise); }

.vorschau {
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .9rem 1rem;
  font-size: .85rem;
}
.vorschau strong { display: block; margin-bottom: .4rem; font-size: .82rem; color: var(--schrift-leise); font-weight: 500; }

/* Umbrechen darf die Leiste (09.09.2026 nachgemessen): Auf 375 Punkten
 * standen „Löschen", „Abbrechen", „Verbindung testen" und „Speichern"
 * nebeneinander in einer Zeile von 477 Punkten — der Speichernknopf lag
 * damit ausserhalb des Bildes, und die ganze Seite bekam einen Querlauf.
 * Am Schreibtisch ändert die Zeile nichts, dort ist Platz. */
.aktionsleiste {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .75rem;
  row-gap: .6rem;
  margin-top: 1.5rem;
  padding: .9rem 1.25rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten);
}


/* ---------------------------------------------------------------- Kalender
 *
 * Aufbau wie auf livebild.triobild.de: Knopf mit Datum, darunter das
 * Monatsblatt. Hier zusätzlich ein Punkt an jedem Tag, für den Aufnahmen
 * vorliegen – sonst sucht man sich durch leere Tage.
 */

.datumsknopf {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  background: var(--grund-tief);
  border: 1px solid rgba(210, 215, 0, .5);
  color: var(--schrift);
  border-radius: var(--rund);
  padding: .35rem .7rem;
  font: 500 .85rem var(--textschrift);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
:root[data-thema="hell"] .datumsknopf { background: var(--karte-hell); }
.datumsknopf:hover { border-color: var(--gelb); filter: none; }
.datumsknopf .sinnbild { color: var(--schrift-leise); }
/* Das Datum im Knopf. Es hatte bis zum 11.09.2026 gar keine Regel – die
   Klasse stand in drei Seiten und in keinem Stylesheet. Tabellenziffern,
   damit der Knopf beim Blättern nicht in der Breite zappelt: In der
   Hausschrift ist die 1 sonst schmaler als die 8. */
.datumsknopf .datum { font-variant-numeric: tabular-nums; }

.kalender {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  z-index: 40;
  width: 16.5rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten);
  padding: .7rem;
  /* Passt das Blatt weder über noch unter den Knopf, deckelt `kalender.js`
   * seine Höhe auf den vorhandenen Platz – dann muss der Rest erreichbar
   * bleiben. Ohne diese Zeile wäre er abgeschnitten statt scrollbar. */
  overflow-y: auto;
  overscroll-behavior: contain;
}
.kalenderkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.kalenderkopf strong { font: 500 .88rem var(--textschrift); }
.kalenderkopf button { font-size: 1.1rem; line-height: 1; padding: .1rem .5rem; }

.kalendergitter {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  justify-items: center;
}
/* Eine achte Spalte ganz vorn für die Kalenderwoche (11.09.2026, Bauarchiv).
   `auto` und nicht `1fr`: Die Zahl ist zweistellig und soll den sieben
   Tagesspalten nichts wegnehmen. */
/* `minmax(0, 1fr)` statt `1fr` (16.09.2026).

   Mit Kalenderwochen hat das Gitter acht Spalten. Die Tageszellen waren
   fest 2 rem breit, die KW-Spalte kam dazu: zusammen 251 Punkte in einem
   Gitter von 240. Die letzte Spalte – der Sonntag – wurde nach rechts
   hinausgedrueckt und war nur noch durch seitliches Scrollen zu erreichen.
   Gemeldet als „der letzte Tag rutscht manchmal aus dem Bild"; „manchmal",
   weil es nur mit KW-Spalte auftritt und die Zellbreite je nach Schirm
   wechselt.

   `1fr` allein half nicht: Es bedeutet `minmax(auto, 1fr)`, und das `auto`
   nimmt die feste Breite der Zelle als Mindestmass. Erst `minmax(0, …)`
   erlaubt der Spur, schmaler zu werden als ihr Inhalt. Die Zelle selbst
   gibt darum unten ihre feste Breite auf und fuellt die Spur. */
.kalendergitter.mit-kw { grid-template-columns: auto repeat(7, minmax(0, 1fr)); }
.kalendergitter .kw {
  align-self: center;
  padding-right: .45rem;
  font-size: .7rem;
  font-variant-numeric: tabular-nums;
  color: var(--schrift-leise);
  /* Ein feiner Strich trennt sie von den Tagen – sonst liest man die 32
     als Datum. */
  border-right: 1px solid var(--linie);
  margin-right: .15rem;
  line-height: 2rem;
}
.kalendergitter .kwkopf {
  padding-right: .45rem;
  font-size: .66rem;
  letter-spacing: .04em;
}
.kalendergitter .wtag {
  font-size: .72rem;
  color: var(--schrift-leise);
  padding-bottom: .3rem;
}
.kalendergitter .tag {
  position: relative;
  /* Kein Raster im Knopf: WebKit auf dem iPhone verweigert `display: grid`
     in `<button>` teils bis heute – die Zahl klebte oben links. Zeilenhöhe
     zentriert überall gleich. */
  display: block;
  line-height: 2rem;
  text-align: center;
  /* Breite aus der Spur, nicht fest (16.09.2026): Eine feste Breite machte
     das Gitter breiter als das Blatt und schob den Sonntag hinaus. `2rem`
     bleibt als Mindestmass – darunter ist eine Zahl mit Punkt nicht mehr
     zu treffen –, und `max-width` haelt sie in engen Blaettern zusammen. */
  width: 100%;
  min-width: 1.75rem;
  max-width: 2.25rem;
  margin: 0 auto;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--rund);
  background: none;
  color: var(--schrift);
  font: .85rem var(--textschrift);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.kalendergitter .tag:hover:not(:disabled) { background: var(--karte-hell); filter: none; }
.kalendergitter .tag.gewaehlt { background: var(--gelb); color: var(--auf-gelb); font-weight: 600; }
.kalendergitter .tag:disabled { color: var(--linie); cursor: default; }
.kalendergitter .tag.fremd { color: var(--linie); font-size: .8rem; }
/* Der Punkt sitzt unter der Zahl, nicht daneben: In einer 2-rem-Zelle ist
 * seitlich kein Platz, ohne dass die Ziffer verrutscht. */
.kalendergitter .tag.voll::after {
  content: "";
  position: absolute;
  bottom: 3px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--gelb);
}
.kalendergitter .tag.gewaehlt.voll::after { background: var(--auf-gelb); }

.kalenderfuss {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-top: .6rem;
  padding-top: .5rem;
  border-top: 1px solid var(--linie);
  font-size: .72rem;
  color: var(--schrift-leise);
}
.punkt-voll { width: 4px; height: 4px; border-radius: 50%; background: var(--gelb); }

/* ------------------------------------------------------------------- Fuß */

.seitenfuss {
  border-top: 1px solid var(--linie);
  margin-top: 3rem;
  background: var(--grund-tief);
}
:root[data-thema="hell"] .seitenfuss { background: var(--karte); }
.fusszeile {
  max-width: var(--breite);
  margin: 0 auto;
  padding: 1.25rem 1.5rem 2rem;
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 2rem;
  align-items: flex-start;
  font-size: .8rem;
  color: var(--schrift-leise);
}
.fusszeile .verweise { display: flex; gap: 1.1rem; flex-wrap: wrap; }
.fusszeile .verweise a { color: var(--schrift-leise); text-decoration: underline; }
.fusszeile .verweise a:hover { color: var(--gelb-schrift); }
/* Der Hinweis auf die Verpixelung steht bewusst im Fuß jeder Seite und nicht
 * nur unter dem Bild: Wer Bilder weitergibt, muss die Zusage zur Hand haben. */
.fusszeile .schutz {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  max-width: 44rem;
  margin-left: auto;
}
.fusszeile .schutz .sinnbild { color: var(--gut); margin-top: .1rem; }


/* -------------------------------------------------------------- Vergleich
 *
 * Zwei Aufnahmen liegen deckungsgleich übereinander; der Schieber beschneidet
 * die obere. So wandert nur die Kante, das Motiv bleibt an Ort und Stelle –
 * nebeneinandergestellt müsste das Auge springen und sähe die kleinen
 * Unterschiede nicht.
 */

.vergleich {
  position: relative;
  /* Der Kasten muss **genau die Form des Bildes** haben, sonst stimmt der
     Schnitt nicht mit dem Griff überein.

     Vorher stand hier `width: 100%` zusammen mit `aspect-ratio` und
     `max-height`. Sobald die Höhe begrenzt wurde, war der Kasten breiter als
     16:9 – das Bild lag dann mit Rändern links und rechts darin (`contain`),
     der Griff aber richtete sich nach dem Kasten. Ergebnis: Man zog den Griff
     an eine Stelle, und die Kante sprang woanders hin. Genau das fühlt sich
     an, als hake es.

     Mit `height` als Ausgangsgröße und `width: auto` folgt die Breite aus dem
     Seitenverhältnis. Der Kasten ist dann das Bild. */
  aspect-ratio: 16 / 9;
  height: min(68vh, calc((100vw - 4rem) * 9 / 16));
  width: auto;
  max-width: 100%;
  margin-inline: auto;
  overflow: hidden;
  cursor: ew-resize;
  touch-action: none;   /* sonst scrollt das Tablet, statt zu schieben */
  user-select: none;
}
.vergleich .lage {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* `cover` statt `contain`: Der Kasten hat jetzt dieselbe Form wie das Bild,
     also ist beides gleichwertig – aber `cover` lässt auch bei einer Kamera,
     die nicht genau 16:9 liefert, keine Ränder entstehen, an denen der
     Schnitt wieder auseinanderliefe. */
  object-fit: cover;
  display: block;
}
.vergleich .klipp { clip-path: inset(0 calc(100% - var(--schnitt)) 0 0); }

.vergleich .griff {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--schnitt);
  /* Auf dem Verbund zeichnen lassen: Ohne das rechnet der Browser bei jedem
     Ziehen die Seite neu durch, statt nur die Ebene zu verschieben. */
  will-change: left;
  width: 2px;
  margin-left: -1px;
  background: var(--gelb);
  pointer-events: none;
}
.vergleich .griff span {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--gelb);
  color: var(--auf-gelb);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
}
.zeitmarke {
  position: absolute;
  top: 10px;
  padding: .15rem .5rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, .6);
  color: #f2f2ee;
  font-size: .74rem;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.zeitmarke.links { left: 10px; }
.zeitmarke.rechts { right: 10px; }

/* --------------------------------------------------- Öffentliche Seite */

/* Die Marke „Freier Weblink" oben rechts, wo sonst das Konto steht
 * (21.09.2026): kein Knopf, nichts zu klappen – eine Auskunft. */
.freimarke {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .35rem .7rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  color: var(--schrift-leise);
  font-size: .82rem;
  white-space: nowrap;
}
@media (max-width: 640px) { .freimarke .wort { display: none; } }

body.oeffentlich main {
  max-width: 1400px;
  padding-top: 2rem;
}
.oeffentlichtafel .tafelkopf h1 { font-size: 1rem; }
.oeffentlichtafel .kamerastreifen .kameraknopf { font-size: .76rem; }

/* -------------------------------------------------------------- Personen */

.personenliste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.personenliste li {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .7rem 0;
  border-bottom: 1px solid var(--linie-leise);
}
.personenliste li:last-child { border-bottom: 0; }
.personenliste .wer { margin-right: auto; min-width: 0; }
.personenliste .wer strong { display: block; font-size: .9rem; font-weight: 600; }
.personenliste .wer span { font-size: .78rem; color: var(--schrift-leise); }
.personenliste li.gesperrt { opacity: .5; }

/* ---- Betriebstafel: feste Spalten (09.09.2026) ----------------------
 *
 * „das ist irgendwie nicht ordentlich untereinander. mache das schöner."
 *
 * Es war dieselbe Sache wie bei den Kameralisten: `table-layout: auto`
 * bemisst jede Spalte am längsten Inhalt. Eine Zeile wie
 * „0 offen · heute 4 · Runde 3 · Modell yolo11l@5:0.15" zog die
 * Zahlenspalte auf, der Vorgangsname daneben brach dreimal um, und
 * zwischen „Zahlen" und „Letzter Fehler" stand eine halbe Bildschirmbreite
 * Leere.
 *
 * Jetzt feste Breiten, und die Zahlenspalte nimmt, was übrig bleibt: Sie
 * ist die einzige, deren Länge man nicht kennt. `min-width`, damit auf
 * schmalen Schirmen die Tafel in ihrer Karte rollt statt sieben Spalten
 * auf 375 Punkte zu pressen. */
.tabelle.betriebstafel {
  table-layout: fixed;
  width: 100%;
  min-width: 40rem;
}
.betriebstafel th:nth-child(1),
.betriebstafel td:nth-child(1) { width: 1.8rem; padding-right: 0; }
.betriebstafel th:nth-child(3),
.betriebstafel td:nth-child(3) { width: 9rem; white-space: nowrap; }
.betriebstafel th:nth-child(4),
.betriebstafel td:nth-child(4) { width: 9rem; }
/* Vier Spalten statt sieben (20.09.2026): Ampel, Vorgang mit dem Satz
 * darunter, Stand, Knöpfe. Zahlen und Fehlertexte stehen als Zeilen unter
 * dem Namen – und nur, wenn die Zeile nicht grün ist.
 *
 * `table-layout: fixed`, damit die Sätze umbrechen: Bei automatischer
 * Breite dehnt ein langer Satz die Spalte, und am Telefon war die Tafel
 * 1056 Punkte breit und rollte in ihrer Karte, statt dass der Text
 * umbricht. Fest heißt: Die drei schmalen Spalten bekommen ihr Maß, der
 * Vorgang den Rest. */
.betriebstafel { table-layout: fixed; }
.betriebstafel td:nth-child(2) > span { display: block; line-height: 1.35; overflow-wrap: anywhere; }
.betriebstafel td:nth-child(2) strong { overflow-wrap: normal; }
/* Am Telefon (375 Punkte) fällt die Spalte „Stand" weg – der Satz unter dem
 * Namen trägt den Zustand ohnehin, und mit ihr blieben dem Vorgang 135
 * Punkte, sodass „Fotoverpixelung" mitten im Wort brach. */
@media (max-width: 640px) {
  .betriebstafel { min-width: 0; }
  .betriebstafel th:nth-child(3), .betriebstafel td:nth-child(3) { display: none; }
  .betriebstafel th:nth-child(4), .betriebstafel td:nth-child(4) { width: 5.5rem; }
}

/* Oben ausgerichtet, nicht mittig: Die Zeilen sind verschieden hoch (der
 * Name bringt Untertitel und manchmal eine Klappe mit). Mittig gesetzt
 * begänne jede Zelle auf einer anderen Höhe, und genau das sah unordentlich
 * aus. Oben beginnen alle auf derselben Linie. */
.betriebstafel td { vertical-align: top; }
/* Die Ampel sitzt auf der Höhe der ersten Textzeile daneben, nicht am
 * oberen Rand der Zelle. */
.betriebstafel td:nth-child(1) .ampel { margin-top: .38rem; }
.betriebstafel td:nth-child(2) strong { display: block; line-height: 1.35; }
.betriebstafel .zahlen { overflow-wrap: anywhere; }

/* Die Knöpfe untereinander und gleich breit: Nebeneinander passten zwei
 * nicht in die Spalte, und umgebrochen standen sie versetzt.
 *
 * **Kein `display: flex` auf der Zelle.** Eine Tabellenzelle, die zur
 * Flexbox wird, ist keine Tabellenzelle mehr – sie fällt aus dem
 * Spaltenraster, und genau das sollte hier behoben werden. Deshalb bleibt
 * die Zelle eine Zelle, und die Knöpfe darin werden zu Blöcken. */
.betriebstafel .tafelknoepfe > * {
  display: block;
  width: 100%;
  text-align: center;
  margin: 0 0 .3rem;
}
.betriebstafel .tafelknoepfe > *:last-child { margin-bottom: 0; }
.betriebstafel th.rechts { text-align: right; }

/* ---- Die Nutzerliste unter „Mitarbeiter" (09.09.2026) ---------------
 *
 * Dieselbe Bauart und dieselbe Krankheit wie die Kamera- und Kundenlisten:
 * eine Tabelle je Firma, jede maß ihre Spalten am eigenen Inhalt. Acht
 * Spalten, und bei jeder Firma standen sie woanders.
 *
 * Name und E-Mail bekommen keine feste Breite — sie teilen sich, was
 * übrig bleibt; die Zahlenspalten rechts sind schmal und stehen fest. */
/* Seit dem 21.09.2026 drei Spalten: Name (mit E-Mail und ⓘ), Zuletzt da,
   Knopf. Die acht Spalten mit Modulmarken davor waren „zu überladen". */
.tabelle.nutzerliste { table-layout: fixed; width: 100%; }
.nutzerliste th:nth-child(2),
.nutzerliste td:nth-child(2) { width: 10rem; white-space: nowrap; }
.nutzerliste th:nth-child(3),
.nutzerliste td:nth-child(3) { width: 8.5rem; }
.nutzerliste td:nth-child(1) { overflow-wrap: anywhere; }

/* ------------------------------------------- Kundenliste (09.09.2026)
 *
 * Eine Zeile je Kunde, aufklappbar. Ein Raster mit festen Spalten und kein
 * Fließtext: Die Zahlen sollen untereinander stehen, sonst sucht das Auge
 * bei jedem Kunden neu.
 *
 * Kopfzeile und Zeilen teilen sich `--kundenraster`. Zwei getrennte
 * Angaben wären zwei Gelegenheiten, sie auseinanderlaufen zu lassen. */
.tafel {
  --kundenraster: 1fr 7rem 7.5rem 7rem 7rem;
}
.kundenkopfzeile,
.kundenzeile > summary {
  display: grid;
  grid-template-columns: var(--kundenraster);
  gap: .8rem;
  align-items: center;
  padding: .6rem 1rem;
}
.kundenkopfzeile {
  border-bottom: 1px solid var(--linie);
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--schrift-leise);
}
.kundenzeile { border-bottom: 1px solid var(--linie-leise); }
.kundenzeile:last-child { border-bottom: 0; }
.kundenzeile > summary { cursor: pointer; list-style: none; }
.kundenzeile > summary::-webkit-details-marker { display: none; }
.kundenzeile > summary:hover { background: var(--karte-hell); }
.kundenzeile[open] > summary { background: var(--karte-hell); }
.kundenfirma { font-weight: 600; font-size: .92rem; min-width: 0;
  overflow-wrap: anywhere; }
.kundenfirma .klein { font-weight: 400; margin-left: .4rem; }
.kundenzeile .zahlen { font-variant-numeric: tabular-nums; }
.kundenstand { text-align: right; }
/* Stillgelegt heißt: niemand kommt hinein. Das darf man der Zeile ansehen,
 * ohne sie aufzuklappen – aber blass, nicht rot: Es ist ein Zustand, den
 * jemand absichtlich hergestellt hat, keine Störung. */
.kundenzeile.still > summary .kundenfirma { opacity: .6; }
.kundenkoerper { padding: .3rem 1rem 1.2rem; }

/* Auf dem Handy untereinander (09.09.2026 nachgemessen).
 *
 * Die festen Spalten sind zusammen 28,5rem breit, dazu Zwischenräume und
 * Innenrand — über 500 Punkte, wo auf einem 375-Punkte-Gerät rund 315 zur
 * Verfügung stehen. Und `.tafel` trägt `overflow: hidden`: Was rechts
 * hinausragt, ließ sich nicht einmal heranschieben. „Kameras" und
 * „stillgelegt" waren auf dem Handy schlicht nicht zu erreichen.
 *
 * Statt zu rollen wird gestapelt: Der Name steht oben, die drei Zahlen
 * darunter in einer Zeile, jede mit ihrer Beschriftung davor — sonst
 * stünden drei Zahlen ohne Erklärung da, weil die Kopfzeile fehlt. */
@media (max-width: 640px) {
  .kundenkopfzeile { display: none; }
  .kundenzeile > summary {
    grid-template-columns: 1fr;
    gap: .25rem;
    padding: .7rem 1rem;
  }
  .kundenzeile > summary .zahlen::before {
    content: attr(data-was) ": ";
    color: var(--schrift-leise);
  }
  .kundenstand:empty { display: none; }
}

/* Die Freigabemaske, die ein Hauptnutzer im Portal aufklappt (09.09.2026).
 * Sie hängt unter der Zeile der Person, deshalb `100%`: Sie ist selbst ein
 * Element der Zeilen-Flexbox und bekäme sonst einen Streifen neben den
 * Knöpfen. Die Zeile darf dafür umbrechen. */
.personenliste li { flex-wrap: wrap; }
.rechtefeld {
  flex: 1 0 100%;
  margin: .6rem 0 .2rem;
  padding: .8rem .9rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
}
.rechtegruppe { margin-bottom: .7rem; }
.rechtegruppe:last-of-type { margin-bottom: 0; }
.rechtezeile {
  display: flex; align-items: baseline; gap: .45rem;
  padding: .18rem 0; font-size: .85rem; cursor: pointer;
}
/* Kameras stehen unter ihrer Baustelle, und das soll man sehen, ohne die
 * Namen zu lesen. */
.rechtezeile.eingerueckt { padding-left: 1.5rem; font-size: .8rem; }
.rechtezeile input { margin: 0; flex: none; }
.rollenmarke {
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 999px;
  padding: .1rem .55rem;
  font-size: .7rem;
  font-weight: 600;
  color: var(--schrift-leise);
  white-space: nowrap;
}
.rollenmarke.haupt { color: var(--gelb-schrift); border-color: rgba(210, 215, 0, .5); }
/* `haupt` ist Herkunft („unser Haus", „Hauptkontakt") – deshalb gelb.
 * `an` ist ein Zustand („hinterlegt", „gesetzt") – deshalb grün, denn Gelb
 * steht hier nie für einen Zustand. Ohne Zusatz bleibt die Pille grau; das
 * trägt „gesperrt", „stillgelegt", „abgeschaltet", „abgelaufen". */
.rollenmarke.an { color: var(--gut); border-color: color-mix(in srgb, var(--gut) 45%, transparent); }

/* ---------------------------------------------------------------- Alarme */

.alarmliste { display: flex; flex-direction: column; gap: 1rem; }
.alarmzeile {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
}
.alarmzeile .beschriftung { margin-right: auto; min-width: 12rem; }
.alarmzeile .beschriftung strong { display: block; font-size: .88rem; }
.alarmzeile .beschriftung span { font-size: .78rem; color: var(--schrift-leise); }
.alarmzeile .grenze {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .82rem;
  color: var(--schrift-leise);
}
.alarmzeile .grenze input { width: 4.5rem; text-align: right; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- Öffentlicher Link */

.linkfeld {
  display: flex;
  align-items: center;
  gap: .5rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .4rem .5rem .4rem .7rem;
  margin-top: .6rem;
}
.linkfeld code {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: .78rem;
  color: var(--gelb-schrift);
}

/* Alter des Bildes. Orange, wenn es deutlich älter ist als der Takt – nicht
 * rot: Es ist kein Fehler, nur ein Hinweis. Regel aus TB BauCam. */
.bildalter { font-style: normal; color: var(--schrift-leise); margin-left: .4rem; }
.bildalter.alt { color: var(--achtung); font-weight: 600; }
.kameraknopf .bildalter { margin-left: 0; font-size: .72rem; }

/* Die Schärfe-Marke neben dem Bildalter (09.09.2026): BauCam zeigt zuerst
 * das 1600er, damit das Bild sofort steht, und zieht die volle Aufnahme
 * danach still nach. Ohne ein Wort dazu wäre für den Betrachter nicht zu
 * unterscheiden, ob nachgeladen wurde oder nichts gebaut ist. „4K" steht
 * dort erst, wenn das Bild wirklich da ist – ein Versprechen vorweg wäre
 * schlimmer als keins. */
.schaerfe {
  font-style: normal;
  margin-left: .5rem;
  padding: .05rem .35rem;
  border: 1px solid var(--linie);
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  color: var(--gelb-schrift);
}
.schaerfe.laedt { color: var(--schrift-leise); font-weight: 500; }

/* -------------------------------------------------------------- Nachweis
 *
 * Ein Dokument, kein Bildschirm: heller Grund, ruhige Typografie, alles auf
 * eine A4-Seite. Ein Screenshot taugt vor einem Gutachter wenig; ein Blatt mit
 * Herkunft, Zeitpunkt und Prüfsumme schon eher.
 */

body.nachweisseite { background: var(--karte); }
body.nachweisseite main { max-width: 62rem; padding-top: 1.5rem; }

.nachweisleiste {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: 1.25rem;
}

.nachweis {
  background: #fff;
  color: #111308;
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten);
  padding: 2.4rem 2.6rem 2rem;
  max-width: 52rem;
  margin: 0 auto;
}
.nachweis header {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid #d2d700;
  margin-bottom: 1.5rem;
}
.nachweis header .marke { display: flex; align-items: baseline; gap: .5rem; margin-right: auto; }
.nachweis header .marke svg { height: 17px; width: auto; color: #22240f; display: block; }
/* Auch hier die Hausfarbe (15.09.2026). Der Nachweis hat eine eigene,
   fest eingetragene Palette - er wird gedruckt und darf nicht vom Thema
   des Browsers abhaengen -, und deshalb stand hier noch das alte Grau,
   waehrend alles andere schon umgestellt war. */
.nachweis header .marke em {
  font-style: normal;
  font-family: var(--titelschrift);
  font-weight: 500;
  letter-spacing: .08em;
  font-size: .9rem;
  color: #ecf200;
}
.nachweis header .rechts { text-align: right; }
.nachweis header .rechts strong { display: block; font-family: var(--titelschrift); font-size: 1.05rem; }
.nachweis header .rechts span { font-size: .8rem; color: #66695a; }

.nachweis figure { margin: 0 0 1.5rem; }
.nachweis figure img {
  display: block;
  width: 100%;
  border-radius: 4px;
  background: #000;
}
.nachweis figcaption {
  margin-top: .5rem;
  font-size: .82rem;
  color: #66695a;
  font-variant-numeric: tabular-nums;
}

.nachweis dl.angaben {
  grid-template-columns: 11rem 1fr;
  gap: .5rem 1.5rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid #e3e4d8;
  margin-bottom: 1.25rem;
}
.nachweis dl.angaben dt { color: #66695a; font-size: .84rem; }
/* `anywhere`: Dateiname und Prüfsumme sind ein Wort ohne Fugen – ohne
   Umbrucherlaubnis schoben sie die Seite auf dem Handy ins Querscrollen. */
.nachweis dl.angaben dd { font-size: .9rem; overflow-wrap: anywhere; }
/* Auf dem Handy untereinander statt zweispaltig: 11rem Beschriftung plus
   Prüfsumme passen nicht nebeneinander auf 375 Punkte. */
@media (max-width: 640px) {
  .nachweis dl.angaben { grid-template-columns: 1fr; gap: .1rem 0; }
  .nachweis dl.angaben dt { margin-top: .55rem; }
}

.nachweis .vermerk {
  font-size: .82rem;
  line-height: 1.6;
  color: #3a3d2c;
  margin: 0 0 1.75rem;
}
.nachweis footer {
  padding-top: 1rem;
  border-top: 1px solid #e3e4d8;
  font-size: .74rem;
  color: #66695a;
}

/* Das Wetter auf dem Bildnachweis (09.09.2026 gewünscht): die Werte zum
   Zeitpunkt der Aufnahme, ein Satz über die vierundzwanzig Stunden davor
   und der Verlauf als Grafik. Auf einem Blatt Papier ist ein Satz mehr
   wert als eine Tabelle mit vierundzwanzig Zeilen; die Grafik zeigt, wann
   es geregnet hat. */
.nachweiswetter {
  margin: 1.4rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--linie-leise);
}
.nachweiswetter h2 {
  font-size: .84rem;
  font-weight: 600;
  color: var(--schrift-leise);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin: 0 0 .8rem;
}
.nachweiswetter .wetterwerte { justify-content: flex-start; }
.nachweiswetter .wetterkurz {
  margin: .9rem 0 .3rem;
  font-size: .86rem;
  color: var(--schrift);
}
.nachweiswetter .wetterkurz em { color: var(--schrift-leise); font-style: normal; }
.nachweiswetter .wetterverlauf { max-width: none; margin-top: .5rem; }
.nachweiswetter .wetterverlauf svg { height: 76px; }

@media print {
  body.nachweisseite { background: #fff; }
  body.nachweisseite main { padding: 0; max-width: none; }
  .nachweisleiste, .beispielhinweis, .seitenfuss { display: none !important; }
  .nachweis { box-shadow: none; border-radius: 0; padding: 0; max-width: none; }
  .nachweis figure img { max-height: 12cm; object-fit: contain; }
  /* Der Wetterteil gehört auf denselben Bogen wie das Bild und darf nicht
     mittendrin umbrechen. Und die Farben müssen mit: Ein Drucker lässt
     Flächen sonst weg, und dann steht dort eine leere Achse. */
  .nachweiswetter {
    break-inside: avoid;
    page-break-inside: avoid;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .nachweiswetter .wetterverlauf svg { height: 3.2cm; }
  /* Das Ablesefeld ist eine Bildschirmsache – auf Papier fährt niemand
     mit der Maus darüber. */
  .nachweiswetter .wablesung { display: none !important; }
}

/* Decke über dem Standbild, solange kein Strom läuft. Das Bild bleibt
 * sichtbar – nur so lässt sich beurteilen, wie groß es später steht. */
.startdecke {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: .9rem;
  padding: 2rem;
  text-align: center;
  background: rgba(8, 8, 8, .55);
}
.startdecke p {
  margin: 0;
  max-width: 24rem;
  color: #d8d9d2;
  font-size: .82rem;
}
.grossstart {
  padding: .7rem 1.4rem;
  font-size: .95rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .45);
}

/* Wortmarke in der Verwaltung: als Bild eingebunden statt inline, weil die
 * Seiten dort vom Server kommen und kein JavaScript zum Einsetzen brauchen.
 * Der Preis: Sie dreht im hellen Modus nicht mit, deshalb der Filter. */
.marken-logo { height: 15px; width: auto; display: block; }
:root[data-thema="hell"] .marken-logo { filter: brightness(.3) saturate(1.6); }


/* ============================================================== Übersicht
 *
 * Wörtlich übernommen aus TB BauCam Web, damit beides gleich aussieht: ein
 * durchgehender gelber Strich neben allem, was zu einer Baustelle gehört,
 * davor ein weißes Namensschild mit Balken. Der Strich grenzt ab, ohne Flächen
 * einzufärben – getönte Kacheln würden mit den Vorschaubildern streiten.
 *
 * Einzige Änderung: Die waagerecht laufende Kachelreihe heißt hier
 * `.kamerareihe`. `.reihe` ist in TB Web schon die Formularzeile.
 */

/* -------------------------------------------------------------- Übersicht
 *
 * Aufbau wie in der Mac-App: ein durchgehender gelber Strich neben allem, was
 * zu einer Baustelle gehört, davor ein weißes Namensschild mit Balken. Der
 * Strich grenzt ab, ohne Flächen einzufärben – getönte Kacheln würden mit den
 * Vorschaubildern streiten.
 */

.baustelle { display: flex; gap: 14px; align-items: stretch; margin-bottom: 3rem; }
.strich { flex: 0 0 3px; border-radius: 2px; background: rgba(210, 215, 0, .5); }
.gruppe { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1.4rem; }

/* Die Fläche umschließt nur ihren Inhalt. Ein Band über die ganze Breite liefe
 * bei den waagerecht laufenden Reihen weit aus dem Fenster hinaus. */
/* Der Anker `#b<id>`: Wer von einer Kamera zurückkommt, landet an ihrer
 * Baustelle – und die soll nicht unter der klebenden Kopfleiste liegen.
 * `scroll-margin-top` hält den Abstand frei, den der Kopf einnimmt. */
.namensschild { scroll-margin-top: 7.5rem; }
.namensschild {
  /* `align-self: flex-start` in der Flexspalte darüber: Das Schild bleibt so
   * breit wie sein Inhalt und läuft nicht über die Reihe.
   *
   * Kein `width: fit-content` – das klingt richtiger, fällt hier aber in sich
   * zusammen: Die Kinder sind selbst Flexelemente und schrumpfen dann auf
   * null, übrig bleibt die Polsterung. Einmal ausprobiert, 26 Pixel breit. */
  align-self: flex-start;
  max-width: 100%;
  display: flex;
  align-items: center;
  gap: .65rem;
  background: #fff;
  border-radius: 11px;
  padding: .5rem 1rem .5rem .6rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
  max-width: 100%;
}
.namensschild .balken {
  flex: 0 0 4px; height: 24px; border-radius: 2px; background: var(--gelb);
}
/* Auf Weiß ist das Marken-Gelb als Schrift zu hell – der Name steht dunkel,
 * das Gelb übernimmt der Balken davor. */
.namensschild h2 { margin: 0; font-size: 17px; color: var(--tinte); }
.namensschild .anzahl { font-size: .85rem; color: rgba(8, 8, 8, .55); white-space: nowrap; }
.namensschild .archivlink { display: flex; color: rgba(8, 8, 8, .5); }
.namensschild .archivlink:hover { color: #777b00; }

.block { display: flex; flex-direction: column; gap: .7rem; min-width: 0; }
.blockkopf {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--gelb);
}
.blockkopf .sinnbild { color: rgba(210, 215, 0, .8); }
.blockkopf em {
  font-style: normal;
  font-family: var(--textschrift);
  font-size: .85rem;
  color: var(--schrift-leise);
}

/* Kanäle eines Rekorders laufen nach rechts weiter, statt umzubrechen – so
 * bleibt zusammen, was zu einem Gerät gehört. */
.kamerareihe {
  display: flex;
  /* Umbrechen statt waagerecht scrollen: Eine Baustelle mit acht Kameras
   * zeigte vier und versteckte den Rest hinter einem Scrollbalken, den
   * niemand sah (02.09.2026, Celle). Die Kacheln behalten ihre Breite aus
   * dem Seitenverhältnis – ein Panorama bleibt breit – und stehen in so
   * vielen Reihen, wie sie brauchen. */
  flex-wrap: wrap;
  gap: 16px;
}

/* Feste Bildhöhe, Breite aus dem Seitenverhältnis: alle Kacheln einer Reihe
 * stehen damit auf einer Linie, ohne dass ein Bild verzerrt oder gerahmt wird. */
.kachel {
  --bildhoehe: 170px;
  position: relative;
  flex: 0 0 auto;
  /* Der Rückfallwert ist kein Schmuck: Fehlt die Angabe, wäre `calc()`
   * ungültig und die Kachel fiele auf `min-width` zurück – oder, je nach
   * Umgebung, auf die volle Breite. Genau das ist schon passiert. */
  width: calc(var(--bildhoehe) * var(--verhaeltnis, 1.7778));
  min-width: 15rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .15);
  transition: box-shadow .15s ease-out, border-color .15s ease-out;
}
.kachel:hover { box-shadow: 0 9px 18px rgba(0, 0, 0, .35); border-color: rgba(210, 215, 0, .6); }

/* Innerhalb einer Kachel ist `.bildflaeche` die **kleine** Fläche mit fester
 * Höhe – draußen ist es die große Bühne der Kameraseite. Beide heißen in
 * TB BauCam so; hier müssen die Kachelregeln deshalb genauer sein, sonst
 * gewinnt die Bühne und die Kachel wird doppelt so hoch. */
.kachel .bildflaeche {
  display: block;
  height: var(--bildhoehe);
  min-height: 0;
  max-height: none;
  background: #000;
}
.kachel .bildflaeche img {
  display: block;
  width: 100%;
  height: 100%;
  max-height: none;
  /* `contain`, nicht `cover`: Die Kachelbreite folgt dem Seitenverhältnis der
   * Kamera (`--verhaeltnis`), deshalb passt das Bild genau hinein. Mit `cover`
   * wäre ein Panorama oben und unten abgeschnitten – und dort steht die
   * Baustelle. */
  object-fit: contain;
}
.bildflaeche.laedt { animation: puls 1.6s ease-in-out infinite; }
@keyframes puls {
  0%, 100% { background-color: #111214; }
  50%      { background-color: #1d1f22; }
}
@media (prefers-reduced-motion: reduce) {
  .bildflaeche.laedt { animation: none; background-color: #17181a; }
}

.neuladen {
  position: absolute;
  top: 6px; right: 6px;
  display: grid; place-items: center;
  width: 26px; height: 26px; padding: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  opacity: 0;
  transition: opacity .15s;
}
.kachel:hover .neuladen, .neuladen:focus-visible { opacity: 1; }

.fuss { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .5rem; padding: .65rem .75rem; }

/* Der Fuß der Kamerakachel: **drei Zeilen, festes Raster.**
 *
 * Vorher eine Flex-Zeile, die sich selbst umbrach – und bei einem längeren
 * Namen rutschten die Symbole auf eine eigene Zeile, „Verpixelt" auf die
 * nächste, Adresse und Zeit auf die vierte. Vier Zeilen, jede anders
 * ausgerichtet (02.09.2026 bemängelt).
 *
 * Ein Raster mit benannten Feldern kann das nicht: Jeder Teil hat seinen
 * Platz, und ein langer Name schiebt nichts mehr weg – er bekommt die ganze
 * erste Zeile. Die mittlere Spalte ist `minmax(0, 1fr)`, damit ein langer
 * Name die Kachel nicht breiter macht, sondern selbst umbricht.
 *
 * Eigene Klasse, weil `.fuss` auch auf der Kundenseite (`baustellen.html`)
 * vorkommt – dort steht ein ganz anderer Inhalt darin.
 */
.kamerafuss {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "punkt name    name"
    "adresse adresse ampel"
    "stand   stand   symbole";
  align-items: center;
  gap: .2rem .5rem;
  padding: .65rem .75rem;
}
.kamerafuss .punkt { grid-area: punkt; margin-top: 0; }
.kamerafuss .kameraname {
  grid-area: name;
  color: var(--schrift);
  font-weight: 600;
  font-size: .92rem;
  overflow-wrap: anywhere;
}
.kamerafuss .kameraname:hover { color: var(--gelb); text-decoration: none; }
.kamerafuss .adresse { grid-area: adresse; }
.kamerafuss .stromampel { grid-area: ampel; justify-self: end; }
.kamerafuss .stand { grid-area: stand; }
.kamerafuss .symbole { grid-area: symbole; justify-self: end; }

/* **Das Wort „Bearbeiten" weicht, wenn die Kachel schmal wird.**
 *
 * Gemessen: In einer 240 Punkte breiten Kachel belegen Symbole samt Wort
 * 133 Punkte – mehr als die Hälfte. Für die Adresse blieben 73, gebraucht
 * werden 126, und sie stand wieder abgeschnitten da. In einer 300er Kachel
 * passt beides.
 *
 * Deshalb keine feste Entscheidung, sondern eine nach der **Kachel**:
 * `@container` fragt die Breite des Elternteils, nicht die des Fensters –
 * und genau darauf kommt es an, weil dieselbe Seite Kacheln in beiden
 * Breiten zeigt. Der Stift trägt `aria-label` und `title`, das Wort ist
 * also nur für das Auge, und die Kachelüberschrift führt ohnehin auf
 * dieselbe Seite.
 *
 * Kann ein Browser keine Container-Abfragen, bleibt das Wort stehen und die
 * Adresse wird knapp – der Zustand von vorher, kein neuer Schaden. */
.kachel { container-type: inline-size; }
@container (max-width: 285px) {
  .kamerafuss .symbole .wort { display: none; }
}
/* Der Versatz gilt nur dort, wo der Punkt neben mehrzeiligem Text steht
 * (Aufgabenliste). Als blankes `.punkt` schob er bis zum 03.09.2026 jeden
 * Zustandspunkt auf den Kundenseiten um 5,6 Punkte nach unten. */
.aufgabenpunkt.punkt, .zutun .punkt { margin-top: .35rem; }
.punkt.gut { background: var(--gut); }
.punkt.schlecht { background: var(--schlecht); }
.punkt.unbekannt { background: rgba(139, 141, 133, .5); }

.namen { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.namen a { color: var(--schrift); font-weight: 600; font-size: .92rem; }
.namen a:hover { color: var(--gelb); text-decoration: none; }
/* **Ganz oder gar nicht.** Adresse und Stand wurden mit `…` abgeschnitten:
 * „188.246.24.2…" ist keine Adresse, und „20:50 · gerade…" keine Angabe – man
 * musste den Zeiger daraufhalten und bekam eine Sprechblase, die die Zeile
 * darunter verdeckte (02.09.2026). Beides steht jetzt vollständig da und
 * bricht nicht mehr um: Sie steht in einer eigenen Zeile über die volle
 * Kachelbreite (siehe `.fuss .angaben`). Platz dafür hat auch das Wort
 * „Info" hergegeben, das neben dem i im Kreis nichts sagte. */
.kamerafuss .adresse, .kamerafuss .stand {
  font-size: .74rem;
  color: var(--schrift-leise);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Orange, wenn das Bild deutlich älter ist als der Takt – nicht rot: Es ist
 * kein Fehler, nur ein Hinweis, dass gerade nichts Neues ankam. */
/* `var(--achtung)` statt der festen Farbe: #d99a3c ist der Wert des
 * DUNKLEN Themas. Im hellen stand er auf weißem Grund bei 2,2 zu 1 —
 * ein Datum, das man nicht mehr liest, und ausgerechnet das soll ja
 * auffallen (09.09.2026 nachgemessen). */
.kamerafuss .stand.alt { color: var(--achtung); }

.werkzeuge {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .4rem;
}
.symbole { display: flex; gap: .7rem; color: var(--schrift-leise); }
.symbole a { display: flex; color: inherit; }
.kachel:hover .symbole { color: var(--schrift); }
.symbole a:hover { color: var(--gelb); }
.symbole a { align-items: center; gap: .3rem; }
.symbole .wort { font-size: 11px; font-weight: 600; letter-spacing: .01em; }

/* Ampel für den Livestream. Grau, solange nichts geprüft wurde – eine rote
 * Anzeige, die nur „noch nicht nachgesehen“ meint, wäre schlimmer als keine. */
.stromampel {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .6px;
  color: rgba(139, 141, 133, .5);
}
.stromampel.gut { color: var(--gut); }
.stromampel.schlecht { color: var(--schlecht); }

.beispielhinweis {
  /* Kein `display: flex`.
     
     Der Kasten enthält Fließtext mit einem Sinnbild davor und einzelnen
     hervorgehobenen Wörtern darin. In einem Flex-Kasten wird **jedes**
     Textstück zwischen zwei Elementen ein eigener Block: Auf einem breiten
     Bildschirm fiel das nicht auf, auf einem Handy zerfiel der Satz in
     „Das  Bild  ist ein  Platzhalter". Fließtext gehört in den normalen
     Textfluss; das Sinnbild reiht sich mit `vertical-align` ein. */
  display: block;
  margin-top: 2rem;
  padding: .6rem .9rem;
  border: 1px dashed var(--linie);
  border-radius: 6px;
  color: var(--schrift-leise);
  font-size: .82rem;
}
.beispielhinweis strong { color: var(--schrift); }


/* Sortieren mit Pfeilen. Im Namensschild sitzen sie auf Weiß, an der Kachel
 * auf dem Bild – deshalb zwei Fassungen derselben Sache. */
.namensschild .ordnen { display: flex; align-items: center; gap: .15rem; }
.namensschild .ordnen form { margin: 0; }
.namensschild .ordnen button,
.namensschild .ordnen a {
  display: grid;
  place-items: center;
  width: 22px; height: 22px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: rgba(8, 8, 8, .45);
  font-size: .9rem;
  line-height: 1;
  cursor: pointer;
}
.namensschild .ordnen button:hover,
.namensschild .ordnen a:hover {
  background: rgba(8, 8, 8, .08);
  color: var(--tinte);
  filter: none;
  text-decoration: none;
}
.namensschild .ordnen button:disabled { opacity: .25; cursor: default; }
.namensschild .ordnen button.weg:hover { background: rgba(224, 101, 91, .18); color: #a3271c; }

.kachel .schieben {
  position: absolute;
  top: 6px; left: 6px;
  display: flex;
  gap: 2px;
  opacity: 0;
  transition: opacity .15s;
}
.kachel:hover .schieben, .kachel .schieben:focus-within { opacity: 1; }
.kachel .schieben form { margin: 0; }
.kachel .schieben button {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.kachel .schieben button:hover { background: rgba(0, 0, 0, .8); filter: none; }
.kachel .schieben button:disabled { opacity: .3; cursor: default; }

/* Kachel ohne Passwort: sagt, was fehlt, statt ein Ersatzbild zu zeigen, das
 * man für ein Kamerabild halten könnte. */
.kachel .bildflaeche.kein-bild {
  display: grid;
  place-items: center;
  text-align: center;
  color: var(--schrift-leise);
  background: #111214;
}
.kachel .bildflaeche.kein-bild span {
  display: grid;
  justify-items: center;
  gap: .4rem;
  font-size: .82rem;
}
.kachel .bildflaeche.kein-bild em {
  font-style: normal;
  font-size: .74rem;
  color: var(--achtung);
}

/* Kurzer Überblick unter der Überschrift. Zahlen, die eine Handlung nach sich
 * ziehen, stehen in Orange – die übrigen bleiben ruhig. */
.ueberblick { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0 0 !important; }
.ueberblick span, .ueberblick a {
  border: 1px solid var(--linie);
  border-radius: 999px;
  padding: .1rem .6rem;
  font-size: .76rem;
  color: var(--schrift-leise);
}
.ueberblick a { text-decoration: none; }
/* Die Farbe steht hier ein zweites Mal, obwohl `.warnt` sie schon setzt:
 * `.ueberblick span` ist die genauere Regel und würde sie sonst mit
 * `--schrift-leise` überschreiben. */
.ueberblick span.warnt { color: var(--achtung); border-color: rgba(217, 154, 60, .45); }

/* Die eigene Gruppe hebt sich ab – sie steht oben und gehört nicht zu den
 * Kunden. Ein gelber Rand genügt; eine eingefärbte Fläche wäre zu laut. */
.karte.eigene { border-color: rgba(210, 215, 0, .45); }
.karte.eigene > h2 .sinnbild { color: var(--gelb); }

/* Suchzeile bei Downloads.
 *
 * Auswahllisten, Zeitfelder und ein Knopf haben von Haus aus verschiedene
 * Höhen – nebeneinander sieht das nach Zufall aus. Deshalb hier eine feste
 * Höhe für alles, was in der Zeile steht, und eine gemeinsame Grundlinie. */
.suchzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: .9rem;
}
/* Vorher wuchs das erste Feld (Kamera) ins Unendliche, während Tag, Von und
 * Bis schmal danebenstanden – das sah aus wie ein Versehen. Jetzt wächst nur
 * das Kamerafeld, und auch das nur bis zu einem vernünftigen Maß. */
.suchzeile .feld { flex: 0 1 auto; margin: 0; }
.suchzeile .feld:first-child { flex: 1 1 16rem; max-width: 26rem; }
/* `:not([type="checkbox"])`: Diese Regel meint Eingabefelder, die eine
 * Spalte füllen sollen – nicht Ankreuzkästchen. Ohne die Ausnahme traf
 * `width: 100%` auch das Kästchen und zog es über die ganze Feldbreite;
 * der Text daneben wurde aus seinem Kasten geschoben und lag auf der
 * Downloadseite über dem Suchen-Knopf (gemessen am 09.09.2026: Kasten
 * 931–1081, Text 1096–1218).
 *
 * `.ankreuz input { width: auto }` gab es längst – es steht nur zweitausend
 * Zeilen weiter oben, und bei gleicher Spezifität gewinnt das Spätere.
 * Eine Regel, die eine frühere still aushebelt, findet man erst, wenn
 * jemand auf die verschobene Anzeige zeigt. */
.suchzeile select,
.suchzeile input:not([type="checkbox"]):not([type="radio"]),
.suchzeile button,
.suchzeile .knopf {
  height: 2.6rem;
  box-sizing: border-box;
  width: 100%;
}
.suchzeile button { justify-content: center; }
.suchzeile .datumsknopf { justify-content: flex-start; }

/* Gefundene Aufzeichnungen: Vorschaubild, Angaben, Ladeknopf. */
/* Ein Videobild, dessen Strom ein anderes Verhältnis behauptet als die
 * Kamera wirklich hat (08.09.2026 gemessen: Kamera 45 sendet ihren Substrom
 * als 640×480 mit SAR 1:1, obwohl der Hauptstrom 16:9 zeigt). Die Seite
 * setzt dann `--formzahl` auf das echte Verhältnis und diese Klasse aufs
 * Video; `fill` zieht das gequetschte Bild wieder auseinander.
 *
 * `width` statt `aspect-ratio` allein: Ein Element mit `aspect-ratio` und
 * ohne bestimmte Breite hätte keine. Die Breite ist deshalb ausgerechnet —
 * so breit, wie es die Fläche zulässt, und höchstens so breit, dass die
 * Höhe unter 68vh bleibt. Dieselbe Grenze wie bei `.bildflaeche img`. */
.bildflaeche video.formkorrektur {
  width: min(100%, calc(68vh * var(--formzahl, 1.7778)));
  height: auto;
  max-height: none;
  aspect-ratio: var(--bildform, 16 / 9);
  object-fit: fill;
}

/* Die Zeile „Datenschutz-Freigabe" unter jeder Ampelzeile (08.09.2026).
 * Abgesetzt durch eine feine Linie darüber, damit sie nicht als weiterer
 * Befund der Ampel gelesen wird — sie beantwortet eine andere Frage. Der
 * Knopf steht rechts und ist flach: Er ist ein Weg, keine Aufforderung. */
.freigabezeile {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin-top: .6rem; padding-top: .6rem;
  border-top: 1px solid var(--linie-leise);
}

/* Das Häkchen „Zeit im Bild" in der Bedienleiste der Wiedergabe
 * (08.09.2026). Es steht neben dem Knopf „Diesen Abschnitt laden" und darf
 * ihn nicht überstimmen: kleiner, leise, und es bricht in der Leiste nicht
 * um – sonst rutschte der Knopf in die nächste Zeile. */
.zeitwahl {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .78rem; color: var(--schrift-leise);
  white-space: nowrap; margin-right: .2rem;
}

/* ------------------------------------------------- Bild-Links (08.09.2026)
 *
 * Vorher eine Tabelle mit vier Spalten, deren letzte eine lange Adresse und
 * fünf Knöpfe nebeneinander trug – und `table.liste` hatte in dieser Datei
 * keine einzige Regel. Eine Tabelle wird so breit, wie ihr breitester
 * Inhalt es verlangt; die Seite lief über den rechten Rand hinaus.
 *
 * Jetzt eine Liste aus Kästen. Jeder bricht für sich um, nichts darin
 * erzwingt eine Mindestbreite (`min-width: 0` an jeder Stelle, an der ein
 * Flex-Kind sonst seine Inhaltsbreite behielte). Damit kann die Liste nicht
 * breiter werden als die Seite – auf einem schmalen Bildschirm stapelt sie
 * sich, statt zu schieben. */
.bildlinkliste { display: flex; flex-direction: column; gap: .7rem; min-width: 0; }

.bildlinkzeile {
  min-width: 0;
  border: 1px solid var(--linie-leise);
  border-radius: var(--rund);
  padding: .8rem .9rem;
  background: var(--karte-hell);
}
.bildlinkzeile .kopfteil {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .3rem 1rem; min-width: 0;
}
/* Der Name darf schrumpfen, nicht schieben: ohne `min-width: 0` behält ein
 * Flex-Kind seine Inhaltsbreite und drückt die Zeile auseinander. */
.bildlinkzeile .wer { min-width: 0; margin-right: auto; }
.bildlinkzeile .wer strong { display: block; font-size: .92rem; }
.bildlinkzeile .stand { white-space: nowrap; }

/* Die Handgriffe stehen in einer eigenen Reihe und brechen um. Die
 * `<form>`-Elemente sind nur Hüllen um je einen Knopf – ohne `display:
 * contents` stünden sie als eigene Blöcke dazwischen. */
.bildlinkzeile .handgriffe {
  display: flex; flex-wrap: wrap; gap: .3rem .5rem; margin-top: .5rem;
}
.bildlinkzeile .handgriffe form { margin: 0; display: contents; }

.bildlinkzeile .ordnerwahl {
  display: flex; flex-wrap: wrap; gap: .45rem; margin: .6rem 0 0; min-width: 0;
}
.bildlinkzeile .ordnerwahl select { flex: 1 1 16rem; min-width: 0; max-width: 26rem; }

/* Die Zahl neben der Überschrift – wie viele Kameras in dieser Gruppe
 * stehen. Leise, damit sie die Überschrift nicht überstimmt. */
.karte > h2 .anzahl {
  margin-left: .1rem; font-size: .8rem; font-weight: 500;
  color: var(--schrift-leise);
  background: var(--karte-hell); border: 1px solid var(--linie);
  border-radius: 999px; padding: .05rem .5rem;
}

.listenkopf {
  display: flex;
  align-items: center;
  gap: .9rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
}
.listenkopf button { margin-left: auto; }

.dateiliste { display: flex; flex-direction: column; gap: .6rem; }
.dateikachel {
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: .6rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  background: var(--karte);
}
.dateikachel img {
  width: 8rem;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: calc(var(--rund) - 2px);
  background: #000;
  flex: 0 0 auto;
  display: block;
}
.dateiangaben { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.dateikachel > .knopf { margin-left: auto; flex: 0 0 auto; }

@media (max-width: 640px) {
  .dateikachel { flex-wrap: wrap; }
  .dateikachel > .knopf { margin-left: 0; width: 100%; }
}

/* Vorschaurahmen im Kameraformular.
 *
 * Keine feste Höhe: Die Kameras liefern 16:9, 4:3 und – bei den
 * Doppelsensor-Geräten – 32:9. Jede feste Höhe passt für höchstens eine davon
 * und schneidet den Rest ab oder lässt Balken stehen. Die Breite ist bekannt,
 * die Höhe ergibt sich aus dem Bild. */
.vorschaurahmen {
  background: #000;
  line-height: 0;          /* sonst steht unter dem Bild ein Zeilenrest */
  min-height: 4rem;
}
.vorschaurahmen img { width: 100%; height: auto; display: block; }

/* ---------------------------------------------------------------- Tablet */

@media (max-width: 1100px) {
  .buehne { grid-template-columns: minmax(0, 1fr); }
  .spalte { max-height: 26rem; }
  .bildraster { grid-template-columns: repeat(3, 1fr); }
}

/* Bauarchiv auf dem Tablet (19.09.2026): die kleinen Bilder bleiben links.
 *
 * Über 1100 Punkten stehen sie links (Schreibtisch), unter 640 auch
 * (Handy, 7,4rem, Wunsch vom 02.09.2026). Nur dazwischen – genau dem
 * Bereich der Tablets – rutschten sie **über** das Bild: eine 416 Punkte
 * hohe Leiste, und das Bild fing auf einem iPad quer (1024 × 768) bei
 * 724 Punkten an, also außerhalb des Schirms. Hochkant bei 780 von 1024.
 * Wer den Reiter aufmachte, sah Kacheln und musste erst rollen.
 *
 * Also auch hier zwei Spalten, mit einer schmaleren Leiste (12rem statt
 * 22rem), eine Aufnahme je Reihe. Quer bleiben dem Bild ~760 Punkte,
 * hochkant ~510 – und es steht sofort da. Nur das Bauarchiv, erkannt an
 * seiner Bilderleiste; Zeitraffer und Live behalten die eine Spalte. */
@media (min-width: 641px) and (max-width: 1100px) {
  .buehne:has(.bildraster) {
    grid-template-columns: 12rem minmax(0, 1fr);
    gap: 1rem;
  }
  .buehne:has(.bildraster) .bildraster { grid-template-columns: 1fr; }
  /* „Archiv" und der Datumsknopf nebeneinander waren 197 Punkte in 191 –
     untereinander wie am Handy, dann ragt nichts über den Rand. */
  .buehne:has(.bildraster) .spaltenkopf {
    flex-direction: column; align-items: stretch; gap: .3rem;
  }
}
/* Hochkant (768 breit) bleiben dem Bild neben der Leiste 518 Punkte. Zwei
 * Dinge gehen darin nicht auf, gemessen am 19.09.2026: die Standzeile mit
 * den Blätterknöpfen (530 Punkte, der Zeitstempel brach in zwei Zeilen
 * und die 4K-Marke war abgeschnitten) und das Wetterband, dessen Zahlen
 * neben dem Diagramm standen und links wie rechts angeschnitten waren.
 * Beide bekommen hier dieselbe Form wie am Handy: Blätterknöpfe in eine
 * eigene Reihe, das Diagramm unter die Zahlen. Quer (1024) hat die Spalte
 * 775 Punkte, dort bleibt alles nebeneinander. */
@media (min-width: 641px) and (max-width: 900px) {
  .buehne:has(.bildraster) .standzeile.mit-blaettern { flex-wrap: wrap; row-gap: .4rem; }
  .buehne:has(.bildraster) .standzeile .blaettern {
    flex: 1 1 100%; order: 9; justify-content: center; gap: .4rem .6rem;
  }
  .buehne:has(.bildraster) .wetterband.zweispaltig {
    grid-template-columns: minmax(0, 1fr);
    gap: .9rem;
  }
}

@media (max-width: 900px) {
  .kopfzeile { flex-wrap: wrap; gap: .6rem .9rem; padding: .6rem 1rem; }
  .module { order: 3; width: 100%; margin-right: 0; }
  .kopfwerkzeuge { margin-left: auto; }
  main { padding: 1.25rem 1rem 3rem; }
  .tafelkopf .wortmarke { display: none; }
  .bildraster { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 640px) {
  .bildraster { grid-template-columns: 1fr; }
  .ortknopf { max-width: 10rem; }
}

@media print {
  .kopf, .tafelfuss, .aktionsleiste { display: none; }
}

/* ------------------------------------------------------------------ Export
 *
 * Faltet sich unter dem Verlauf auf. Bewusst kein eigenes Fenster: Der
 * Zeitraum gehoert sichtbar neben das Diagramm, dessen Ausschnitt er meint. */
.exportfeld {
  margin: -.4rem 0 1.1rem;
  padding: .95rem 1.05rem;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
}
.exportzeile { display: flex; gap: .8rem; align-items: flex-end; flex-wrap: wrap; }
.exportzeile .feld { margin: 0; flex: 0 1 11rem; }
.exportzeile .feld input { width: 100%; }
.exportschnell {
  display: flex; gap: .4rem; align-items: center;
  flex-wrap: wrap; margin-top: .75rem;
}
.exportschnell .knopf { padding: .25rem .6rem; font-size: .78rem; }
#exportauf.an { background: var(--karte-hell); color: var(--schrift); }

/* -------------------------------------------------------------- Blaettern
 *
 * Sitzt zwischen Kopfzeile und Diagramm: Der Zeitraum gehoert dorthin, wo
 * das Auge ohnehin hinwandert, bevor es die Kurve liest. */
.zeitblaetter {
  display: flex; align-items: center; justify-content: center;
  gap: .5rem; margin: 0 0 .9rem;
}
.zeitblaetter strong {
  min-width: 13rem; text-align: center;
  font-size: .85rem; font-weight: 500;
}
.zeitblaetter .knopf { padding: .3rem .55rem; line-height: 1; }
.zeitblaetter .knopf[disabled] { opacity: .3; pointer-events: none; }

/* ----------------------------------------------------------- Adressbuch
 *
 * Über hundert Einträge: Die Liste muss sich auf einen Blick überfliegen
 * lassen, deshalb je Zeile nur Name, Anschrift und Nummern. Die vollen
 * Felder klappen erst beim Anklicken auf. */
.kontaktkarte { padding: .85rem 1.1rem; margin-bottom: .5rem; }
.kontaktkarte > summary {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.6fr) auto auto auto;
  gap: .9rem; align-items: baseline;
  cursor: pointer; list-style: none;
}
.kontaktkarte > summary::-webkit-details-marker { display: none; }
.kontaktkarte > summary::before {
  content: "›"; position: absolute; margin-left: -.85rem;
  color: var(--schrift-leise); transition: transform .12s ease;
}
.kontaktkarte[open] > summary::before { transform: rotate(90deg); }
.kontaktkarte .kfirma { font-weight: 500; }
.kontaktkarte .kort,
.kontaktkarte .knummern { color: var(--schrift-leise); font-size: .85rem; }
.kontaktkarte[data-still] > summary { opacity: .55; }
.kontaktkarte:hover { border-color: var(--linie-hell, var(--gelb)); }

.kontaktgitter {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: .7rem .9rem;
}
.kontaktgitter .feld { margin: 0; }

@media (max-width: 800px) {
  .kontaktkarte > summary { grid-template-columns: 1fr; gap: .2rem; }
}
.auftraggeber {
  display: flex; align-items: center; gap: .5rem;
  margin: .4rem 0 0; flex-wrap: wrap;
}
.auftraggeber select {
  /* `max-width: 26rem` allein reicht nicht: Ein `<select>` wird so breit wie
     seine längste Auswahl, und „GOLDBECK Südwest GmbH · Frankfurt am Main"
     misst 416 Punkte – auf einem 375 Punkte breiten Bildschirm schob es die
     ganze Seite seitwärts (02.09.2026 gemessen). `min-width: 0` erlaubt dem
     Feld zu schrumpfen, `100%` deckelt es am Kasten; der lange Name wird dann
     vom Browser gekürzt, die Auswahl selbst zeigt ihn vollständig. */
  width: auto;
  min-width: 0;
  max-width: min(26rem, 100%);
  padding: .25rem .5rem; font-size: .82rem;
}

/* Kurze Meldung am unteren Rand — der Ersatz für `alert()`.
 *
 * Unten statt oben: Auf dem Handy ist der obere Rand von der Kopfzeile und
 * der Statusleiste des Geräts belegt, und der Daumen liegt unten. Der Abstand
 * nach unten rechnet die Tastenleiste des iPhone mit ein (`safe-area`), sonst
 * liegt die Meldung unter dem Balken zum Schließen der App. */
.meldung {
  position: fixed;
  left: 50%;
  bottom: calc(1.2rem + env(safe-area-inset-bottom, 0px));
  z-index: 200;
  max-width: min(32rem, calc(100vw - 2rem));
  padding: .7rem 1.1rem;
  border-radius: var(--rund);
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-left: 4px solid var(--schlecht);
  color: var(--schrift);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .4);
  font-size: .92rem;
  line-height: 1.4;
  /* Unsichtbar und unantastbar, solange nichts zu melden ist. */
  opacity: 0;
  transform: translate(-50%, .6rem);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.meldung.zeigt { opacity: 1; transform: translate(-50%, 0); }
.meldung.gut { border-left-color: var(--gut); }
/* Wer Bewegung im System abgeschaltet hat, bekommt sie auch hier nicht. */
@media (prefers-reduced-motion: reduce) {
  .meldung { transition: none; }
}

/* Das Zeitfenster der Verpixelung: zwei Uhrzeiten nebeneinander, der
 * erklärende Satz darunter über die volle Breite. */
.zeitfenster { display: flex; flex-wrap: wrap; gap: .9rem; align-items: flex-end; }
.zeitfenster .feld { margin: 0; flex: 0 1 9rem; }

/* Vollbild-Ersatz für Geräte ohne Vollbild-Schnittstelle – vor allem das
 * iPhone. Kein echtes Vollbild (die Leisten des Browsers bleiben), aber der
 * Kasten liegt über der ganzen Seite statt in seiner Tafel.
 *
 * `100dvh` statt `100vh`: Auf dem Handy zählt `vh` die Adressleiste mit,
 * die beim Scrollen verschwindet – das Bild ragte unten heraus. `dvh` misst,
 * was gerade wirklich zu sehen ist. Für ältere Browser steht `vh` davor. */
.vollbild-ersatz {
  position: fixed;
  inset: 0;
  z-index: 300;
  width: 100vw !important;
  max-width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  max-height: none !important;
  margin: 0 !important;
  background: #000;
  display: grid;
  place-items: center;
  /* Kein Rollen, kein Seitenzoom unter dem Vollbild (23.09.2026): `overflow:
     hidden` am body hält iOS nicht vom Rollen ab – der Finger auf dem
     Bild gehört dem Wischen und dem Zoom (mittel/vollbild.js, zoom.js). */
  touch-action: none;
  overscroll-behavior: none;
}
.vollbild-ersatz img, .vollbild-ersatz video {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: contain;
}
.vollbild-ersatz .vergleich {
  height: min(100dvh, calc(100vw * 9 / 16));
  width: auto;
  max-width: 100vw;
}
/* Solange der Ersatz offen ist, soll die Seite darunter nicht mitscrollen. */
body.vollbild-offen { overflow: hidden; }
.vollbild-schliessen {
  position: fixed;
  z-index: 301;
  top: calc(.8rem + env(safe-area-inset-top, 0px));
  right: calc(.8rem + env(safe-area-inset-right, 0px));
  padding: .5rem 1rem;
  border: 0;
  border-radius: 999px;
  background: rgba(0, 0, 0, .65);
  color: #f2f2ee;
  font: inherit;
  font-size: .9rem;
  /* Fingerkuppe, nicht Mauszeiger: mindestens 44 Punkte hoch. */
  min-height: 44px;
}

/* ================================================================= Handy
 *
 * TB Web wird auf der Baustelle benutzt, im Stehen, mit einer Hand. Bis
 * hierher war die Seite auf einem Handy zwar lesbar, aber für die Maus
 * gebaut. Drei Dinge waren an echten Geräten nachweisbar falsch:
 *
 * 1. Die Modulleiste brach auf zwei Zeilen um und belegte mit der Kopfzeile
 *    zusammen 180 von 812 Punkten – ein Fünftel des Bildschirms, bevor der
 *    erste Inhalt kam.
 * 2. Neunzehn Knöpfe und Verweise waren niedriger als 44 Punkte. Das ist
 *    das Maß einer Fingerkuppe; darunter trifft man daneben.
 * 3. Die Ränder des iPhone – Kerbe oben, Balken unten – waren nicht
 *    berücksichtigt.
 */

/* Von oben nach unten ziehen frischt die Seite auf (20.09.2026).
 *
 * Das Band hängt über dem oberen Rand und wird mit dem Finger
 * hereingezogen; `mittel/rahmen.js` schiebt es. Es fängt keine Berührung
 * ab (`pointer-events: none`) – darunter liegt die Kopfzeile. */
.auffrischband {
  position: fixed;
  inset-inline: 0;
  top: calc(env(safe-area-inset-top, 0px) + .4rem);
  z-index: 400;
  display: flex;
  justify-content: center;
  pointer-events: none;
  transform: translateY(-100%);
}
.auffrischband.federt { transition: transform .22s ease-out; }
.auffrischband span {
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: 999px;
  box-shadow: var(--schatten-klein);
  padding: .4rem .9rem;
  font-size: .82rem;
  color: var(--schrift-leise);
  white-space: nowrap;
}
.auffrischband.laeuft span { color: var(--schrift); }
@media (prefers-reduced-motion: reduce) {
  .auffrischband.federt { transition: none; }
}

/* Der unsichtbare Schalter für den haptischen Tick (mittel/rahmen.js).
   Nicht `display: none` – ein Schalter, der nicht gezeichnet wird, klopft
   auch nicht. Also da, aber außerhalb des Schirms. */
.haptik {
  position: fixed;
  left: -100px;
  top: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/* Nur am Finger: Das Auffrischen des Browsers selbst wird abgeschaltet,
   damit nicht zwei Gesten dasselbe tun – unsere zeigt dabei, wie weit
   noch zu ziehen ist. Am Zeigergerät bleibt alles, wie es war. */
@media (pointer: coarse) {
  html { overscroll-behavior-y: contain; }
}

@media (max-width: 640px) {

  /* Die Modulleiste in **eine** Zeile, die seitlich geschoben wird.
     Das ist auf einem Handy das gewohnte Muster und spart die zweite Zeile.
     `scroll-snap` lässt jeden Punkt sauber einrasten, statt halb im Rand zu
     stehen. */
  .module {
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* Der Streifen darf über den Seitenrand hinauslaufen, sonst sieht es
       aus, als wäre er zu Ende. */
    margin-inline: -1rem;
    padding-inline: 1rem;
  }
  .module::-webkit-scrollbar { display: none; }
  .module > * { flex: 0 0 auto; scroll-snap-align: start; }

  /* Der Rand der Leiste blendet aus, solange dort noch etwas liegt –
     die Klassen setzt mittel/rahmen.js beim Rollen. Ohne JavaScript
     bleibt die Leiste unverändert schiebbar. */
  .module.mehr-rechts {
    -webkit-mask-image: linear-gradient(to right, #000 78%, transparent 100%);
    mask-image: linear-gradient(to right, #000 78%, transparent 100%);
  }
  .module.mehr-links {
    -webkit-mask-image: linear-gradient(to left, #000 78%, transparent 100%);
    mask-image: linear-gradient(to left, #000 78%, transparent 100%);
  }
  .module.mehr-links.mehr-rechts {
    -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 12%,
                                        #000 88%, transparent 100%);
    mask-image: linear-gradient(to right, transparent 0%, #000 12%,
                                #000 88%, transparent 100%);
  }

  /* Kopfzeile schlanker: Der Ortname darf mehr Platz haben, die Wortmarke
     weniger – wer auf der Seite ist, weiß, wo er ist. */
  .kopfzeile { padding: .45rem .8rem .35rem; gap: .45rem .6rem; }
  .marke small { display: none; }
  .ortknopf { max-width: none; padding: .3rem .6rem; }
  .module a, .module span { padding: .3rem .5rem; }
  main { padding: 1rem .8rem 3rem; }

  /* ----------------------------------------- Der Kopf in einer Zeile
   *
   * Gemessen am 19.09.2026 auf 375 × 812: Wortmarke, Ortswahl und die
   * Werkzeuge standen in drei Zeilen untereinander, mit der Modulleiste
   * zusammen 191 von 812 Punkten – fast ein Viertel des Schirms, bevor
   * das Bild anfing, das der Kunde sehen will. Das Bild selbst war
   * 348 × 196 Punkte groß.
   *
   * Jetzt: eine Zeile. Was dafür weichen muss, ist das, was man ohnehin
   * weiß oder nicht braucht – „Web" hinter der Wortmarke, die Anschrift
   * unter dem Baustellennamen (sie steht weiterhin in der aufgeklappten
   * Liste) und die Beschriftung des Adminknopfes, den nur wir sehen.
   * Nichts davon verschwindet, es steht nur nicht mehr im Weg.
   *
   * Die Fingermaße bleiben: Die Knöpfe behalten ihre 44 Punkte Höhe. */
  .marke em { display: none; }
  .marke svg { height: 12px; }
  /* Grundmaß null, damit die Ortswahl den Rest der Zeile nimmt und die
     Werkzeuge nicht in eine zweite Zeile drückt – mit 8rem Grundmaß war
     der Kopf wieder 161 statt 109 Punkte hoch. */
  .ortwahl, .ortfest { flex: 1 1 0; min-width: 4rem; }
  .ortknopf { width: 100%; }
  .ortknopf .ortzeilen small, .ortfest .ortzeilen small { display: none; }
  .ortknopf .ortzeilen .name, .ortfest .ortzeilen .name {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .kopfwerkzeuge { flex: 0 0 auto; flex-wrap: nowrap; }
  /* Der Weg zurück ins Adminportal als reines Zeichen. Der Titel bleibt,
     wer lange tippt, liest ihn. */
  .kopfzeile .zurverwaltung .wort { display: none; }
  .kopfzeile .zurverwaltung { padding-inline: .55rem; }

  /* Die Bildtafel randlos: Der Rahmen kostete links und rechts je 13
     Punkte, die dem Bild fehlten. Nur Tafeln mit Bild – eine Textkarte
     ohne Rand sähe aus, als wäre der Rahmen vergessen worden.

     Und nur auf einspaltigen Bühnen (BauCam, Live): Im Bauarchiv steht
     links die Bilderleiste, dort zöge ein negativer Rand die Tafel in die
     Leiste hinein und rechts über den Schirm hinaus – gemessen am
     19.09.2026, die Wetterwerte standen halb außerhalb. */
  .buehne.ohne-spalte > .tafel:has(.bildflaeche) {
    margin-inline: -.8rem;
    border-inline: 0;
    border-radius: 0;
  }

  /* Beim Blättern bleibt nur die Modulleiste oben – nicht die ganzen drei
     Zeilen (146 Punkte auf einem 812 Punkte hohen Bildschirm). Der Kopf
     klebt mit negativem Abstand: So viel, wie über der Modulleiste liegt,
     rutscht hinaus; `--kopfversatz` misst mittel/thema.js. */
  .kopf { top: calc(-1 * var(--kopfversatz, 0px)); }

  /* Werkzeuge der Kopfzeile. Sie stehen hier schon in eigener Zeile (der
     Umbruch kommt aus der 900px-Regel), waren aber 395 px breit bei 349 px
     Platz – „Abmelden" ragte 33 px über den rechten Rand. Enger gesetzt
     passen alle vier in eine Zeile (gemessen 342 px); wird es noch schmaler
     (320 px), bricht der letzte Knopf um, statt hinauszulaufen. */
  .kopfwerkzeuge { flex: 0 1 auto; min-width: 0; max-width: 100%; flex-wrap: wrap; gap: .35rem; }
  .kopfzeile .kopfwerkzeuge .knopf.umrandet,
  .kopfzeile .kopfwerkzeuge button.umrandet { padding-inline: .7rem; font-size: .82rem; }

  /* Suchleiste: Suchfeld über die volle Breite, Auswahl nicht breiter als
     die Seite. Gilt für Benutzer und Protokoll – beide bauen die Leiste gleich. */
  .adminleiste input[type="search"] { flex: 1 1 100%; width: 100%; }
  .adminleiste select { max-width: 100%; }

  /* Fußzeilen der Tafeln untereinander statt nebeneinander – nebeneinander
     wurden die Knöpfe schmaler als ihre Beschriftung. */
  .tafelfuss { flex-wrap: wrap; gap: .5rem; }
  .tafelfuss .rechts { margin-left: 0; width: 100%; display: flex;
                       flex-wrap: wrap; gap: .5rem; }
  .tafelfuss .rechts > * { flex: 1 1 8rem; justify-content: center; }
  /* „Vergrößerung" sitzt in einem Hüllen-Span (fürs Klappmenü). Der Span
     wurde gestreckt, der Knopf darin nicht – er stand schmaler da als die
     drei anderen (23.09.2026). Der Knopf füllt jetzt seine Hülle. */
  .tafelfuss .rechts > .vergroesserung { display: flex; }
  .tafelfuss .rechts > .vergroesserung > button { flex: 1 1 auto; width: 100%; justify-content: center; }
  /* Der Satz „Bild wird alle 10 Minuten erneuert · Personen und Fahrzeuge
     sind verpixelt" nur am Rechner – am Telefon nahm er zwei Zeilen und
     sagte nichts, was der grüne Punkt nicht sagt (23.09.2026). Störung und
     „nicht gemeldet" bleiben stehen, die sind eine Auskunft. */
  .tafelfuss .nurbreit { display: none; }

  /* Videofläche in fester Form.
     Ohne Poster und ohne Strom hat ein <video> keine Maße – 300×150, also 2:1 –
     und die Fläche ist so lange schmaler und flacher als das Bild, das gleich
     kommt. Auf dem Handy springt dann die ganze Tafel, und der weiche
     Wartegrund (cover + scale) wird oben und unten beschnitten. Deshalb hier:
     volle Breite, 16:9 als Vorgabe, das Video eingepasst (`contain` – nie
     beschnitten, nie gezerrt). Kennt der Strom seine Maße, setzt die Seite
     --bildform (wiedergabe.html, live.html, zeitraffer.html), und die Fläche
     übernimmt sie – eine Panoramakamera ist kein 16:9.
     `grid-template-rows: minmax(0, 1fr)` ist Pflicht: Sonst richtet sich die
     Rasterzeile nach dem Inhalt, nicht nach der Fläche, und `height: 100%`
     am Video meint die Zeile. Gemessen in WebKit bei 32:9: Fläche 93 px,
     Video 186 px, mittig – oben und unten abgeschnitten. */
  .bildflaeche:has(video) {
    width: 100%;
    aspect-ratio: var(--bildform, 16 / 9);
    grid-template-rows: minmax(0, 1fr);
  }
  .bildflaeche:has(video) video {
    width: 100%;
    height: 100%;
    max-height: none;
    object-fit: contain;
  }
  /* Luft zwischen Bild und Zeitleiste: Auf dem Handy sind beide gleich breit,
     ohne Abstand klebt die Leiste am schwarzen Kasten (gemessen 1 px). */
  .zeitleiste { margin-top: .8rem; }
  /* Der senkrechte Trenner hat in einer umbrechenden Leiste keinen Platz: Er
     blieb als 1-px-Strich am Zeilenende stehen, sobald „Verpixelung aus“ in
     die nächste Zeile rutschte. */
  .tafelfuss .trenner { display: none; }
  /* 33 px breit waren die flachen Knöpfe – eine Fingerkuppe braucht 44. */
  .tafelfuss .flach { min-width: 44px; }
}

/* Nur auf Geräten, die mit dem Finger bedient werden.
 *
 * Bewusst nicht an der Fensterbreite festgemacht: Ein schmales Fenster auf
 * dem Mac wird weiter mit der Maus bedient, und dort sind 44 Punkte hohe
 * Menüpunkte unnötig grob. `pointer: coarse` fragt das Gerät, nicht die
 * Größe. */
@media (hover: none) and (pointer: coarse) {
  /* **Alle** Knöpfe, nicht eine Aufzählung.
     Die erste Fassung zählte einzelne Klassen auf – und jede Seite, die
     nicht in der Liste stand, behielt ihre Mausknöpfe. Beim Rundgang am
     29.08. hatten Bauarchiv 19, Zeitraffer 14 und Downloads 12 zu kleine
     Ziele. Eine allgemeine Regel kann keine Seite vergessen; was wirklich
     kleiner sein muss, steht als Ausnahme darunter. */
  button, .knopf, .module a, .ortliste a, .kontoliste a {
    min-height: 44px;
  }
  /* Ausnahme Kalendertage: 44 Punkte sprengten das Wochenraster. 2,25rem
     (36 Punkte) ist der übliche Kompromiss für Kalender. */
  .kalendergitter .tag { min-height: 0; width: 100%; max-width: 2.25rem;
                         height: 2.25rem; line-height: 2.25rem; }
  .kalender { width: 18rem; }
  .module a { display: inline-flex; align-items: center; }

  /* Zeilen in Listen – Aufzeichnungen, Dateien – ebenfalls fingergerecht.
     `.abschnittsliste li`, nicht `.abschnitt`: Diese Klasse tragen nur die
     Balken der Zeitleiste, und die wurden damit 52 px hoch in einem 46-px-
     Kasten – die Stundenzahlen darunter waren verdeckt. */
  .abschnittsliste li, .dateikachel { min-height: 52px; }

  /* Streifen, die seitlich geschoben werden, mit Schwung ausrollen lassen.
     Ohne das bleiben sie abrupt stehen und fühlen sich kaputt an. */
  .kamerastreifen, .module, .bildraster {
    -webkit-overflow-scrolling: touch;
  }

  /* **Sechzehn Punkte in jedem Eingabefeld** (09.09.2026).

     Safari auf dem iPhone zoomt die ganze Seite heran, sobald man in ein
     Feld tippt, dessen Schrift kleiner als 16 Punkte ist — und zoomt
     danach nicht wieder heraus. Die Seite steht dann breiter als der
     Bildschirm, der Knopf „Anmelden" liegt rechts daneben, und das bei
     jedem Feld aufs Neue.

     Die Grundschrift des Hauses ist 15 Punkte, Teilbereiche gehen auf 13
     herunter; die Felder erben das über `font: inherit`. Getroffen hätte
     es die Anmeldung, jedes Suchfeld und jedes Formular zum Bearbeiten.

     `16px` und nicht `1rem`: Es geht um die Schwelle von Safari, und die
     ist in Punkten festgelegt — wer die Grundschrift des Browsers
     verstellt, verschöbe sonst genau die Grenze, um die es hier geht.
     Nur auf Fingergeräten; am Schreibtisch bleibt die feinere Schrift. */
  input, select, textarea,
  /* Auch die Felder, die eine eigene, kleinere Schrift bekommen — genau
     die tippt man am Handy am häufigsten an. Zwei weitere stehen im
     Stylesheet WEITER UNTEN als diese Regel und würden sie überstimmen;
     die stehen deshalb am Dateiende. */
  .tafelsuche, .tafelfuss select, .tafelfuss input,
  .spaltenkopf select, .spaltenkopf input[type="date"] {
    font-size: 16px;
  }
}

/* Die Ränder des Geräts: Kerbe oben, Balken unten, abgerundete Ecken seitlich.
 * Ohne das liegt die Kopfzeile unter der Uhr des iPhone und der letzte Knopf
 * unter dem Balken zum Schließen der App. */
@supports (padding: env(safe-area-inset-top)) {
  .kopfzeile { padding-top: calc(.5rem + env(safe-area-inset-top)); }
  main {
    padding-left: max(.8rem, env(safe-area-inset-left));
    padding-right: max(.8rem, env(safe-area-inset-right));
    padding-bottom: calc(3rem + env(safe-area-inset-bottom));
  }
}

/* Zustand und Alter an der Kamerakachel der Übersicht.
 *
 * Der Punkt sitzt **im** Bild, oben rechts: Dort ist er beim Überfliegen
 * sofort zu sehen, ohne dass die Kachel höher wird. Die Zeile darunter sagt,
 * was das Bild zeigt und von wann es ist – ein Vorschaubild ohne Zeitangabe
 * ist eine Behauptung. */
.kamerakachel .bild { position: relative; }
.kamerakachel .zustand {
  position: absolute;
  top: .5rem; right: .5rem;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .18rem .5rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, .62);
  color: #f2f2ee;
  font-size: .72rem;
  line-height: 1.4;
  letter-spacing: .02em;
}
.kamerakachel .kachelzeile {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  padding: 0 .7rem .6rem;
}
.kamerakachel .kachelzeile em { font-style: normal; }

/* Der Einrichtungskasten für die Verpixelung.
 *
 * Vorher war das ein umrandeter Verweis in einer grauen Zeile zwischen zwei
 * Absätzen Kleingedrucktem – er ging unter, obwohl er der wichtigste Weg
 * dieser Seite ist. Jetzt ein eigener Kasten mit gefülltem Knopf, und mit
 * gelber Kante links, damit er sich vom Fließtext abhebt, ohne zu schreien.
 *
 * `dringend` färbt ihn um, wenn bei einer verpixelten Kamera das Profil
 * fehlt. Ein Knopf, der immer gleich auffällt, wird genauso überlesen wie
 * einer, der nie auffällt. */
.einrichtkasten {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1rem 0 0;
  padding: .9rem 1rem;
  border: 1px solid var(--linie);
  border-left: 4px solid var(--gelb);
  border-radius: var(--rund);
  background: var(--karte-hell);
}
.einrichtkasten .wortlaut {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  flex: 1 1 16rem;
  min-width: 0;
}
.einrichtkasten .wortlaut strong {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-size: .98rem;
}
.einrichtkasten > .knopf { flex: 0 0 auto; }
.einrichtkasten.dringend {
  border-left-color: var(--achtung);
  background: color-mix(in srgb, var(--achtung) 9%, var(--karte-hell));
}
.einrichtkasten.dringend .wortlaut strong { color: var(--achtung); }
/* Auf dem Handy untereinander, Knopf über die volle Breite. */
@media (max-width: 640px) {
  .einrichtkasten > .knopf { flex: 1 1 100%; justify-content: center; }
}

/* Bilderstreifen zum Einrichten der Verpixelung.
 *
 * Waagerecht schiebbar statt umbrechend: Der Streifen soll nicht die halbe
 * Seite füllen und die Werkzeuge nach unten drücken – er ist eine Auswahl,
 * kein Inhalt. */
.einrichtstreifen {
  display: flex;
  gap: .5rem;
  overflow-x: auto;
  padding: .7rem .2rem .2rem;
  -webkit-overflow-scrolling: touch;
}
.einrichtbild {
  position: relative;
  flex: 0 0 auto;
  width: 8.5rem;
  padding: 0;
  border: 2px solid var(--linie);
  border-radius: var(--rund);
  background: #000;
  color: var(--schrift);
  cursor: pointer;
  overflow: hidden;
  line-height: 0;
}
.einrichtbild.an { border-color: var(--gelb); }
.einrichtbild img { width: 100%; height: 4.8rem; object-fit: cover; display: block; }
.einrichtbild .wann {
  display: block;
  padding: .25rem .4rem;
  font-size: .68rem;
  line-height: 1.4;
  color: var(--schrift-leise);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Die Zahl sagt, wie viele Stellen die Erkennung gefunden hat – nicht, wie
   viele Menschen darauf sind. Fehlalarme sind dabei; gemessen ist etwa jeder
   zweite Fund keiner. Deshalb steht dort „Funde“ und nicht „Personen“. */
.einrichtbild .funde {
  position: absolute;
  top: .3rem; right: .3rem;
  min-width: 1.2rem;
  padding: .05rem .35rem;
  border-radius: 999px;
  background: var(--gelb);
  color: var(--auf-gelb);
  font-size: .7rem;
  font-weight: 700;
  line-height: 1.5;
}

/* Unterleiste: die Seiten innerhalb eines Menüpunkts.
 *
 * Leiser als die Hauptleiste – sie ordnet, sie führt nicht. Deshalb kleiner,
 * ohne eigenen Kasten, und der aktive Punkt nur durch Farbe und Unterstrich
 * gekennzeichnet. */
/* Die Unterleiste: die Seiten innerhalb eines Bereichs.
 *
 * Bewusst eine **andere** Sprache als die Pillen oben – sonst wiederholt
 * sich dasselbe Bild, und die Rangfolge verschwindet. Hier reiner Text mit
 * viel Luft; der aktive Eintrag trägt volles Gewicht, volle Schriftfarbe
 * und einen kleinen gelben Punkt davor.
 *
 * Der Punkt steht bei **jedem** Eintrag, nur unsichtbar – sonst rückt die
 * ganze Zeile weiter, sobald man einen anderen Punkt anklickt.
 *
 * Die Trennlinie über die ganze Breite ist weg: Sie sah aus wie ein
 * Seitentrenner und nicht wie das Ende einer Navigation. Abstand trennt
 * hier besser als ein Strich. */
.unterleiste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Enger gesetzt als zuerst versucht: Bei 1,45rem standen fünf lange
     Wörter über die halbe Bildschirmbreite verstreut und wirkten wie
     Einzelteile statt wie eine Reihe (02.09.2026 bemängelt). */
  gap: 0 1.15rem;
  /* Luft zum Kopf: Vorher klebte die Reihe mit negativem Abstand an der
   * Trennlinie und las sich als angehängter Rest. Und eine Stufe kleiner
   * als die Modulleiste (.875 → .84) – zwei Ebenen, zwei Größen. */
  margin: .35rem 0 1.4rem;
  font-size: .84rem;
}
/* Der Name des offenen Bereichs vor seinen Punkten (09.09.2026). Er ist
 * die zweite Hälfte der Verbindung nach oben: dasselbe Wort, dieselbe
 * Farbe wie der hervorgehobene Punkt in der Kopfzeile. Danach ein
 * senkrechter Strich – er trennt die Überschrift von der Reihe und macht
 * aus zwei Sorten Text eine Struktur.
 *
 * Kein Link: Das Ziel steht oben, und zwei Wege zur selben Seite
 * nebeneinander sind einer zu viel. */
.unterleiste-bereich {
  display: inline-flex;
  align-items: center;
  padding: .3rem .95rem .3rem 0;
  margin-right: .2rem;
  border-right: 1px solid var(--linie);
  color: var(--gelb-schrift);
  font-weight: 600;
  font-size: .84rem;
  white-space: nowrap;
}
.unterleiste a {
  display: inline-block;
  padding: .3rem 0;
  color: var(--schrift-leise);
  text-decoration: none;
}
/* **Der Strich sitzt unter dem Wort, kein Punkt davor.**
 *
 * Zuerst stand hier ein gelber Punkt vor dem aktiven Eintrag. Der schob den
 * Text um zwölf Punkte nach rechts – gemessen –, und damit begann die
 * Unterleiste weiter rechts als die Überschrift darunter. Eine Seite, deren
 * linke Kante in jeder Zeile woanders liegt, sieht ungeordnet aus, auch wenn
 * jede Zeile für sich stimmt.
 *
 * Gezeichnet wird er als **Unterstreichung des Textes**, nicht als Kasten
 * darunter: Auf dem Handy ist die Trefferfläche 44 Punkte hoch, und ein
 * Strich am unteren Rand des Kastens hing dort zwölf Punkte unter dem Wort
 * in der Luft. Eine Unterstreichung hängt immer am Text, egal wie hoch der
 * Kasten ist. Sie verschiebt auch nichts, wenn sie erscheint. */
.unterleiste a:hover { color: var(--schrift); }
.unterleiste a.hier {
  color: var(--schrift);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--gelb);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.unterleiste a:focus-visible { outline: 2px solid var(--gelb); outline-offset: 3px; }
@media (hover: none) and (pointer: coarse) {
  .unterleiste a { min-height: 44px; display: flex; align-items: center; }
}

/* Die Werkzeuge in der Seitenkopfzeile stehen nebeneinander.
 *
 * `.werkzeuge` ist als Spalte angelegt – richtig in einer Kamerakachel, wo
 * untereinander Platz ist. In der Kopfzeile einer Seite standen die
 * Zeitraum-Knöpfe dadurch übereinander und schoben die Überschrift nach
 * unten. Betrifft sechs Seiten, deshalb hier einmal statt sechsmal. */
.seitenkopf .werkzeuge {
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
}

/* Das Band, das den Anlauf der Verpixelung erklärt.
 *
 * Es liegt **unter** dem Bild und deckt es nicht zu: Wer sehen will, wie es
 * aufklart, soll dabei zusehen können. Eine Decke über dem Bild wäre genau
 * die Ratlosigkeit, die es beheben soll. */
.anlaufband {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .55rem .9rem;
  background: var(--karte-hell);
  border-top: 1px solid var(--linie);
  color: var(--schrift-leise);
  font-size: .85rem;
  line-height: 1.45;
}
.anlaufband strong { color: var(--schrift); }
.anlaufband .zahlen { margin-left: auto; white-space: nowrap; }
.anlaufband .ladekreis.klein { width: 14px; height: 14px; border-width: 2px; flex: 0 0 auto; }


/* ------------------------------------------------------- Lagebild
   Die Startseite. Sie soll in einer Sekunde beantworten, ob etwas brennt –
   deshalb oben ein Ring und drei Zahlen, nicht fünfzehn Zeilen. Die erste
   Fassung war eine Liste, und der Betreiber sagte zu Recht, man werde
   „gleich mit Informationen überfrachtet".

   Eigene Klassennamen mit Vorsilbe `lage`, Zustände mit `ist-`. Kurze
   Wörter wie `.gut`, `.aus`, `.leer`, `.text` und `.meldung` waren im
   gewachsenen Stylesheet längst belegt; `.meldung` etwa ist die
   eingeblendete Kurzmeldung mit `opacity: 0`, und meine Zeilen standen
   unsichtbar im Text. Vor dem Erfinden im Stylesheet nachsehen – dafür
   gibt es jetzt werkzeuge/stil-kollisionen.py. */

/* Vier ruhige Kennzahl-Karten oben – dieselbe Kartensprache wie überall,
   keine Alarmleiste. Rot gibt es nur an der Zahl der offenen Punkte. */
.lagenkarten {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.5rem;
  margin-bottom: 1.5rem;
}
.lagenkarte {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1.35rem 1.5rem;
  background: var(--kartengrund);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  box-shadow: var(--schatten-klein);
  overflow: hidden;
}
.lagenzahl {
  font-size: 2.1rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.lagenzahl.warnt { color: var(--schlecht); }
.lagenwort { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.lagenwort strong { font-size: .95rem; font-weight: 600; }

/* Das Stundenband der Bautätigkeit: 24 feine Balken am Kartenfuß. */
.lagenband {
  position: absolute;
  inset: auto 0 0 0;
  height: 26%;
  display: flex;
  align-items: flex-end;
  gap: 2px;
  padding: 0 .6rem;
  opacity: .5;
  pointer-events: none;
}
.lagenband i {
  flex: 1;
  border-radius: 2px 2px 0 0;
  background: var(--gelb);
  min-height: 2px;
}
.lagenband i.leerstunde { background: var(--linie); }

.lageringbereich { position: relative; width: 92px; height: 92px; flex: 0 0 auto; }
.lagering { width: 92px; height: 92px; transform: rotate(-90deg); }
.lagering circle { fill: none; stroke-width: 4.4; }
.lagering-bahn { stroke: var(--linie); }
/* Der Ring ist eine Anzeige, kein Alarm — und er soll ins Auge fallen
 * (09.09.2026, zweimal bemängelt: erst „das Braun sieht traurig aus", dann
 * „keine schöne Farbe, nimm was Knalligeres, nichts so Dreckiges").
 *
 * Zwei Irrwege liegen hinter dieser Zeile. Erst stand hier `--achtung`,
 * Bernstein, sobald nicht alles lief — bei 27 von 28 Boxen also fast
 * immer; ein Warnton, der bei 96 Prozent leuchtet, warnt nicht mehr, er
 * nörgelt. Dann `--gelb-schrift`, weil das die Hausfarbe in der Form ist,
 * die auf beiden Gründen *lesbar* ist. Im hellen Thema ist das aber
 * #5f6300, ein tiefes Oliv — für Schrift genau richtig, als Ring auf
 * beinahe weißem Grund schlicht schmutzig.
 *
 * Der Denkfehler war, den Ring wie Schrift zu behandeln. Die Zahl in der
 * Mitte steht in `--schrift` (fast Schwarz, 17 zu 1) und trägt die
 * Auskunft ganz allein; der Ring zeichnet sie nur nach. Ein Schaubild
 * neben seinem eigenen Text braucht keine Textkontraste — es braucht eine
 * Form, die man sieht. Deshalb jetzt das volle Marken-Gelb #d2d700 und ein
 * Strich von 4,4 statt 3,6: auf dem dunklen Grund leuchtet es ohnehin, auf
 * dem hellen wirkt es wie ein Textmarker auf Papier. Am Bildschirm
 * verglichen mit Orange, Frischgrün, Türkis und Pink — die knallen auch,
 * aber Orange ist unser Achtung und Grün unser „läuft"; eine Farbe, die
 * anderswo etwas bedeutet, darf hier nicht bloß hübsch sein.
 *
 * Grün bleibt für „alles läuft": Der eine Zustand, der eine eigene Farbe
 * verdient, ist der, den man erreichen will. */
.lagering-voll { stroke: var(--gelb); stroke-linecap: round;
                 transition: stroke-dasharray .5s ease, stroke .3s ease; }
.lagering-voll.voll { stroke: var(--gut); }
.lageringtext {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .1rem;
}
.lageringtext strong { font-size: 1.25rem; line-height: 1; font-weight: 600;
                       font-variant-numeric: tabular-nums; }
.lageringtext strong span { font-size: .8rem; font-weight: 400;
                            color: var(--schrift-leise); }

.lagemeldungen { display: flex; flex-direction: column; }
.lagemeldung {
  display: flex; align-items: center; gap: .8rem;
  padding: .85rem .9rem;
  border-bottom: 1px solid var(--linie);
  color: var(--schrift); text-decoration: none;
}
.lagemeldung:last-child { border-bottom: 0; }
.lagemeldung:hover { background: var(--karte-hell); text-decoration: none; }
.lagemeldung .lagetext { flex: 1; font-size: .93rem; }
.lagemeldung .pfeil { color: var(--schrift-leise); font-size: 1.2rem; }
.lageampel { width: 10px; height: 10px; min-width: 10px; min-height: 10px;
             border-radius: 50%; flex: 0 0 auto; }
.lagemeldung.hoch .lageampel    { background: var(--schlecht); }
.lagemeldung.mittel .lageampel  { background: var(--achtung); }
.lagemeldung.niedrig .lageampel { background: var(--schrift-leise); }
/* Gute Nachricht (16.09.2026): die erste Anmeldung eines Kunden.
   Grün, damit man auf einen Blick sieht, dass hier nichts hakt. */
.lagemeldung.gut .lageampel { background: var(--gut); }

/* ------------------------------------------------------- Archiv: Ziehen
   Eigene Vorsilbe `archiv…`: `.ablage` gibt es schon, das ist die
   Hochladeflaeche fuer Belege (`ablegen.js`). Zwei Flaechen mit einem
   Namen hiessen: Wer die eine aendert, veraendert die andere mit
   (gefunden bei der Durchsicht am 16.09.2026).
   Baustellen in das Archiv ziehen (16.09.2026). Das Ziehen ist die
   Abkuerzung; jede Karte traegt denselben Knopf, und diese Regeln
   duerfen ausfallen, ohne dass etwas fehlt. */

.ziehliste { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: .5rem; max-height: 30rem; overflow-y: auto; }
.ziehkarte {
  display: flex; align-items: center; gap: .6rem;
  padding: .6rem .75rem; border: 1px solid var(--linie); border-radius: 8px;
  background: var(--karte); cursor: grab;
}
.ziehkarte:hover { background: var(--karte-hell); }
.ziehkarte.zieht { opacity: .4; cursor: grabbing; }
.ziehgriff { color: var(--schrift-leise); font-size: 1.1rem; line-height: 1;
  letter-spacing: -.15em; }

.archivablage {
  display: flex; align-items: center; justify-content: center;
  min-height: 11rem; padding: 1.5rem 1rem; text-align: center;
  border: 2px dashed var(--linie); border-radius: 12px;
  color: var(--schrift-leise); transition: border-color .12s, background .12s;
}
/* `bereit` waehrend des Ueberfahrens, `belegt` danach: Zwei Zustaende,
   weil sie Verschiedenes sagen – „hier kannst du loslassen" und „hier
   liegt etwas". Ein einziger gruener Rahmen fuer beides liesse offen, ob
   die Ablage noch wartet oder schon etwas hat. */
.archivablage.bereit { border-color: var(--gelb); background: var(--karte-hell);
  color: var(--schrift); }
.archivablage.belegt { border-style: solid; border-color: var(--gut);
  color: var(--schrift); }
.archivablage .sinnbild { color: var(--schrift-leise); margin-bottom: .5rem; }
.archivablage.belegt .sinnbild { color: var(--gut); }
.archivablagemitte p { margin: 0; }
.archivablagemitte p:first-of-type { font-weight: 600; font-size: 1rem;
  margin-bottom: .35rem; }

/* Kacheln statt Zeilen: Das Auge erfasst fünfzehn Farben auf einmal und
   bleibt nur dort hängen, wo eine andere ist. */
.lagekacheln {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: .6rem;
  padding: .9rem;
}
.lagekachel {
  display: flex; flex-direction: column; gap: .25rem;
  padding: .75rem .85rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  color: var(--schrift); text-decoration: none;
  background: var(--karte-hell);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.lagekachel:hover { border-color: var(--gelb); text-decoration: none;
                    box-shadow: var(--schatten-klein); }
/* Farbe trägt der Punkt, nicht der Rand: Ein Feld voller roter Rahmen
   sah nach Großalarm aus, wenn zwei Kameras hakten. Nur „aus" (keine
   einzige liefert, obwohl die Baustelle läuft) färbt auch den Rand. */
.lagekachel.ist-aus { border-color: color-mix(in srgb, var(--schlecht) 55%, var(--linie)); }
.lagekachelkopf { display: flex; align-items: center; gap: .5rem; }
.lagekachelkopf strong { font-size: .95rem; }
.lagepunkt { width: 9px; height: 9px; min-width: 9px; min-height: 9px;
             border-radius: 50%; flex: 0 0 auto; }
.lagepunkt.ist-gut       { background: var(--gut); }
.lagepunkt.ist-teilweise { background: var(--achtung); }
.lagepunkt.ist-aus       { background: var(--schlecht); }
.lagepunkt.ist-leer,
.lagepunkt.ist-ruhend    { background: var(--linie); }

/* Buchhaltungs-Überblick: zwölf Monate, je zwei Balken (Eingang, Ausgang). */
.lagenzahl.klein { font-size: 1.45rem; }
.monatsverlauf {
  display: flex; gap: .5rem; align-items: stretch;
  padding: 1rem 1.1rem .4rem; height: clamp(9rem, 22vh, 14rem);
}
.monatsspalte { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.monatsspalte small { text-align: center; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; margin-top: .3rem; }
.monatsbalken { flex: 1; display: flex; gap: 3px; align-items: flex-end; justify-content: center; }
.monatsbalken span { flex: 1; max-width: 1.4rem; border-radius: 3px 3px 0 0; min-height: 2px; }
.monatsbalken .eingang, .legende.eingang { background: var(--schrift-leise); opacity: .55; }
.monatsbalken .ausgang, .legende.ausgang { background: var(--gelb); }
.legende { display: inline-block; width: .7rem; height: .7rem; border-radius: 2px; vertical-align: -1px; }

/* Ruhende Baustellen: eine Zeile aus stillen Namen unter den Kacheln. */
.lageruhig {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .35rem .8rem;
  padding: .8rem .95rem .95rem;
  border-top: 1px solid var(--linie-leise);
}
.lagestill { color: var(--schrift-leise); font-size: .85rem; }
.lagestill:hover { color: var(--schrift); text-decoration: none; }

@media (max-width: 640px) {
  .lagenkarten { gap: .8rem; }
  .lagenkarte { padding: 1rem 1.1rem; }
  .lagenzahl { font-size: 1.7rem; }
}

/* -------------------------------------------------------- Belegarchiv
   Die Reihenfolge auf dem Bildschirm ist die Reihenfolge der Benutzung:
   Gesucht wird täglich, hochgeladen einmal im Monat. Bis zum 30.08.2026
   standen beide gleich groß untereinander und die Trefferliste ganz unten –
   man scrollte an einem Formular vorbei, das man gar nicht wollte. */

.belegsuchtafel { margin-bottom: 1.25rem; }
.belegsuchzeile { display: flex; gap: .6rem; padding: 1rem 1.15rem .8rem; flex-wrap: wrap; }

/* Lupe im Feld statt daneben: Ein Sinnbild neben dem Feld ist Zierat,
   eines darin sagt, wozu das Feld da ist. */
.suchfeld {
  flex: 1 1 22rem;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: 0 .8rem;
  background: var(--feld, var(--karte-hell));
  border: 1px solid var(--linie);
  border-radius: var(--rund);
}
.suchfeld:focus-within { border-color: var(--gelb); }
.suchfeld .sinnbild { color: var(--schrift-leise); flex: 0 0 auto; }
.suchfeld:focus-within .sinnbild { color: var(--gelb); }
.suchfeld input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: .62rem 0;
  font-size: .95rem;
}
.suchfeld input[type="search"]:focus { outline: none; box-shadow: none; }

/* Die Arten als anklickbare Marken statt als Auswahlliste. Aufklappen,
   wählen, absenden war dreimal so viel Arbeit für dieselbe Auskunft – und
   man sah nicht, wovon es wie viel gibt. */
.belegfilter {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  padding: 0 1.15rem 1rem;
}
.belegfilter a {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .28rem .7rem;
  border: 1px solid var(--linie);
  border-radius: 999px;
  font-size: .82rem;
  color: var(--schrift-leise);
  text-decoration: none;
}
.belegfilter a:hover { border-color: var(--schrift-leise); color: var(--schrift); }
.belegfilter a.gewaehlt {
  background: var(--gelb);
  border-color: var(--gelb);
  color: var(--auf-gelb);
  font-weight: 500;
}
.belegfilter .zahl {
  font-variant-numeric: tabular-nums;
  font-size: .76rem;
  opacity: .75;
}

/* Treffer breit, Hochladen schmal daneben. Unter 62rem untereinander –
   dann steht das Hochladen unter der Liste, wo es hingehört. */
.belegspalten {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
@media (max-width: 62rem) {
  .belegspalten { grid-template-columns: minmax(0, 1fr); }
}

.belegliste { display: flex; flex-direction: column; }
.belegzeile {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .85rem 1.15rem;
  border-bottom: 1px solid var(--linie-leise);
}
.belegzeile:last-child { border-bottom: 0; }
.belegzeile:hover { background: var(--karte-hell); }
.belegzeichen { flex: 0 0 auto; color: var(--schrift-leise); padding-top: .1rem; }

/* Ein Bild der ersten Seite statt eines Sinnbilds.
 *
 * „Scan22042026-3.pdf" sagt nichts; der Briefkopf sagt sofort, von wem
 * die Rechnung ist. Fehlt die Vorschau – etwa bei einer geschützten
 * Datei –, nimmt das Bild sich selbst aus dem Weg (`onerror`), und das
 * Sinnbild darunter tritt an seine Stelle. Deshalb liegen beide
 * übereinander im selben Knopf. */
.belegbild {
  position: relative;
  flex: 0 0 auto;
  width: 2.6rem;
  height: 3.4rem;
  padding: 0;
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: 4px;
  overflow: hidden;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.belegbild:hover { border-color: var(--gelb); filter: none; }
.belegbild img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}
.belegbild .sinnbild { color: var(--schrift-leise); }

/* Der Name als Knopf – er öffnet das Fenster, nicht einen zweiten Reiter. */
.belegzeile .belegname {
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  font: inherit;
  font-weight: 600;
  color: var(--schrift);
  cursor: pointer;
}
.belegzeile .belegname:hover { color: var(--gelb); filter: none; }

/* Das Vorschaufenster. Groß genug zum Lesen, mit Rand zum Schließen. */
.belegfenster {
  width: min(58rem, 92vw);
  height: min(88vh, 60rem);
  padding: 0;
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  background: var(--karte);
  color: var(--schrift);
  box-shadow: var(--schatten);
  overflow: hidden;
}
.belegfenster::backdrop { background: rgba(0, 0, 0, .55); }
.belegfensterkopf {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--linie);
}
.belegfensterkopf strong {
  margin-right: auto;
  font-size: .95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.belegfensterbild {
  height: calc(100% - 3.6rem);
  overflow: auto;
  background: var(--karte-hell);
  display: grid;
  place-items: start center;
  padding: 1rem;
}
.belegfensterbild img {
  max-width: 100%;
  height: auto;
  background: #fff;
  border: 1px solid var(--linie);
  border-radius: 4px;
}
.belegkeinbild { max-width: 26rem; text-align: center; align-self: center; }
.belegtext { flex: 1 1 auto; min-width: 0; }
.belegkopf { display: flex; align-items: baseline; gap: .7rem; }
.belegname {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--schrift);
  font-weight: 600;
  overflow-wrap: anywhere;
}
.belegbetrag { flex: 0 0 auto; margin-left: auto; font-weight: 600; }
.belegangaben { margin-top: .15rem; overflow-wrap: anywhere; }

/* Die Art an der Zeile: sieht aus wie eine Marke, ist aber eine Auswahl.
   Ein Rahmen um jede Zeile wäre Lärm – er kommt erst, wenn die Maus
   darauf zeigt und die Auswahl damit gemeint ist. */
.belegartwahl { flex: 0 0 auto; }
.belegartwahl select {
  width: auto;
  padding: .2rem 1.4rem .2rem .6rem;
  font-size: .78rem;
  color: var(--schrift-leise);
  background-color: transparent;
  border-color: transparent;
  border-radius: 999px;
  cursor: pointer;
}
.belegzeile:hover .belegartwahl select,
.belegartwahl select:focus { border-color: var(--linie); color: var(--schrift); }

/* Die Fundstelle: schmaler Balken links, damit das Auge sie vom Titel
   trennt, ohne dass ein zweiter Kasten nötig wird. */
.belegstelle {
  margin: .45rem 0 .2rem;
  padding-left: .7rem;
  border-left: 2px solid var(--gelb);
  font-size: .88rem;
  color: var(--schrift-leise);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.belegstelle mark {
  background: rgba(210, 215, 0, .3);
  color: var(--schrift);
  border-radius: 3px;
  padding: 0 .1rem;
}

.belegleer { padding: 1.6rem 1.15rem 1.9rem; }
.belegleer p { margin: 0 0 .4rem; }
.belegleer p:last-child { margin-bottom: 0; }

/* Hochladen: eine Fläche, sonst nichts. Vorher standen hier drei Felder,
   zwei davon musste man bei fast jedem Beleg wegklicken. */
.beleghochladen { padding: 1.15rem; }
.ablagefeld {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .35rem;
  padding: 1.6rem 1rem;
  text-align: center;
  border: 2px dashed var(--linie);
  border-radius: var(--rund-gross);
  cursor: pointer;
  transition: border-color .12s, background-color .12s;
}
.ablagefeld:hover { border-color: var(--gelb); background: var(--karte-hell); }
.ablagefeld .sinnbild { color: var(--schrift-leise); }
.ablagefeld:hover .sinnbild { color: var(--gelb); }
.ablagefeld input[type="file"] { display: none; }
/* Während eine Datei über dem Fenster hängt – auf der ganzen Seite, nicht
   nur über dem Feld: Wer eine Datei über das Fenster zieht, zielt nicht. */
/* Jede Fläche um ein Hochladefeld, während eine Datei darüber schwebt.
   `ablegen.js` setzt die Klassen; das Aussehen steht hier. */
.ablage { position: relative; }
/* Während ein abgelegter Ordner durchgegangen wird. Bei zweihundert
   Dateien dauert das ein paar Sekunden – ohne ein Zeichen dafür klickt
   man ein zweites Mal. */
.ablage.sucht { opacity: .6; }
.ablage.sucht::after {
  content: "Ordner wird durchgesehen …";
  position: absolute;
  inset: auto 0 .4rem 0;
  text-align: center;
  font-size: .76rem;
  color: var(--schrift-leise);
}
/* Der Fortschritt beim Hochladen.
 *
 * Ein abgeschicktes Formular zeigt gar nichts – deshalb schickt die Seite
 * die Dateien einzeln und zeigt hier, wie weit sie ist. Ein Balken über
 * den Stapel **und** eine Zeile je Datei: Der Balken sagt „noch lange",
 * die Zeilen sagen, woran es gerade liegt. Bei einem 40-seitigen Scan
 * steht die Texterkennung minutenlang auf derselben Datei, und ohne die
 * Zeile sähe das nach einem Hänger aus. */
.ablagelauf { margin-top: 1rem; }
.ablagekopf {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  margin-bottom: .5rem;
}
.ablagekopf strong { font-size: .9rem; margin-right: auto; }
.ablagebalken {
  height: 8px;
  border-radius: 999px;
  background: var(--linie);
  overflow: hidden;
}
.ablagebalken span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--gelb);
  border-radius: 999px;
  transition: width .25s ease-out;
}

.ablageliste {
  list-style: none;
  margin: .7rem 0 0;
  padding: 0;
  max-height: 15rem;
  overflow-y: auto;
}
.ablagezeile {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .6rem;
  padding: .3rem 0;
  border-bottom: 1px solid var(--linie-leise);
  font-size: .82rem;
}
.ablagezeile:last-child { border-bottom: 0; }
.ablagename {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--schrift-leise);
}
.ablagewas { flex: 0 0 auto; text-align: right; }
/* Der Grund unter der Zeile: neuer Name, oder warum es vermutlich derselbe ist. */
.ablagehinweis { flex: 1 0 100%; margin-top: -.15rem; }
.ablagezeile.laeuft .ablagename { color: var(--schrift); }
.ablagezeile.laeuft .ablagewas { color: var(--gelb); }
.ablagezeile.fertig .ablagename { color: var(--schrift); }
.ablagezeile.schief .ablagename,
.ablagezeile.schief .ablagewas { color: var(--achtung); }
/* Schon vorhanden ist kein Fehler – deshalb nicht die Warnfarbe, sondern
   zurückgenommen. Rot für dreißig Zeilen „liegt schon da" sähe nach
   dreißig kaputten Dateien aus. */
.ablagezeile.schonda .ablagename { color: var(--schrift-leise); }
.ablagezeile.schonda .ablagewas { color: var(--schrift-leise); font-style: italic; }
.ablagezeile .trotzdem {
  flex: 0 0 auto;
  padding: .1rem .5rem;
  font-size: .74rem;
}
/* Was noch wartet, tritt zurück – sonst sieht eine Liste aus 200 Zeilen
   aus wie 200 Fehler. */
.ablagezeile.wartet { opacity: .5; }
.ablagemeldung {
  display: block;
  margin-top: .5rem;
  font-size: .78rem;
  color: var(--gelb);
}
.ablage.zieht {
  outline: 2px dashed var(--gelb);
  outline-offset: 3px;
  border-radius: var(--rund);
}
.ablagefeld.zieht,
.ablagefeld.bereit {
  border-color: var(--gelb);
  border-style: solid;
  background: rgba(210, 215, 0, .1);
}
.ablagefeld.laeuft { opacity: .55; pointer-events: none; }

.beleghilfe { margin: .85rem 0 0; }
.belegschutz { margin-top: .9rem; border-top: 1px solid var(--linie-leise); padding-top: .7rem; }
.belegschutz summary { cursor: pointer; }
.beleghochladen .breit { width: 100%; }


/* ---------------------------------------------------------- OneNote
   Der Spiegel der Notizbücher. Die Seite zeigt immer nur den Zustand, in
   dem man gerade ist – nicht eingerichtet, eingerichtet, verbunden –,
   damit niemand ein Formular vor sich hat, das er nicht braucht. */
.onenotestand { padding: 1rem 1.15rem; }
.onenotestand > p { margin: 0 0 .8rem; display: flex; align-items: center; gap: .5rem; }
.onenotestand > p .sinnbild { color: var(--gelb); flex: 0 0 auto; }
.onenotestand .werkzeuge { padding: 0; gap: .5rem; flex-wrap: wrap; }
.onenotestand .klein.leise { margin: .8rem 0 0; }

.onenoteangaben { border-top: 1px solid var(--linie-leise); padding: .8rem 1.15rem 1.15rem; }
.onenoteangaben summary { cursor: pointer; }
.onenoteform { display: flex; flex-wrap: wrap; gap: .5rem 1rem; margin-top: .9rem; }
.onenoteform .feld { flex: 1 1 17rem; margin: 0; }
.onenoteform button { align-self: flex-end; }

/* Die Klickanleitung. Sie steht auf der Seite und nicht in einer Datei
   nebenan: Wer sie braucht, sitzt in genau diesem Augenblick davor. */
.onenoteanleitung {
  margin-top: 1.2rem;
  padding: .9rem 1.1rem;
  background: var(--karte-hell);
  border-radius: var(--rund);
}
.onenoteanleitung ol { margin: .5rem 0 .6rem; padding-left: 1.2rem; }
.onenoteanleitung li { margin-bottom: .45rem; }
.onenoteanleitung code { font-size: .82em; }
/* Die Umleitungsadresse muss zeichengenau übernommen werden – deshalb in
   einer eigenen Zeile, auswählbar, ohne Umbruch mitten im Wort. */
.onenoteadresse {
  display: inline-block;
  margin-top: .2rem;
  padding: .2rem .45rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: 5px;
  user-select: all;
  overflow-wrap: anywhere;
}

/* Wie viele Zugangsdaten aus dieser Seite im Tresor liegen. Ein Schlüssel
   und eine Zahl – der Wert selbst steht hier nirgends. */
.onenoteschloss {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  flex: 0 0 auto;
  padding: .05rem .45rem;
  border: 1px solid var(--linie);
  border-radius: 999px;
  font-size: .74rem;
  color: var(--schrift-leise);
}
.onenoteschloss .sinnbild { color: var(--gelb); }
.onenoteweg {
  flex: 0 0 auto;
  font-size: .74rem;
  color: var(--achtung);
  border: 1px solid rgba(217, 154, 60, .45);
  border-radius: 999px;
  padding: .05rem .45rem;
}

.onenotezuordnung { flex: 0 0 auto; display: flex; gap: .35rem; flex-wrap: wrap; }
.onenotezuordnung select { width: auto; font-size: .78rem; padding: .25rem .5rem; }


/* Eine gespiegelte OneNote-Seite an der Kamera. Bewusst schlicht: Sie ist
   eine Abschrift, keine Arbeitsfläche. */
.onenotekarte { padding: 1rem 1.15rem; border-bottom: 1px solid var(--linie-leise); }
.onenotekarte:last-child { border-bottom: 0; }
.onenotekopf { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.onenotekopf a { color: var(--schrift); }
.onenoteinhalt {
  margin: .7rem 0 0;
  padding: .7rem .9rem;
  background: var(--karte-hell);
  border-radius: var(--rund);
  font: inherit;
  font-size: .88rem;
  color: var(--schrift-leise);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 22rem;
  overflow-y: auto;
}

/* Die Zugangsdaten. Der Wert steht hier nicht – bis jemand darauf klickt,
   und dann steht der Klick im Protokoll. */
.onenotegeheim {
  margin-top: .8rem;
  padding: .7rem .9rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
}
.onenotegeheim > p { margin: 0 0 .6rem; display: flex; align-items: center; gap: .4rem; }
.onenotegeheim > p .sinnbild { color: var(--gelb); flex: 0 0 auto; }
.onenotezeile { display: flex; align-items: center; gap: .6rem; padding: .25rem 0; flex-wrap: wrap; }
.onenoteart {
  flex: 0 0 auto;
  min-width: 6rem;
  font-size: .78rem;
  color: var(--schrift-leise);
}
.onenotewert {
  flex: 1 1 12rem;
  min-width: 0;
  font-size: .85rem;
  overflow-wrap: anywhere;
  user-select: all;
}
.onenotezeile button { flex: 0 0 auto; }


/* ------------------------------------------------------- Übersicht Boxen
   Der Belegungsplan. Eine Zeile je Box, 52 Spalten für die Wochen, ein
   Balken je Einsatz.

   Die Farbe kommt als **Farbton** aus der Datenbank (`--ton`, 0–360);
   Sättigung und Helligkeit stehen hier. Nur hier ist bekannt, ob gerade
   hell oder dunkel dargestellt wird – ein reines Gelb aus der Excel-Mappe
   ist auf Papier gut lesbar und auf dunklem Grund blendend. */

.boxzahlen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.boxzahl {
  background: var(--karte);
  border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
  padding: .9rem 1.05rem;
  display: flex;
  flex-direction: column;
  gap: .15rem;
}
.boxzahl strong {
  font-size: 1.9rem;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -.02em;
  margin: .15rem 0 .1rem;
}
.boxzahl strong small { font-size: .9rem; font-weight: 500; color: var(--schrift-leise); }
.boxbalken {
  margin-top: .5rem;
  height: 6px;
  border-radius: 999px;
  background: var(--linie);
  overflow: hidden;
}
.boxbalken span { display: block; height: 100%; background: var(--gelb); }

/* Der Plan rollt waagerecht, wenn der Bildschirm zu schmal ist – die
   Boxspalte bleibt dabei stehen, sonst weiß man nicht mehr, welche Zeile
   welche Box ist. */
/* Der Plan rollt in beide Richtungen – und zwar in sich selbst.
 *
 * Das ist nicht Geschmack, sondern Notwendigkeit: Ein Kasten mit
 * `overflow-x: auto` ist nach den CSS-Regeln auch senkrecht ein
 * Rollbereich. Haftende Kopfzeilen darin richten sich nach **ihm**, nicht
 * nach dem Fenster. Beim ersten Anlauf standen Monat und KW deshalb
 * mitten im Plan und deckten zwei Boxzeilen zu.
 *
 * Mit einer Höhengrenze wird daraus etwas Besseres als vorher: Monat, KW
 * und die Boxspalte bleiben stehen, während man durch alle sechsunddreißig
 * Zeilen und dreiundfünfzig Wochen fährt. */
/* Der Belegungsplan rollt mit der SEITE, nicht in einem eigenen Kasten.
 *
 * Vorher stand hier `overflow: auto; max-height: min(72vh, 44rem)`. Damit
 * gab es zwei Rollbalken übereinander: den der Seite und den des Plans.
 * Gemessen an einem 1400x900-Fenster: Der Plan ist 2327 Punkte hoch, der
 * Kasten deckelte ihn auf 648 – und die Seite war trotzdem 1906 hoch. Man
 * rollt die Seite bis zum Plan und dann noch einmal im Plan; wer den
 * Zeiger einen Fingerbreit daneben hat, rollt das Falsche
 * (11.09.2026 bemängelt).
 *
 * Waagerecht bleibt der Kasten nötig, aber nur, wenn der Plan wirklich
 * nicht hineinpasst: Er braucht 58rem, und die Seite lässt bei einem
 * Fenster ab rund 971 Punkten so viel übrig (gemessen bei 1000, 1400 und
 * 1600 Punkten Fensterbreite: je 43 Punkte gehen für Rand und Rollbalken
 * ab). Unterhalb greift die Regel weiter unten.
 *
 * Wichtig dabei: Ein Element mit `overflow-x: auto` rollt laut Norm auch
 * senkrecht (`overflow-y` wird zu `auto`) und ist damit wieder ein
 * Rollkasten – dann klebten die Kopfzeilen an IHM statt an der Seite und
 * wären beim Rollen weg. Deshalb hier gar kein `overflow`, und der
 * waagerechte Fall steht getrennt in der Medienregel. */
.planrolle { overscroll-behavior-x: contain; }
.plan { min-width: 58rem; }

/* Und deshalb `clip` statt `hidden` an der Tafel, die den Plan trägt:
 * Beides schneidet an den runden Ecken ab, aber `hidden` ist ein
 * Rollkasten – und ein Rollkasten dazwischen fängt das Kleben der
 * Kopfzeilen ab, obwohl er selbst nie rollt. `:has()`, damit alle
 * anderen Tafeln bleiben, wie sie sind. */
.tafel:has(.planrolle) { overflow: clip; }
.planzeile { display: flex; align-items: stretch; border-bottom: 1px solid var(--linie-leise); }
.planzeile:last-child { border-bottom: 0; }
/* Getönte Zeilen im Wechsel – bei sechsunddreißig gleich aussehenden
   Zeilen verrutscht das Auge sonst waagerecht.
 *
 * Zwei Fallstricke, beide gemessen: `:not(.plankopf)` muss dabeistehen,
 * sonst gewinnt diese Regel gegen `.plankopf` (eine Klasse plus
 * Pseudoklasse ist genauer als eine Klasse) und färbt die Kopfzeile mit.
 * Und die Tönung muss **deckend** sein, nicht halbdurchsichtig: Die
 * Kopfzeilen haften und die Boxspalte auch – durch eine durchscheinende
 * Fläche wandern die Balken darunter hindurch. */
.planzeile:not(.plankopf):nth-of-type(even) {
  background: color-mix(in srgb, var(--karte-hell) 60%, var(--karte));
}
.planzeile:hover { background: var(--karte-hell); }
/* Zwei Kopfzeilen, beide haftend – die zweite muss unter der ersten
   stehen bleiben, sonst legen sie sich beim Rollen übereinander. */
/* Die beiden Kopfzeilen kleben jetzt an der SEITE – also unter der
 * Kopfleiste von TB Web, nicht am oberen Fensterrand. Wie hoch die ist,
 * misst `boxen.html` beim Laden und schreibt es in `--plankopfoben`;
 * 3,5rem ist der gemessene Wert für den Fall, dass das Skript nicht
 * läuft. */
.plankopf { position: sticky; top: var(--plankopfoben, 3.5rem);
            z-index: 3; background: var(--karte); }
.plankopf:hover { background: var(--karte); }
.plankw { top: calc(var(--plankopfoben, 3.5rem) + 2.1rem); z-index: 3; }

/* Schmaler als der Plan breit ist: Dann doch ein Rollkasten, sonst ragte
 * er in den Schnitt der Tafel und wäre nicht heranzuschieben. Die
 * Kopfzeilen kleben dort an ihm selbst, also wieder bei 0. */
@media (max-width: 62rem) {
  .planrolle { overflow-x: auto; }
  .plankopf { top: 0; }
  .plankw { top: 2.1rem; }
}

.plimbox {
  flex: 0 0 9.5rem;
  position: sticky;
  left: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .05rem;
  padding: .45rem .8rem;
  background: inherit;
  border-right: 1px solid var(--linie);
  color: var(--schrift);
  text-decoration: none;
}
.planzeile:hover .plimbox { background: var(--karte-hell); }
.plimbox strong { font-size: .85rem; font-weight: 600; }
.planquote { font-size: .72rem; color: var(--schrift-leise); font-variant-numeric: tabular-nums; }

.plangitter {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(var(--wochen, 52), minmax(0, 1fr));
  align-items: center;
  padding: .3rem 0;
  position: relative;
  min-height: 2.1rem;

  /* Die Wochenlinien als Hintergrund, nicht als Elemente: 52 Striche mal
     37 Zeilen wären fast zweitausend Elemente für einen Strich.
     `background-size` teilt genauso wie das Raster, deshalb decken sie
     sich – aber nur ohne `gap`. Der Abstand zwischen den Balken kommt
     jetzt aus ihrem eigenen Rand. */
  background-image:
    linear-gradient(to right, var(--linie-leise) 1px, transparent 1px),
    linear-gradient(to right, var(--linie) 1px, transparent 1px);
  background-size:
    calc(100% / var(--wochen, 52)) 100%,
    calc(500% / var(--wochen, 52)) 100%;
  background-position: 0 0;
}
.planmonat {
  grid-row: 1;
  font-size: .7rem;
  color: var(--schrift-leise);
  border-left: 1px solid var(--linie);
  padding-left: .3rem;
  overflow: hidden;
}
/* Die Wochenzahlen. Klein, mittig über ihrer Spalte, und sie dürfen über
   den Rand ihrer Spalte hinausragen – bei elf Bildpunkten Spaltenbreite
   passt „52" sonst nicht hinein. */
.plankw .plangitter { min-height: 1.4rem; padding: 0; }
.planwoche {
  grid-row: 1;
  justify-self: center;
  font-size: .62rem;
  line-height: 1;
  color: var(--schrift-leise);
  font-variant-numeric: tabular-nums;
  overflow: visible;
  white-space: nowrap;
  z-index: 1;
}
.plankw { border-bottom: 1px solid var(--linie); }
.plankw .plimbox, .plankopf .plimbox { justify-content: center; }

/* Ein Einsatz. Der Text steht darin, solange er passt – ein Balken ohne
   Beschriftung zwingt zum Zeigen mit der Maus, und auf einem Ausdruck geht
   das gar nicht. */
.planbalken {
  grid-row: 1;
  min-width: 0;
  height: 1.5rem;
  display: flex;
  align-items: center;
  padding: 0 .4rem;
  border-radius: 5px;
  margin: 0 1px;
  font-size: .72rem;
  text-decoration: none;
  background: hsl(var(--ton) 70% 80%);
  color: hsl(var(--ton) 65% 18%);
  border: 1px solid hsl(var(--ton) 55% 58%);
  overflow: hidden;
}
.planbalken span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.planbalken:hover { filter: brightness(1.06); }
/* Über den Jahreswechsel: an der offenen Seite keine runde Kante, sonst
   sähe ein durchgehender Einsatz nach zweien aus. */
.planbalken.offenlinks { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; }
.planbalken.offenrechts { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; }
.planbalken.unsicher { border-style: dashed; border-width: 2px; }

:root:not([data-thema="hell"]) .planbalken {
  /* Kräftiger als der erste Versuch: Mit 42 % Sättigung auf 26 % Helligkeit
     sahen sechs verschiedene Baustellen alle braun aus. */
  background: hsl(var(--ton) 55% 34%);
  color: hsl(var(--ton) 85% 92%);
  border-color: hsl(var(--ton) 55% 52%);
}

.planheute {
  grid-row: 1;
  justify-self: center;
  width: 2px;
  height: 100%;
  background: var(--gelb);
  opacity: .55;
  pointer-events: none;
}
.planleer .plimbox strong { color: var(--schrift-leise); font-weight: 500; }

.boxspalten {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
@media (max-width: 62rem) { .boxspalten { grid-template-columns: minmax(0, 1fr); } }

.projektliste { display: flex; flex-direction: column; max-height: 26rem; overflow-y: auto; }
.projektzeile {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .55rem 1.15rem;
  border-bottom: 1px solid var(--linie-leise);
}
.projektzeile:last-child { border-bottom: 0; }
.projektfarbe {
  flex: 0 0 auto;
  width: 12px; height: 12px;
  border-radius: 3px;
  background: hsl(var(--ton) 65% 62%);
  border: 1px solid hsl(var(--ton) 55% 48%);
}
.projekttext { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.projekttext strong { font-size: .88rem; font-weight: 600; }
.projektzahl { flex: 0 0 auto; text-align: right; font-variant-numeric: tabular-nums; }

/* Die Jahre als Säulen. Kein Diagramm mit Achsen – die Frage lautet
   „läuft es besser oder schlechter als vorher", und die beantwortet eine
   Reihe verschieden hoher Balken sofort. */
.jahresreihe {
  display: flex;
  align-items: flex-end;
  gap: .35rem;
  padding: 1.2rem 1.15rem .6rem;
  height: 9rem;
}
.jahressaeule {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: .2rem;
  height: 100%;
  text-decoration: none;
  color: var(--schrift-leise);
}
.jahressaeule .saeule {
  width: 100%;
  min-height: 2px;
  border-radius: 4px 4px 0 0;
  background: var(--linie);
}
.jahressaeule:hover .saeule { background: var(--schrift-leise); }
.jahressaeule.hier .saeule { background: var(--gelb); }
.jahressaeule.hier { color: var(--schrift); }
.saeulenwert { font-variant-numeric: tabular-nums; font-size: .68rem; }
.saeulenjahr { font-size: .68rem; }
.jahreshinweis { margin: 0 1.15rem .9rem; }

.jahrestabelle { width: 100%; border-collapse: collapse; font-size: .85rem; }
.jahrestabelle th, .jahrestabelle td {
  padding: .4rem 1.15rem;
  border-top: 1px solid var(--linie-leise);
  text-align: left;
}
.jahrestabelle th { font-size: .74rem; color: var(--schrift-leise); font-weight: 500; }
.jahrestabelle .rechts { text-align: right; }
.jahrestabelle tr.hier { background: var(--karte-hell); }
.jahrestabelle a { color: var(--schrift); }


/* Das Wetter neben dem Bild – dezent, eine Zeile.
 *
 * Kein Kasten, keine Farbe: Die Angabe gehört zum Bild und soll es nicht
 * überstimmen. Ein feiner Strich darüber genügt, um sie vom Bild zu
 * trennen. */
.wetterband {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  flex-wrap: wrap;
  margin: 0 1rem;
  padding: .75rem .15rem .2rem;
  border-top: 1px solid var(--linie-leise);
}
.wetterband > .klein { display: inline-flex; align-items: center; gap: .4rem; }
.wetterband .sinnbild { color: var(--schrift-leise); }
/* Zwei Spalten: links die Zahlen zum Bild, rechts der Tagesverlauf
   (09.09.2026 gewünscht, am selben Tag nachgebessert: „das Format sieht
   scheiße aus, mach das mal schön – locker, aber gut zu lesen").

   Der erste Wurf war zu eng gedacht: Das Diagramm bekam alles, was übrig
   war, und wurde auf breiten Schirmen 1600 Punkte breit bei 62 Punkten
   Höhe – eine flachgedrückte Linie in einer leeren Fläche. Jetzt hat es
   eine Höchstbreite; darüber hinaus wächst der Abstand, nicht die Grafik.

   `minmax(0, …)` bei beiden Spalten: Ein Rasterfeld weigert sich sonst,
   unter seinen Inhalt zu schrumpfen, und das SVG mit `width:100%` schöbe
   die Zahlenspalte aus dem Bild – derselbe Griff wie bei `.karten`. */
/* Mittig unter dem Bild (09.09.2026 gewünscht: „rücke alle in die Mitte
   unter dem Bild").

   Dafür bekommt die rechte Spalte eine Höchstbreite statt `1fr`: Mit
   `1fr` frisst sie allen übrigen Platz, und die Gruppe klebte links.
   `justify-content: center` rückt beide Spalten zusammen in die Mitte,
   und auf breiten Schirmen wächst der Rand außen, nicht die Grafik. */
.wetterband.zweispaltig {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 44rem);
  justify-content: center;
  gap: 1rem 2.4rem;
  align-items: center;
  padding: 1.1rem 1rem 1.15rem;
}
.wetterzahlen {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .75rem;
  min-width: 0;
  text-align: center;
}
.wetterzahlen > .klein { display: inline-flex; align-items: center; gap: .4rem; }
/* Eine Reihe, immer (09.09.2026: „wenn Winddaten noch dazu kommen, muss
   trotzdem noch alles in einer Reihe stehen"). Mit Windmesser sind es
   vier Werte statt drei; die rutschten vorher in eine zweite Zeile und
   der Block wurde hoch. `nowrap` hält sie zusammen, und weil jeder Block
   schrumpfen darf (`min-width: 0`), passen sie auch auf ein Handy. */
.wetterwerte {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 1.25rem;
  align-items: flex-start;
}
/* Der einzelne Wert: Zahl groß, Wort klein darunter. Nur hier – die
   `.nebenwert`-Form der übrigen Seiten bleibt, wie sie ist. */
.wetterwerte .nebenwert {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .05rem;
  min-width: 0;
}
.wetterwerte .nebenwert .zahlteil {
  display: inline-flex;
  align-items: baseline;
  gap: .2rem;
}
.wetterwerte .nebenwert strong {
  font-size: 1.3rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -.01em;
}
.wetterwerte .nebenwert small { font-size: .74rem; color: var(--schrift-leise); }
.wetterwerte .nebenwert .was {
  margin-left: 0;
  font-size: .72rem;
  color: var(--schrift-leise);
  white-space: nowrap;
}
/* Auf schmalen Schirmen wird die Reihe enger statt umzubrechen: kleinere
   Zahlen, kleinere Wörter, weniger Abstand. Vier Werte gehen so auch auf
   375 Punkten in eine Zeile. */
@media (max-width: 480px) {
  .wetterwerte { gap: .85rem; }
  .wetterwerte .nebenwert strong { font-size: 1.1rem; }
  .wetterwerte .nebenwert .was { font-size: .66rem; }
  .wetterwerte .nebenwert small { font-size: .68rem; }
}
/* Auf schmalen Schirmen untereinander: Zahlen neben einem Diagramm gehen
   auf 375 Punkten nicht auf – dann steht es darunter und hat die volle
   Breite. */
@media (max-width: 660px) {
  .wetterband.zweispaltig {
    grid-template-columns: minmax(0, 1fr);
    gap: .9rem;
  }
}

/* Knöpfe und Kameraleiste unter der ganzen Breite (20.09.2026).
 *
 * Am Telefon steht das Bauarchiv zweispaltig – links die Bilderleiste,
 * rechts das Bild. Die Knöpfe unter dem Bild erbten damit die schmale
 * rechte Spalte: fünf Stück, jeder in einer eigenen Zeile, keiner so breit
 * wie die Seite („wirken irgendwie komisch", 20.09.2026). `bauarchiv.html`
 * rückt sie nach dem Zeichnen in diesen Kasten, der über beide Spalten
 * läuft; hier steht, wie er aussieht.
 *
 * Die Kameras der Baustelle stehen darin größer und deutlicher: Sie sind
 * der Weg zur nächsten Kamera, nicht eine Fußnote. Vorschau 96 × 54 →
 * volle Breite des Knopfes im Hochformat, Name in Schriftfarbe statt
 * leise, zwei Knöpfe je Reihe statt eines schiebbaren Streifens. */
.unterbau { display: grid; gap: .7rem; }

@media (max-width: 900px) {
  .buehne:has(.bildraster) .unterbau { grid-column: 1 / -1; margin-top: .2rem; }

  /* Die Knöpfe: volle Breite, nebeneinander, solange zwei nebeneinander
     passen. Kein Rahmen oben – der Kasten steht ohnehin für sich. */
  .unterbau .tafelfuss {
    padding: 0;
    border-top: 0;
    display: block;
  }
  .unterbau .tafelfuss .rechts {
    margin-left: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    gap: .5rem;
  }
  .unterbau .tafelfuss .rechts > *,
  .unterbau .tafelfuss .rechts .knopf,
  .unterbau .tafelfuss .rechts > .vergroesserung > button {
    width: 100%;
    justify-content: center;
  }
  /* Das Klappmenü der Vergrößerung hängt an seinem Knopf und darf nicht
     mitwachsen – sonst stünde es als dritte Spalte im Raster. */
  .unterbau .tafelfuss .rechts .vergroesserung { position: relative; }

  /* Die Kameras der Baustelle im Kasten unter der Bühne. */
  .unterbau .kamerawahl {
    padding: .9rem 0 0;
    border-top: 1px solid var(--linie-leise);
  }
}

/* Die Kameras einer Baustelle als Kacheln, nicht als Streifen (20.09.2026).
 *
 * „Die weiteren Kameras einer Baustelle dominanter und etwas größer
 * zeigen" – zuerst im Bauarchiv, auf Wunsch dann überall, wo dieselbe
 * Leiste steht: BauCam, Live, Wiedergabe, Zeitraffer, freie Seite. Am
 * Telefon war sie ein schmaler Streifen mit 96 × 54 großen Vorschauen,
 * den man seitlich schieben musste – wer nicht wusste, dass da noch etwas
 * kommt, sah zwei Kameras von acht.
 *
 * Jetzt zwei Kacheln je Reihe (auf dem Tablet vier), Vorschau über die
 * volle Breite der Kachel im Format der Aufnahme, Name darunter in
 * Schriftfarbe. Am Zeigergerät bleibt der Streifen, wie er war: Dort ist
 * Platz, und er stört nicht. */
@media (max-width: 900px) {
  .kamerastreifen {
    display: grid;
    /* 8,5rem, nicht 10: Auf der Zeitraffer- und der Wiedergabeseite steht
       die Leiste in einem engeren Kasten (316 statt 343 Punkte). Mit 10rem
       blieb dort **eine** Kachel je Reihe übrig, und aus dreizehn Kameras
       wurden drei Bildschirmlängen. */
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: .6rem;
    overflow: visible;
  }
  /* Auch in der Kamerawahl: zwei Kacheln je Reihe statt 15rem-Kacheln,
     die am Telefon einzeln untereinander stünden. */
  .kamerawahl .kamerastreifen {
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: .6rem;
  }
  .kamerawahl { padding-inline: .8rem; }
  .kameraknopf {
    flex-direction: column;
    align-items: stretch;
    gap: .45rem;
    padding: .45rem;
    font-size: .88rem;
    color: var(--schrift);
  }
  .kameraknopf img {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
  }
  .kameraknopf .kamerabild { width: 100%; }
  /* Name und Bildalter untereinander unter der Vorschau. */
  .kameraknopf .wer {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding-inline: .15rem;
  }
  .kameraknopf > .punkt { margin-inline: .15rem; }
}

/* Das Wetterband im Bauarchiv am Handy (19.09.2026).
 *
 * Dort steht es nicht auf der vollen Breite, sondern in der rechten
 * Spalte neben der Bilderleiste: 158 Punkte für vier Werte. Gemessen
 * waren sie 208 Punkte breit – „18,9 °C" war links angeschnitten, „5
 * km/h" stand rechts außerhalb der Karte.
 *
 * Die Reihe bleibt eine Reihe (Wunsch vom 09.09.2026). Sie rückt nur
 * enger zusammen und darf notfalls geschoben werden – geschoben ist
 * lesbar, abgeschnitten nicht. */
@media (max-width: 640px) {
  .buehne:has(.bildraster) .wetterwerte {
    gap: .45rem;
    overflow-x: auto;
    scrollbar-width: none;
    justify-content: flex-start;
  }
  .buehne:has(.bildraster) .wetterwerte::-webkit-scrollbar { display: none; }
  .buehne:has(.bildraster) .wetterwerte .nebenwert strong { font-size: .95rem; }
  .buehne:has(.bildraster) .wetterwerte .nebenwert .was { font-size: .6rem; }
  .buehne:has(.bildraster) .wetterband.zweispaltig { padding-inline: .5rem; }
}

/* ------------------------------------------- Der Tagesverlauf selbst
 *
 * Das SVG wird über `preserveAspectRatio="none"` in die Breite gezogen –
 * gewollt: Jede Stunde soll genau an ihrer Stelle sitzen. Damit dabei
 * nichts flachgedrückt wird, ist der Kasten (700 × 96 in wetterband.js)
 * ungefähr so groß wie die Anzeige, und `max-width` hält ihn dort.
 *
 * Die Strichstärken sind `vector-effect: non-scaling-stroke`, sonst würden
 * sie beim Ziehen mitverzerrt und die Linie wäre auf einem breiten Schirm
 * dünner als auf einem schmalen. */
.wetterverlauf {
  /* Siehe den Kommentar am `.taetigkeitsband`: derselbe Abstand nach oben,
     damit die Reihenfolge der beiden Baender frei bleibt. Nur oben – nach
     unten setzt der naechste Baustein seinen eigenen. */
  margin: .75rem 0 0;
  min-width: 0;
  max-width: 44rem;
  /* `--gelb-schrift`, nicht `--gelb`: Im dunklen Thema ist beides das
     volle Marken-Gelb, im hellen wird daraus der tiefe Ton. Die Kurve
     trägt hier eine Auskunft – anders als der Ring auf der Lagetafel, wo
     die Zahl daneben steht. Reines Gelb auf Weiß wäre 1,5 zu 1 und für
     einen Strich von anderthalb Punkten schlicht nicht zu sehen. */
  color: var(--gelb-schrift);  /* Grundfarbe für die Fläche unter der Kurve */
}
/* Die Kurve tritt beim Überfahren einen Hauch hervor – das ist das
   Verspielte daran, mehr braucht es nicht. */
.wetterverlauf svg {
  display: block;
  width: 100%;
  height: 96px;
  overflow: visible;
}
.wetterverlauf .wtemp,
.wetterverlauf .wtempflaeche,
.wetterverlauf .wregen { transition: opacity .18s ease, stroke-width .18s ease; }
.wetterverlauf:hover .wtemp { stroke-width: 1.7; }
.wetterverlauf:hover .wregen { opacity: 1; }
/* Feine Striche (09.09.2026: „filigraner und verspielter"). Alles hier
   ist eine Nuance dünner und leiser als üblich – die Grafik steht neben
   einem Foto und soll es nicht überstimmen. Die Grundlinie verläuft nach
   rechts ins Nichts, statt hart abzubrechen. */
.wetterverlauf .wgrund {
  stroke: var(--linie);
  stroke-width: .75;
  vector-effect: non-scaling-stroke;
}
.wetterverlauf .wraster {
  stroke: var(--linie);
  stroke-width: .75;
  stroke-dasharray: 1 6;
  stroke-linecap: round;
  opacity: .5;
  vector-effect: non-scaling-stroke;
}
.wetterverlauf .wstunde {
  fill: var(--schrift-leise);
  font-size: 9px;
  font-family: var(--zahlschrift, inherit);
  letter-spacing: .06em;
  opacity: .75;
}
/* Regen: die Farbe, die für Wasser steht. Kein Verlauf und kein Schatten –
   ein Balken von wenigen Punkten Breite trägt keine Verzierung. */
/* Regen: schmale, ganz gerundete Tropfen statt Klötze. Leicht
   durchscheinend, damit die Kurve dahinter nicht abreißt. */
.wetterverlauf .wregen { fill: #4a9fd8; opacity: .82; }
.wetterverlauf .wtempflaeche { stroke: none; }
.wetterverlauf .wtemp {
  fill: none;
  stroke: var(--gelb-schrift);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* Wind: rosa und gestrichelt. Die Farbe allein genügt nicht – wer sie
   nicht unterscheiden kann, erkennt die Linie an ihrer Strichelung
   (dieselbe Regel wie bei den Zustandspunkten). */
.wetterverlauf .wwind {
  fill: none;
  stroke: var(--wind);
  stroke-width: 1.2;
  stroke-dasharray: 1.5 3.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
/* Die Marke steht senkrecht durch alles hindurch und trägt ihre Uhrzeit –
   sie beantwortet die Frage, um die es hier geht: War der Regen vor oder
   nach diesem Bild? */
.wetterverlauf .wmarke {
  stroke: var(--schrift);
  stroke-width: .75;
  stroke-dasharray: 1 3;
  stroke-linecap: round;
  opacity: .45;
  vector-effect: non-scaling-stroke;
}
.wetterverlauf .wmarke-punkt { fill: var(--schrift); opacity: .6; }
.wetterverlauf .wmarke-zeit {
  fill: var(--schrift-leise);
  font-size: 9px;
  font-family: var(--zahlschrift, inherit);
  letter-spacing: .04em;
  opacity: .85;
}

/* Der Balken, der beim Überfahren die gemeinte Stunde hervorhebt. Kein
   Rahmen, kein Schatten – nur eine Spur heller Grund, damit klar ist,
   worauf sich die abgelesenen Zahlen beziehen. */
.wetterverlauf .wzeiger { fill: currentColor; opacity: .12; pointer-events: none; }

/* ---------------------------------------------- Bautätigkeit im Tagesverlauf
 *
 * Das schmale Band unter dem Bild in BauCam und Bauarchiv (10.09.2026).
 * Dieselbe Machart wie die Wetterkurve darunter – aber flacher (48 statt
 * 96 Punkte) und in einer eigenen Farbe.
 *
 * Grün und nicht das Regenblau: Zwei Bänder untereinander in derselben
 * Farbe liest man als dieselbe Größe, und „viel Regen" wäre dann von „viel
 * los auf der Baustelle" nicht zu unterscheiden. `--gut` ist der Ton, der
 * im Haus schon für „läuft" steht.
 *
 * `height: 48px` muss zur Höhe im Skript passen (H = 48): Das SVG wird mit
 * `preserveAspectRatio="none"` gezogen, eine andere Höhe zerrt die Kurve. */
/* Luft nach oben (16.09.2026): Die Bänder sassen unmittelbar unter der
   Bildfläche – Kante an Kante, und das wirkte gedrungen. Ein halbes
   Zeilenmass genuegt: Sie sollen erkennbar zum Bild darueber gehoeren, nur
   nicht daran kleben.

   Der Abstand haengt an beiden Baendern, nicht nur an einem: Seit dem
   Tausch steht das Wetter oben und die Bautaetigkeit darunter, und
   dazwischen ist derselbe Abstand richtig wie zum Bild. Eine Regel, die
   nur fuer das erste Band gaelte, muesste bei jedem Tausch mitwandern. */
.taetigkeitsband {
  margin: .75rem auto 0;
  min-width: 0;
  max-width: 44rem;
  color: var(--gut);
}
.taetigkeitsband svg {
  display: block;
  width: 100%;
  height: 48px;
  overflow: visible;
}
.taetigkeitsband figcaption {
  display: flex;
  gap: .5rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: .1rem;
  font-size: .72rem;
}
.taetigkeitsband .tablesung {
  font-variant-numeric: tabular-nums;
  color: var(--schrift);
  min-height: 1em;
}
.taetigkeitsband .tgrund {
  stroke: var(--linie);
  stroke-width: .75;
  vector-effect: non-scaling-stroke;
}
.taetigkeitsband .traster {
  stroke: var(--linie);
  stroke-width: .75;
  stroke-dasharray: 1 6;
  stroke-linecap: round;
  opacity: .5;
  vector-effect: non-scaling-stroke;
}
.taetigkeitsband .tstunde {
  fill: var(--schrift-leise);
  font-size: 8px;
  font-family: var(--zahlschrift, inherit);
  letter-spacing: .06em;
  opacity: .7;
}
.taetigkeitsband .tbalken { fill: currentColor; opacity: .5; }
.taetigkeitsband .tflaeche { stroke: none; }
.taetigkeitsband .tlinie {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.taetigkeitsband .tmarke {
  stroke: var(--schrift);
  stroke-width: .75;
  opacity: .5;
  stroke-dasharray: 2 3;
  vector-effect: non-scaling-stroke;
}
.taetigkeitsband .tmarke-punkt { fill: var(--schrift); opacity: .6; }
.taetigkeitsband .tzeiger { fill: currentColor; opacity: .12; pointer-events: none; }
.taetigkeitsband .tbalken,
.taetigkeitsband .tlinie { transition: opacity .18s ease, stroke-width .18s ease; }
.taetigkeitsband:hover .tbalken { opacity: .72; }
.taetigkeitsband:hover .tlinie { stroke-width: 1.6; }

/* ------------------------------------------------- Wetterblatt (Druck)
 *
 * Die Tagestabelle auf `seiten/wetterdruck.html` (10.09.2026). Sie sitzt
 * in derselben `.nachweis`-Karte wie der Bildnachweis und erbt von dort
 * Papierformat, Ränder und die Druckregeln.
 *
 * Zahlen rechtsbündig und mit Tabellenziffern: Nur so stehen Komma unter
 * Komma, und man sieht auf einen Blick, welcher Tag der wärmste war. */
.wetterzusammen {
  margin: 1rem 0 1.2rem;
  font-size: .95rem;
  line-height: 1.6;
}
.wetterblatt {
  width: 100%;
  border-collapse: collapse;
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
.wetterblatt th,
.wetterblatt td {
  padding: .3rem .45rem;
  text-align: right;
  border-bottom: 1px solid var(--linie-leise);
}
.wetterblatt thead th {
  color: var(--schrift-leise);
  font-weight: 600;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  border-bottom: 1px solid var(--linie);
}
.wetterblatt thead .unterkopf th {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  border-bottom: 1px solid var(--linie);
}
.wetterblatt tbody th {
  text-align: left;
  font-weight: 400;
  white-space: nowrap;
}
/* Ein Tag ohne Messung ist eine Aussage, keine Lücke – er steht in der
 * Tabelle, aber leise. */
.wetterblatt tr.ohne { color: var(--schrift-leise); }
.wetterblatt tr.ohne th { font-style: italic; }

@media print {
  /* Der Tabellenkopf wiederholt sich auf jedem Blatt, und keine Zeile
   * wird zwischen zwei Blättern zerrissen. Bei einem Vierteljahr sind
   * das drei Seiten – ohne das stünde auf Seite zwei eine Zahlenwand
   * ohne Überschrift. */
  .wetterblatt thead { display: table-header-group; }
  .wetterblatt tr { break-inside: avoid; }
  .wetterblatt th,
  .wetterblatt td { border-bottom: 1px solid #ccc; }
}
.wetterverlauf svg { cursor: crosshair; }
/* Anwählbar nur, wo ein Klick auch etwas tut: Bauarchiv meldet sich beim
   Wetterband an, BauCam nicht (13.09.2026). Das Fadenkreuz heißt „hier
   liest du ab", die Hand heißt „hier kommst du hin" – zwei verschiedene
   Versprechen, und nur eines davon wird auf jeder Seite gehalten. */
.wetterverlauf.waehlbar svg { cursor: pointer; }
.wetterverlauf.waehlbar:hover .wzeiger { opacity: .18; }
.wetterverlauf:focus-visible { outline: 2px solid var(--gelb); outline-offset: 3px; }
.wetterverlauf:focus { outline: none; }

/* Die abgelesene Stunde. Sie steht an derselben Stelle wie die Legende und
   ersetzt sie beim Überfahren nicht, sondern tritt darüber – so springt
   nichts, und man verliert den Maßstab nicht aus dem Blick. */
.wablesung {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: .15rem .9rem;
  margin: .45rem 0 -1.15rem;
  min-height: 1.05rem;
  font-size: .76rem;
  color: var(--schrift);
  position: relative;
  z-index: 1;
}
.wablesung strong { font-weight: 600; }
.wablesung .leer { color: var(--schrift-leise); font-style: italic; }
.wablesung span { display: inline-flex; align-items: center; gap: .3rem; }
.wablesung .marke {
  width: 9px; height: 3px; border-radius: 2px; flex: none;
}
.wablesung .marke.temp { background: var(--gelb-schrift); }
.wablesung .marke.regen { background: #4a9fd8; width: 4px; height: 8px; }
.wablesung .marke.wind {
  background: none; border-top: 2px dashed var(--wind); height: 0;
}
/* Solange abgelesen wird, tritt die Legende zurück – zwei Zeilen
   übereinander wären eine zu viel. */
.wetterverlauf:has(.wablesung:not([hidden])) .wlegenden { visibility: hidden; }

.wetterverlauf .wlegenden {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .2rem 1.3rem;
  margin: .45rem 0 0;
}
.wetterverlauf .wlegende {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .72rem;
  color: var(--schrift-leise);
}
.wetterverlauf .wlegende::before {
  content: "";
  width: 13px;
  height: 3px;
  border-radius: 2px;
  background: currentColor;
  flex: none;
}
.wetterverlauf .wlegende.regen::before { background: #4a9fd8; height: 9px; width: 5px; }
.wetterverlauf .wlegende.temp::before { background: var(--gelb-schrift); }
.wetterverlauf .wlegende.wind::before {
  background: none;
  border-top: 2px dashed var(--wind);
  height: 0;
}
@media (max-width: 660px) {
  .wetterverlauf { max-width: none; }
  .wetterverlauf svg { height: 84px; }
}

/* Eine Zwischenüberschrift mit einem Knopf daneben. */
.abschnittskopf {
  display: flex;
  align-items: center;
  gap: .8rem;
  flex-wrap: wrap;
  margin: 1.6rem 0 .8rem;
}
.abschnittskopf h2 { margin: 0; margin-right: auto; }


/* Eintragen im Plan. Zugeklappt eine Zeile, aufgeklappt das Formular –
   an der Stelle, an der man die Lücke sieht. */
.planeintrag { border-bottom: 1px solid var(--linie); }
.planeintrag > summary {
  display: flex;
  align-items: center;
  gap: .45rem;
  padding: .6rem 1.15rem;
  cursor: pointer;
  font-size: .84rem;
  color: var(--schrift-leise);
}
.planeintrag > summary::-webkit-details-marker { display: none; }
.planeintrag > summary:hover { color: var(--schrift); background: var(--karte-hell); }
.planeintrag > summary .sinnbild { color: var(--gelb); }
.planeintrag[open] > summary { color: var(--schrift); border-bottom: 1px solid var(--linie-leise); }

/* Das „+" an jeder Planzeile. Unsichtbar, bis die Maus auf der Zeile ist –
   sechsunddreißig dauerhaft sichtbare Knöpfe wären lauter als der Plan. */
.planplus {
  position: sticky;
  left: 9.5rem;
  z-index: 1;
  align-self: center;
  flex: 0 0 auto;
  width: 1.35rem;
  height: 1.35rem;
  margin-left: -1.6rem;
  margin-right: .25rem;
  padding: 0;
  border-radius: 50%;
  background: var(--gelb);
  color: var(--auf-gelb);
  font-size: .9rem;
  line-height: 1;
  opacity: 0;
  transition: opacity .1s;
}
.planzeile:hover .planplus, .planplus:focus-visible { opacity: 1; }


/* Die Boxen als Kacheln. Eine Kachel je Box, Farbe der laufenden
   Baustelle als Kante – auf einen Blick sieht man, welche Boxen zusammen
   auf derselben Baustelle stehen. */
.boxkacheln {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
  gap: .9rem;
}
.boxkachel {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  padding: .8rem .95rem;
  background: var(--karte);
  border: 1px solid var(--linie);
  border-left: 4px solid hsl(var(--ton, 0) 55% 60%);
  border-radius: var(--rund);
  color: var(--schrift);
  text-decoration: none;
}
.boxkachel:hover { border-color: var(--gelb); border-left-color: hsl(var(--ton, 0) 55% 60%); }
.boxkachel.still { border-left-color: var(--linie); opacity: .72; }
.boxkachel.still:hover { opacity: 1; }
.boxkopf { display: flex; align-items: center; gap: .5rem; }
.boxkopf strong { font-size: 1rem; margin-right: auto; }
.boxpunkt {
  width: 8px; height: 8px; border-radius: 50%;
  background: hsl(var(--ton, 0) 60% 55%);
}
.boxstandort { display: flex; flex-direction: column; gap: .05rem; margin-top: .4rem; }
.boxstandort strong { font-size: .86rem; font-weight: 600; }
.boxfuss { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .55rem; }
.boxmarke {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .05rem .45rem;
  border: 1px solid var(--linie);
  border-radius: 999px;
  font-size: .72rem;
  color: var(--schrift-leise);
}
.boxmarke .sinnbild { color: var(--gelb); }

/* Die Liste. Dieselben Angaben wie in den Kacheln, nur in Spalten –
   nebeneinander lässt sich vergleichen, was untereinander nur aufzählt. */
.boxrolle { overflow-x: auto; }
.boxtabelle { width: 100%; border-collapse: collapse; font-size: .86rem; min-width: 44rem; }
.boxtabelle th, .boxtabelle td {
  padding: .55rem .8rem;
  border-bottom: 1px solid var(--linie-leise);
  text-align: left;
  white-space: nowrap;
}
.boxtabelle th {
  /* Nicht haftend: Die Tabelle steht in einem waagerecht rollenden Kasten,
     und darin bezieht sich `sticky` auf diesen Kasten – die Kopfzeile blieb
     mitten in der Tabelle stehen statt oben. Lieber keine haftende
     Kopfzeile als eine, die im Weg steht. */
  background: var(--karte);
  font-size: .72rem;
  font-weight: 500;
  color: var(--schrift-leise);
  border-bottom-color: var(--linie);
}
.boxtabelle tbody tr:hover { background: var(--karte-hell); }
.boxtabelle tbody tr:last-child td { border-bottom: 0; }
.boxtabelle .rechts { text-align: right; }
.boxtabelle a { color: var(--schrift); display: inline-flex; align-items: center; gap: .45rem; }
.boxtabelle tr.frei td { color: var(--schrift-leise); }
.boxpunkt.still { background: var(--linie); }

.boxneu, .boxform { padding: 1.15rem; display: flex; flex-direction: column; gap: .2rem; }
.boxneu { flex-direction: row; flex-wrap: wrap; gap: .5rem 1rem; align-items: flex-end; }
.boxneu .feld { flex: 1 1 12rem; margin: 0; }
.boxform .feld { margin: 0 0 .7rem; }

/* Ein Einsatz in der Liste: zugeklappt eine Zeile, aufgeklappt das
   Formular. So braucht das Ändern keine eigene Seite und keinen
   Zurück-Weg. */
.einsatzzeile { border-bottom: 1px solid var(--linie-leise); }
.einsatzzeile:last-child { border-bottom: 0; }
.einsatzzeile > summary {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  padding: .75rem 1.15rem;
  cursor: pointer;
}
.einsatzzeile > summary::-webkit-details-marker { display: none; }
.einsatzzeile:hover > summary { background: var(--karte-hell); }
.einsatzzeile > summary .projektfarbe { margin-top: .25rem; }
.einsatzzeile.unsicher > summary { border-left: 3px solid var(--achtung); }
.einsatzform { padding: .4rem 1.15rem 1.2rem; display: flex; flex-direction: column; gap: .7rem; }
.einsatzform .feld { margin: 0; }


/* ---------------------------------------------------------- Aufgaben
   Der Haken links, der Text daneben, das Entfernen rechts. Die Farbe
   sitzt am linken Rand und nicht auf der ganzen Zeile: Überfällig soll
   auffallen, ohne dass die Liste nach Alarm aussieht. */
/* Aufgaben: Kalender links, To-do-Liste rechts – auf schmalem Schirm
   untereinander. */
.aufgabenraum {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
  margin-bottom: 1.5rem;
}
@media (max-width: 780px) { .aufgabenraum { grid-template-columns: 1fr; } }

.kalendertafel .tafelkopf h2 { min-width: 8.5rem; }
.monatsgitter {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  padding: .9rem;
}
.monatswtag {
  text-align: center;
  font-size: .7rem;
  color: var(--schrift-leise);
  padding-bottom: .3rem;
}
.monatsleer { aspect-ratio: 1; }
.monatstag {
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: none;
  color: var(--schrift);
  font: inherit;
  padding: 0;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.monatstag .zahl { font-size: .82rem; font-variant-numeric: tabular-nums; }
.monatstag:hover { background: var(--karte-hell); border-color: var(--linie); }
.monatstag.heute { border-color: var(--gelb); }
.monatstag.heute .zahl { color: var(--gelb-schrift); font-weight: 600; }
.monatstag.hat { background: color-mix(in srgb, var(--gelb) 10%, transparent); }
.monatspunkte { display: flex; gap: 2px; height: 5px; }
.monatspunkte i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--gelb);
}
.monatstag.spaet.hat { background: color-mix(in srgb, var(--schlecht) 14%, transparent); }
.monatstag.spaet .monatspunkte i { background: var(--schlecht); }

/* ---- Aufgabentafeln auf dem Dashboard (09.09.2026) -------------------
 *
 * „Hebe die Aufgaben für Buchhaltung und Technik mehr hervor, dass es sich
 * abhebt und sichtbar wird." Zwei Dinge machen das: Sie stehen jetzt oben
 * (siehe `lagebild.html`), und die Tafel trägt eine Kante in der Farbe
 * ihres dringendsten Eintrags.
 *
 * Die Kante nur, wenn wirklich etwas ansteht. Eine Tafel, die immer rot
 * eingefasst ist, meldet nichts mehr — sie ist dann nur noch Dekoration. */
.aufgabentafel { border-left: 3px solid var(--linie); }
.aufgabentafel.spaet { border-left-color: var(--schlecht); }
.aufgabentafel.heute { border-left-color: var(--achtung); }
.aufgabentafel .tafelkopf h2 { font-size: 1.02rem; }

/* Die Zahlen im Kopf als Merker. „3 überfällig" liest man, „(12)" nicht. */
.aufgabenmerker {
  padding: .12rem .5rem; border-radius: 999px;
  font-size: .72rem; font-weight: 600; white-space: nowrap;
  background: var(--karte-hell); border: 1px solid var(--linie);
  color: var(--schrift-leise);
}
.aufgabenmerker.schlecht {
  color: var(--schlecht);
  border-color: color-mix(in srgb, var(--schlecht) 45%, transparent);
}
.aufgabenmerker.warnung {
  color: var(--achtung);
  border-color: color-mix(in srgb, var(--achtung) 45%, transparent);
}
/* „und 7 weitere" ist eine Zeile zum Weiterklicken, keine Aufgabe – ohne
 * Abhakknopf und blasser als die darüber. */
.aufgabe.mehr { color: var(--schrift-leise); font-size: .85rem; }

.aufgabenliste { display: flex; flex-direction: column; }
.aufgabe {
  display: flex; align-items: flex-start; gap: .75rem;
  padding: .85rem 1rem;
  border-bottom: 1px solid var(--linie-leise);
  border-left: 3px solid transparent;
  transition: background-color .3s ease;
}
.aufgabe:last-child { border-bottom: 0; }
.aufgabe.spaet { border-left-color: var(--schlecht); }
.aufgabe.jetzt { border-left-color: var(--achtung); }
.aufgabe.fertig { opacity: .55; }
/* Ein Tag im Kalender angeklickt: seine Aufgaben leuchten kurz auf. */
.aufgabe.markiert { background: color-mix(in srgb, var(--gelb) 14%, transparent); }
.aufgabetext { flex: 1; display: flex; flex-direction: column; gap: .15rem; }
.aufgabetext .aufgabefrist { display: flex; align-items: center; gap: .3rem; }
.aufgabehaken { flex: 0 0 auto; }

/* Runder Abhaken-Knopf statt Text-Häkchen – ein leerer Kreis, der beim
   Überfahren grün wird; die erledigte Fassung ist gefüllt. */
.hakenknopf {
  width: 1.5rem; height: 1.5rem; flex: 0 0 auto;
  border: 2px solid var(--linie);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  padding: 0;
  position: relative;
  transition: border-color .12s ease, background-color .12s ease;
}
.hakenknopf::after {
  content: "";
  position: absolute; left: 50%; top: 46%;
  width: 5px; height: 9px;
  border: solid transparent;
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
  transition: border-color .12s ease;
}
.hakenknopf:hover { border-color: var(--gut); }
.hakenknopf:hover::after { border-color: var(--gut); }
.hakenknopf.erledigt {
  border-color: var(--gut);
  background: var(--gut);
}
.hakenknopf.erledigt::after { border-color: #fff; }

.loeschknopf {
  flex: 0 0 auto;
  background: none; border: 0;
  color: var(--schrift-leise); font-size: 1.3rem; line-height: 1;
  cursor: pointer; padding: 0 .2rem;
}
.loeschknopf:hover { color: var(--schlecht); }

/* Das Anlege-Formular als ruhiges Raster statt Beschriftungszeilen. */
.aufgabeformular { padding: 1.1rem 1.2rem; }
.aufgabeformular .feld.breit { max-width: 34rem; }
.aufgabeformular .feldreihe { display: flex; gap: 1rem; flex-wrap: wrap; }
.aufgabeformular .feldreihe .feld { flex: 1; min-width: 12rem; max-width: 17rem; }
.aufgabeformular .feld em { font-style: normal; font-size: .78rem; }
.aufgabeabsenden { padding: 0 1.2rem 1.2rem; }
.neueaufgabe { max-width: 40rem; }


/* ------------------------------------------------------- Leistungen
   Eine Zeile je Leistung, alles direkt änderbar. Kein Bearbeiten-Knopf,
   der auf eine eigene Seite führt: Eine Preisliste ändert man im
   Vorbeigehen, nicht in einem Vorgang. */
.leistungsliste { display: flex; flex-direction: column; }
.leistungszeile {
  display: flex; align-items: center; gap: .4rem;
  padding: .5rem .9rem;
  border-bottom: 1px solid var(--linie);
  flex-wrap: wrap;
}
.leistungszeile.neu { background: var(--karte-hell); border-bottom: 0; }
.lfeld-name    { flex: 2 1 12rem; min-width: 0; }
.lfeld-text    { flex: 3 1 14rem; min-width: 0; }
.lfeld-einheit { flex: 0 0 7rem; }
.lfeld-preis   { flex: 0 0 6rem; text-align: right; }
.lfeld-steuer  { flex: 0 0 3.5rem; text-align: right; }

.firmenfelder {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: .7rem; padding: .9rem;
}
.firmenfeld { display: flex; flex-direction: column; gap: .2rem; }


/* ------------------------------------------- Angebote und Rechnungen */
.belegwesenliste { display: flex; flex-direction: column; }
.belegwesenzeile {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem .9rem; border-bottom: 1px solid var(--linie);
  color: var(--schrift); text-decoration: none;
}
.belegwesenzeile:last-child { border-bottom: 0; }
.belegwesenzeile:hover { background: var(--karte-hell); text-decoration: none; }
.bwart {
  flex: 0 0 6.5rem; font-size: .74rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--schrift-leise);
}
.bw-rechnung { color: var(--schrift); font-weight: 600; }
.bwnummer { flex: 0 0 8rem; font-size: .85rem; }
.bwname { flex: 1; display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.bwstatus {
  flex: 0 0 auto; font-size: .75rem; padding: .12rem .5rem;
  border-radius: 999px; border: 1px solid var(--linie);
  color: var(--schrift-leise);
}
.bwstatus.st-fest      { border-color: var(--gelb); color: var(--schrift); }
.bwstatus.st-versendet { border-color: #4a7fb5; color: #3a6fa5; }
.bwstatus.st-bezahlt   { border-color: #3f9c53; color: #2f7c43; }
.bwstatus.st-storniert { border-color: #d8483a; color: #b8382a; }

/* Positionen im Bearbeiten-Formular */
.poszeile {
  display: grid;
  grid-template-columns: 2.2fr 5rem 6.5rem 6.5rem 3.6rem 2rem;
  gap: .4rem; padding: .45rem .9rem;
  border-bottom: 1px solid var(--linie);
  align-items: start;
}
.poszeile input, .poszeile select { min-width: 0; }
.poszeile .rechtsfeld { text-align: right; }
.poskopf {
  display: grid;
  grid-template-columns: 2.2fr 5rem 6.5rem 6.5rem 3.6rem 2rem;
  gap: .4rem; padding: .5rem .9rem;
  background: var(--karte-hell);
  font-size: .78rem; color: var(--schrift-leise);
}
.posbeschreibung { grid-column: 1 / -1; margin-top: .25rem; }
.summenblock { display: flex; flex-direction: column; align-items: flex-end;
               gap: .3rem; padding: .9rem; }
.summenzeile { display: flex; gap: 1.5rem; font-size: .92rem; }
.summenzeile.gesamt { font-size: 1.1rem; font-weight: 600;
                      border-top: 1px solid var(--linie); padding-top: .5rem; }
.summenzeile span:last-child { min-width: 7rem; text-align: right; }

@media (max-width: 780px) {
  .poszeile, .poskopf { grid-template-columns: 1fr 1fr 1fr; }
  .poskopf { display: none; }
  .belegwesenzeile { flex-wrap: wrap; }
  .bwart, .bwnummer { flex: 0 0 auto; }
}


/* Seiten, die es noch nicht gibt: Sie sagen das, statt zu fehlen. */
.leerraum {
  display: flex; flex-direction: column; align-items: center; gap: .8rem;
  padding: 3rem 1.5rem; text-align: center; color: var(--schrift-leise);
}
.leerraum p { max-width: 34rem; margin: 0; }
.leerraum .sinnbild { color: var(--linie); }


/* Listenzeilen mit Zustandspunkt – vom Lagebild übrig geblieben und von
   der Kundenakte weiterbenutzt. Beim Umbau des Lagebilds auf Kacheln waren
   sie mit verschwunden; das Kollisionswerkzeug hat es gemeldet. */
.ortsliste { display: flex; flex-direction: column; }
.ortszeile {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem .9rem; border-bottom: 1px solid var(--linie);
  color: var(--schrift); text-decoration: none;
}
.ortszeile:last-child { border-bottom: 0; }
a.ortszeile:hover { background: var(--karte-hell); text-decoration: none; }
.ortsname { flex: 1; display: flex; flex-direction: column; gap: .1rem; min-width: 0; }

/* Der Weg von der Kontaktzeile in die Akte. */
.kakte {
  margin-left: auto; font-size: .82rem; white-space: nowrap;
  color: var(--schrift-leise); text-decoration: none;
}
.kakte:hover { color: var(--schrift); }

/* --------------------------------------------------------- Kundenakte
   Zwei Spalten: links, was der Kunde mit uns tut (Baustellen, Belege),
   rechts, wer er ist (Stammdaten, Zugänge). Auf schmalen Fenstern
   untereinander, Tätigkeit zuerst. */
.aktenspalten {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 1.25rem;
  align-items: start;
}
.aktenfelder {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: .6rem; padding: .9rem;
}
@media (max-width: 900px) {
  .aktenspalten { grid-template-columns: 1fr; }
}


/* Mail-Entwurf: Beschriftung über dem Feld, alles volle Breite. */
.mailfelder { display: flex; flex-direction: column; gap: .8rem; padding: .9rem; }
.mailfelder label { display: flex; flex-direction: column; gap: .25rem; }
.mailfelder textarea { width: 100%; font-family: inherit; line-height: 1.5; }


/* Baustelle ohne freigegebene Kamera: eine ruhige Zeile.
   Vorher stand dort `.leer` - der gestrichelte Rahmen mit 4 rem Polsterung,
   gedacht fuer eine ganz leere Seite. Bei einer einzelnen Baustelle nahm er
   einen halben Bildschirm ein, nur um zu sagen, dass nichts da ist; wer
   fuenf solche hat, scrollt an fuenf leeren Kaesten vorbei, ehe er die
   erste Kamera sieht. */
.ohnekamera {
  margin: 0; padding: .55rem .2rem 1.1rem;
  font-size: .88rem; color: var(--schrift-leise);
}


/* Unser interner Anlagenname in der Kundenansicht.
   Nur die Verwaltung bekommt ihn überhaupt geliefert - der Kunde sieht ihn
   nie, weder im Text noch im Quelltext der Seite. Gestrichelter Rahmen,
   damit im Vorführmodus auf einen Blick klar ist, dass das nichts ist, was
   der Kunde sieht. */
.internername {
  font-size: .72rem; color: var(--schrift-leise);
  border: 1px dashed var(--linie); border-radius: var(--rund);
  padding: .05rem .4rem; white-space: nowrap;
}


/* Zwei Namen je Baustelle: unserer und der des Kunden. */
.namensschild .kundenname {
  font-size: .8rem; color: var(--schrift-leise);
  border-left: 2px solid var(--gelb); padding-left: .5rem;
}
/* Abrufe je Tag auf der Kamera-Auskunft: ein Band, Flexbox statt gestrecktem SVG. */
.abrufband { display: flex; align-items: stretch; gap: 2px; height: 46px; margin: .6rem 0 .2rem; }
.abrufband i { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; }
.abrufband i b { display: block; background: var(--gelb); border-radius: 2px 2px 0 0; min-height: 2px; }
.abrufband i.leer b { background: var(--linie); }

/* Buchhaltung → Überblick: die Übersicht mit Kurven (02.09.2026).
 * Grün und Rot sind Zustand (Einnahme/Ausgabe), nie Gelb – das ist Bedienung.
 * Das SVG wird im Browser in Pixelmaßen gezeichnet und bei Größenänderung
 * neu; deshalb hier keine Höhe für das SVG selbst, nur für den Kasten. */
.uebersichtwahl { display: flex; gap: .5rem; margin-left: auto; flex-wrap: wrap; justify-content: flex-end; }
.uebersichtwahl select { width: auto; font-size: .85rem; padding: .35rem 1.7rem .35rem .65rem; }
.uebersichtsummen {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem;
  padding: 1rem 1.1rem .2rem;
}
.uebersichtsummen > div { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.uebersichtsummen > div.rechts { text-align: right; align-items: flex-end; }
.uebersichtsummen strong { font-size: 1.55rem; font-weight: 600; letter-spacing: -.02em; line-height: 1.1; }
.uebersichtsummen strong.warnt { color: var(--schlecht); }
.kurvenpunkt {
  display: inline-block; width: .55rem; height: .55rem; border-radius: 50%;
  vertical-align: middle; margin-right: .15rem; background: var(--schrift-leise);
}
.kurvenpunkt.ein { background: var(--gut); }
.kurvenpunkt.aus { background: var(--schlecht); }
.kurvenkasten { position: relative; padding: .4rem 1.1rem 0; }
.kurvenkasten.gezeichnet { height: clamp(13rem, 34vh, 21rem); }
.kurventabelle { margin: .4rem 0 .6rem; }
.kurven { display: block; width: 100%; height: 100%; overflow: visible; font: 11px var(--textschrift, inherit);
         touch-action: pan-y; user-select: none; -webkit-user-select: none; outline: none; }
.kurven:focus-visible { outline: 2px solid var(--gelb); outline-offset: 4px; border-radius: 4px; }
/* Nur fürs Auge versteckt – Vorleser und Tastatur behalten die Tabelle. */
.nur-vorleser { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; margin: -1px; }
.kurven .gitter { stroke: var(--linie); stroke-width: 1; }
.kurven .gitter.null { stroke: var(--schrift-leise); opacity: .55; }
.kurven .achse { fill: var(--schrift-leise); font-variant-numeric: tabular-nums; }
.kurven .achse.monat { font-size: 12px; }
.kurven .linie { fill: none; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; }
.kurven .linie.ein { stroke: var(--gut); }
.kurven .linie.aus { stroke: var(--schlecht); }
.kurven .flaeche { opacity: .11; }
.kurven .flaeche.ein { fill: var(--gut); }
.kurven .flaeche.aus { fill: var(--schlecht); }
.kurven .kurvenmarke line { stroke: var(--schrift); stroke-dasharray: 4 4; opacity: .55; }
.kurven .kurvenmarke circle { stroke: var(--karte); stroke-width: 2.5; }
.kurven .kurvenmarke circle.ein { fill: var(--gut); }
.kurven .kurvenmarke circle.aus { fill: var(--schlecht); }
.kurventip {
  position: absolute; z-index: 3; pointer-events: none;
  background: var(--karte-hell); border: 1px solid var(--linie);
  border-radius: var(--rund); padding: .55rem .85rem .5rem;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22); min-width: 14rem; font-size: .85rem;
}
.kurventip strong { display: block; margin-bottom: .35rem; font-size: .95rem; }
.kurventip table { border-collapse: collapse; width: 100%; }
.kurventip td { padding: .14rem 0; color: var(--schrift-leise); }
.kurventip td:last-child { text-align: right; color: var(--schrift); font-weight: 600; padding-left: 1rem; font-variant-numeric: tabular-nums; }
.kurventip td.warnt { color: var(--schlecht); }
.kurvenfuss { padding: .5rem 1.1rem 1rem; margin: 0; }
/* `.brennt` stand an Lagenkarten, war aber nirgends definiert – ein leiser roter Rand. */
.lagenkarte.brennt { border-color: color-mix(in srgb, var(--schlecht) 40%, var(--linie)); }
/* Eine Kachel, die weiterführt, muss sich auch so anfühlen. Sie ist seit dem
 * 02.09.2026 ein `<a>`; Farbe und Unterstreichung eines Links will sie aber
 * nicht – sie ist eine Fläche, kein Wort. */
a.lagenkarte { color: inherit; text-decoration: none; }
a.lagenkarte:hover { border-color: color-mix(in srgb, var(--gelb) 55%, var(--linie)); }
a.lagenkarte:hover .lagenwort .pfeil { color: var(--gelb); }
a.lagenkarte .pfeil { transition: color .13s ease; }
@media (max-width: 680px) {
  .uebersichttafel .tafelkopf { flex-wrap: wrap; }
  .uebersichtwahl { flex-basis: 100%; margin-left: 0; justify-content: flex-start; }
  .uebersichtwahl select { max-width: 100%; }
  .uebersichtsummen { grid-template-columns: 1fr 1fr; }
  .uebersichtsummen strong { font-size: 1.2rem; }
  .uebersichtsummen > div.rechts { grid-column: 1 / -1; text-align: left; align-items: flex-start; }
  .kurvenkasten.gezeichnet { height: clamp(11rem, 40vh, 16rem); }
  .kurventip { min-width: 11rem; font-size: .8rem; }
}

/* Papierkorb der Belege (02.09.2026). Die Marke steht rechts hinter den
 * Filtern, leise – sie ist kein Filter, sondern ein Ausgang. */
.belegfilter a.papierkorb { margin-left: auto; color: var(--schrift-leise); }
.papierkorbknoepfe { display: flex; flex-direction: column; gap: .4rem; align-self: center; }
.papierkorbknoepfe form { margin: 0; }
.papierkorbknoepfe button { width: 100%; white-space: nowrap; font-size: .82rem; padding: .35rem .7rem; }
a.belegbild { text-decoration: none; }

/* Jahre in der Belegliste (02.09.2026): eine zweite Markenreihe unter den
 * Arten und leise Überschriften in der Liste, wo ein Jahr das nächste ablöst. */
.belegfilter.jahre { margin-top: -.2rem; }
.jahresmarke {
  margin: 0; padding: .75rem 1.1rem .35rem; font-size: .76rem; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; color: var(--schrift-leise);
  border-top: 1px solid var(--linie);
}
.belegliste > .jahresmarke:first-child { border-top: 0; padding-top: .5rem; }
/* Beträge brechen nicht um – „1.881,39" und „€" gehören in eine Zeile. */
.tabelle td.zahlen, .tabelle th.zahlen { white-space: nowrap; }

/* Der Stift im Beleg-Betrachter (02.09.2026): Betrag, Datum, Nummer berichtigen.
 * Nicht `.belegangaben` – die Klasse gehört schon der Angabenzeile in der Liste. */
.belegstift { display: flex; flex-wrap: wrap; gap: .6rem .9rem; align-items: flex-end; padding: .6rem 1rem .8rem; border-bottom: 1px solid var(--linie); margin: 0; }
.belegstift label { display: flex; flex-direction: column; gap: .2rem; flex: 0 1 11rem; }
.belegstift input { width: 100%; padding: .35rem .55rem; font-size: .9rem; }
.belegstift button { padding: .4rem .8rem; }
.belegstift #angabe_hinweis { flex-basis: 100%; }
.vonhand { color: var(--gelb-schrift); font-size: .8em; margin-left: .15rem; }
/* Geschützte Datei beim Hochladen: eine Frage, kein Fehler – deshalb gelb, nicht rot. */
.ablagezeile.schutz .ablagename { color: var(--schrift); }
.ablagezeile.schutz .ablagewas { color: var(--gelb-schrift); font-weight: 600; }

/* ---------------------------------------- Bautätigkeit je Tag (02.09.2026)
 * Balken oben wie beim Tagesverlauf; darunter der Regen als hängende
 * blaue Striche – von oben, damit niemand sie für einen zweiten Balken
 * desselben Maßes hält. `--regen` ist neu und gehört hierher, nicht nur
 * benutzt (siehe die Lehre zu `--akzent`). */
:root { --regen: #3d8bfd; }
.taetigkeitsverlauf .tagesspalte.offen .saeule { opacity: .6; }
.regenreihe {
  display: flex;
  gap: 2px;
  height: 2.2rem;
  align-items: stretch;
  margin-top: 2px;
}
.regenreihe .tagesspalte {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.regenreihe .tropfen {
  width: 100%;
  min-height: 2px;
  border-radius: 0 0 3px 3px;
  background: var(--regen);
}
.taetigkeitszahlen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .8rem 1.2rem;
  margin-bottom: 1rem;
}
.taetigkeitszahlen strong {
  display: block;
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

/* Portalstatistik (20.09.2026): Kennzahlen links gebündelt statt über die
 * ganze Breite verstreut – ein Blick, nicht eine Wanderung. Eine Tabelle je
 * Tag mit festen Spaltenbreiten, damit „Heute" und „Gestern" untereinander
 * stehen. Auf dem Telefon rollt sie in ihrer Karte (`.tabellenrolle`). */
.kennzahlen {
  display: flex;
  flex-wrap: wrap;
  gap: .7rem 2.4rem;
  margin: 0 0 1.1rem;
}
.kennzahlen > div { min-width: 7rem; }
.kennzahlen strong {
  display: block;
  font-size: 1.55rem;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.kennzahlen strong.klein { font-size: 1rem; padding-top: .4rem; }
.kennzahlen span {
  display: block;
  font-size: .76rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--schrift-leise);
}
.kennzahlen small {
  display: block;
  font-size: .8rem;
  color: var(--schrift-leise);
}
.tabelle.nutzer { table-layout: fixed; width: 100%; min-width: 46rem; }
.tabelle.nutzer td { vertical-align: top; }
details.aufklapp summary {
  display: flex;
  align-items: center;
  gap: .45rem;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}
details.aufklapp summary::-webkit-details-marker { display: none; }
details.aufklapp summary::before { content: "›"; display: inline-block; transition: transform .15s; }
details.aufklapp[open] summary::before { transform: rotate(90deg); }
details.aufklapp[open] summary { margin-bottom: .7rem; }

/* Kalender: Tage nach Bautätigkeit getönt. `--stufe` (0..1) setzt die
 * Seite je Tag; die Farbe kommt aus der Hausfarbe, nicht aus einer
 * zweiten Palette. Der gewählte Tag bleibt satt. Die Hover-Regel oben
 * setzt sonst `--karte-hell` und löschte die Tönung – deshalb eigens. */
.kalendergitter .tag.getoent:not(.gewaehlt) {
  background: color-mix(in srgb, var(--gelb) calc(var(--stufe, 0) * 60%), transparent);
}
.kalendergitter .tag.getoent:not(.gewaehlt):hover:not(:disabled) {
  background: color-mix(in srgb, var(--gelb) calc(var(--stufe, 0) * 60% + 12%), var(--karte-hell));
}
.kalenderfuss .toenung {
  width: 2.2rem; height: .6rem;
  border-radius: 3px;
  border: 1px solid var(--linie);
  background: linear-gradient(90deg, transparent, var(--gelb));
}

/* Lageplan: die Ablagefläche wie bei den Belegen; während des Hochladens
 * ausgegraut, damit niemand zweimal zieht. */
.planablage { margin: 0; }
.planablage .ablagefeld.laeuft { opacity: .55; pointer-events: none; }

/* ------------------------------------- Geheimnisfelder (02.09.2026)
 * Kamera-, Postfach- und Belegpasswörter sind keine Anmeldedaten – Safari
 * hielt sie trotzdem dafür: Jedes abgeschickte Formular mit einem
 * `type="password"` löste „Passwort sichern?" für das eigene Konto aus,
 * und wer auf „Sichern" drückte, hatte das Kamerapasswort als Passwort
 * seiner TB-Web-Anmeldung hinterlegt. Ein Textfeld, das der Browser nur
 * maskiert, ist für den Passwort-Manager kein Passwort. Wo der Browser
 * das Maskieren nicht kann, macht `thema.js` wieder ein Passwortfeld
 * daraus – dann lieber Safaris Frage als Klartext. */
input.geheim { -webkit-text-security: disc; }

/* ----------------------------------- Karten in Spalten (02.09.2026)
 * `.karten` ist ein Raster: jede Reihe so hoch wie ihre höchste Karte.
 * Bei Karten sehr verschiedener Höhe (Kamera-Info: drei Zeilen Uhr neben
 * zwanzig Zeilen Netz) bleiben Löcher. `mauer` legt sie stattdessen in
 * Spalten; die nächste Karte rückt nach, wo Platz ist. */
.karten.mauer {
  display: block;
  columns: 21rem;
  column-gap: 1.5rem;
}
.karten.mauer > .karte {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin: 0 0 1.5rem;
}
/* Eine breite Tabelle rollt in ihrer Karte, statt sie zu sprengen. */
.tabellenrolle { overflow-x: auto; }
.tabellenrolle > .tabelle { min-width: max-content; }

/* Die Betrieb-Tafel bricht um, statt zu rollen. `min-width: max-content`
   oben machte sie so breit wie ihr längster Satz – „Zahlen" und „Letzter
   Fehler" standen rechts außerhalb des Bildes und waren nur durch Schieben
   zu lesen (06.09.2026 bemängelt). Anders als eine Zahlentabelle trägt die
   Tafel ganze Sätze; die gehören umgebrochen.

   Die zwei Zeitspalten standen hier bis zum 09.09.2026 auf `width: 1px` –
   „so schmal wie nötig". Das war die richtige Antwort für eine Tabelle, die
   sich am Inhalt bemisst; seit sie feste Spalten hat (`.betriebstafel`
   weiter oben), ist es die falsche: Ein ID-Wähler sticht jede Klasse, und
   die beiden Spalten blieben bei siebenundzwanzig Punkten stehen, während
   „Zahlen" sechshundert bekam. Die Breiten stehen jetzt an einer Stelle,
   nämlich dort. */
.tabellenrolle > #tafel { min-width: 40rem; }
/* Am Telefon kein Mindestmaß: Die Sätze brechen um, statt dass die Tafel
 * in ihrer Karte rollt (20.09.2026). */
@media (max-width: 640px) { .tabellenrolle > #tafel { min-width: 0; } }
#tafel td { overflow-wrap: anywhere; }

/* Lange Zeichenketten ohne Leerzeichen – Seriennummer, Livebild-Kennung,
 * Gerätename – liefen aus der Karte (02.09.2026). Ein Wert darf
 * umbrechen, wo er muss; ein Raster-Kind braucht dafür `min-width: 0`,
 * sonst hält es an seiner Inhaltsbreite fest. */
.angaben dd { min-width: 0; overflow-wrap: anywhere; }
.merkmal { white-space: normal; }

/* Baustellen im Vergleich: Name, Schiene, Wert – eine Zeile je Baustelle.
 * Keine Tabelle: Drei Angaben, von denen eine ein Balken ist, liest man als
 * Reihe besser als in Spalten. */
.ortsbalken { display: flex; flex-direction: column; gap: .1rem; }
/* Eigener Name, nicht `.ortszeile`: Diesen Namen trug schon die Kundenakte
 * (Zeile 5002, eine Reihe). Da die spätere Regel gewinnt, bekam die Akte das
 * Gitter der Bautätigkeit – der Baustellenname stand hinter einer 40-%-Spalte
 * Leere. Am 03.09.2026 gemessen und getrennt. */
.taetigkeitszeile {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(4rem, 40%) auto;
  align-items: center;
  gap: .7rem;
  padding: .45rem .3rem;
  border-radius: var(--rund);
  color: var(--schrift);
  text-decoration: none;
}
.taetigkeitszeile:hover { background: var(--karte-hell); text-decoration: none; }
.taetigkeitszeile .ortsname {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .88rem;
}
.ortsschiene {
  height: 8px;
  border-radius: 4px;
  background: var(--linie);
  overflow: hidden;
}
.ortsschiene i { display: block; height: 100%; background: var(--gelb); border-radius: 4px; }
.ortswert {
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
  color: var(--schrift-leise);
  min-width: 2.2rem;
  text-align: right;
}

/* ------------------------------------ Kacheln oder Liste (02.09.2026)
 * Ein Umschalter mit zwei Feldern in einem gemeinsamen Rahmen – so liest
 * man sofort, dass es eine Wahl zwischen zweien ist und nicht zwei Knöpfe,
 * die Verschiedenes tun. Das gewählte Feld trägt Fläche und Gewicht, nicht
 * nur Farbe. */
/* In der Werkzeugzeile steht der Umschalter am Ende, hinter dem, was
 * wirklich etwas tut. Ohne zusätzlichen Rand links: Bricht die Zeile auf
 * einem schmalen Schirm um, soll er an derselben Kante beginnen wie das
 * Suchfeld darüber – acht Punkte Einzug sähen aus wie ein Versehen
 * (09.09.2026 nachgemessen). Der Abstand der Zeile reicht. */
.ansichtswahl {
  display: inline-flex;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  overflow: hidden;
}
.ansichtswahl a,
.ansichtswahl button {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .42rem .7rem;
  font-size: .84rem;
  color: var(--schrift-leise);
  text-decoration: none;
  background: none;
}
.ansichtswahl a + a,
.ansichtswahl button + button { border-left: 1px solid var(--linie); }
.ansichtswahl a:hover,
.ansichtswahl button:hover { color: var(--schrift); background: var(--karte-hell); text-decoration: none; }
/* Auf der Kundenübersicht sind es Knöpfe statt Verweise (08.09.2026): Die
 * Seite zeichnet sich selbst neu, es wird nichts nachgeladen. Ein Knopf
 * bringt eigene Ränder, eigene Schrift und eigenen Grund mit — die muss man
 * ihm nehmen, sonst sitzt in der Leiste ein Systemknopf. */
.ansichtswahl button {
  border: 0; font: inherit; font-size: .84rem; cursor: pointer;
}
.ansichtswahl a.an,
.ansichtswahl button.an {
  color: var(--gelb-schrift);
  font-weight: 600;
  background: color-mix(in srgb, var(--gelb) 16%, transparent);
}

/* Die Kameraliste. Der Zustandspunkt steht vor dem Namen statt in einer
 * eigenen Spalte – eine Spalte mit acht Pixeln Inhalt ist keine Spalte. */
.kameraliste td .punkt { display: inline-block; margin-right: .45rem; vertical-align: middle; }
.kameraliste td a { color: var(--schrift); font-weight: 600; }
.kameraliste td a:hover { color: var(--gelb); }
/* Die drei Sinnbilder stehen seit dem 09.09.2026 links, gleich neben dem
 * Vorschaubild — also linksbündig und eng, nicht mehr rechts ausgerichtet. */
.kameraliste .symbole { justify-content: flex-start; gap: .55rem; }
/* Ebenfalls die feste Farbe des dunklen Themas – siehe oben. */
.kameraliste td.alt { color: var(--achtung); }
.kameraliste tr.stillgelegt td a { font-weight: 500; }

/* Sortierwahl in der Kopfzeile der Kameraliste. Das Wort davor, damit die
 * Auswahl nicht nackt dasteht und man weiß, worauf sie sich bezieht. */
.sortierwahl { display: inline-flex; align-items: center; gap: .45rem; }
.sortierwahl select {
  width: auto;
  min-width: 0;
  max-width: min(14rem, 100%);
  padding: .35rem .5rem;
  font-size: .84rem;
}

/* „schieben" steht dort, wo sonst die beiden Pfeile stehen – aber es ist ein
 * Wort, kein Zeichen. Die Regel für `.ordnen a` gibt jedem Kind 22 × 22
 * Punkte; das Wort lief darin über die Nachbarn (02.09.2026 gesehen). */
.namensschild .ordnen a.schiebehinweis {
  width: auto;
  height: auto;
  padding: .15rem .45rem;
  font-size: .74rem;
  color: var(--schrift-leise);
  white-space: nowrap;
}
.namensschild .ordnen a.schiebehinweis:hover { color: var(--gelb); background: none; }
/* Auf dem Handy weg: Das Schild trägt dort schon Strich, Namen, Kundennamen
 * und Kamerazahl; das Wort schob es über den Bildschirmrand (gemessen: 414
 * statt 375 Punkte). Sortiert wird am Handy ohnehin über die Auswahl oben,
 * und geschoben wird am Schreibtisch. */
@media (max-width: 640px) {
  .namensschild .ordnen a.schiebehinweis { display: none; }
  /* Das Schild ist eine Flexzeile und wächst mit dem Namen. `max-width: 100%`
   * deckelt zwar das Schild, nicht aber seine Kinder: Ein langer Name schob
   * die Zeichen rechts auf 404 statt 375 Punkte, und damit die ganze Seite
   * (gemessen). Umbrechen lassen statt überlaufen – dann rutschen die
   * Zeichen in eine zweite Zeile im Schild. */
  .namensschild { flex-wrap: wrap; }
  .namensschild h2 { min-width: 0; overflow-wrap: anywhere; }
  .namensschild .ordnen { margin-left: auto; }

  /* Die Kachelbreite folgt dem Seitenverhältnis der Kamera. Bei einem
   * Panorama sind das 604 Punkte – auf einem 375 Punkte breiten Bildschirm
   * schob eine einzige Kachel die ganze Seite seitwärts (gemessen). Am Handy
   * bekommt jede Kachel deshalb die volle Breite, und das Bild richtet sich
   * nach ihr statt umgekehrt: `--bildhoehe: auto` würde `calc()` ungültig
   * machen, deshalb `max-width` und eine Höhe, die sich anpasst. */
  .kachel { max-width: 100%; min-width: 0; }
  .kachel .bildflaeche { height: auto; aspect-ratio: var(--verhaeltnis, 1.7778); }
  .kachel .bildflaeche img { width: 100%; height: 100%; object-fit: cover; }
}

/* Vorschaubild in der Kameraliste: klein, aber groß genug, um die Baustelle
 * zu erkennen. Feste Breite, damit die Spalte nicht mit dem Seitenverhältnis
 * der Kamera wandert; `cover` ist hier richtig, weil der Kasten 16:9 hat und
 * die Bilder auch – abgeschnitten wird nichts Wesentliches. */
/* Die Listenansicht der Kundenübersicht: eine Tabelle je Baustelle —
 * und damit, bis zum 09.09.2026, eine eigene Spaltenbreite je Baustelle.
 *
 * Der Grund ist die Vorgabe `table-layout: auto`: Jede Tabelle misst ihre
 * Spalten am eigenen Inhalt. Bei „Box 7 - K 1" stand „Zustand" weit links,
 * bei „Kamera 1 · Box 10 - Mühlhausen" weit rechts. Untereinander sah das
 * aus, als wäre die Seite verrutscht — bemängelt am 09.09.2026.
 *
 * `fixed` misst nur die erste Zeile und nimmt die Breiten, die hier
 * stehen. Damit stehen alle Blöcke auf denselben Achsen, egal wie lang ein
 * Kameraname ist. Die Namensspalte bekommt keine Breite: Sie nimmt, was
 * übrig bleibt, und bricht um statt zu schieben.
 *
 * `min-width` statt Quetschen: Auf einem schmalen Handy rollt die Tabelle
 * in ihrer Karte (`.tabellenrolle`), statt vier Spalten auf 375 Punkte zu
 * pressen. */
.tabelle.kundenliste {
  table-layout: fixed;
  width: 100%;
  min-width: 46rem;
}
/* Feste Breite auch für den Namen – sonst bekäme er allein den ganzen
 * Rest, und Zustand und Uhrzeit stünden am äußersten Rand. Was übrig
 * bleibt, nimmt die Leerspalte hinten auf. */
.tabelle.kundenliste th:nth-child(2),
.tabelle.kundenliste td:nth-child(2) { width: 26rem; }
.tabelle.kundenliste th:nth-child(3),
.tabelle.kundenliste td:nth-child(3) { width: 8.5rem; }
.tabelle.kundenliste th:nth-child(4),
.tabelle.kundenliste td:nth-child(4) { width: 10rem; }
/* Der Name darf umbrechen; der interne Name rutscht dann in die zweite
 * Zeile, statt die Spalte aufzublähen. */
.tabelle.kundenliste td:nth-child(2) { overflow-wrap: anywhere; }

/* Dieselbe Sache in der Verwaltungsliste (09.09.2026 nachgemessen: bis zu
 * 70 Punkte Versatz zwischen zwei Baustellenblöcken). Auch dort eine
 * Tabelle je Baustelle, auch dort `auto` — und dort fällt es noch mehr auf,
 * weil sieben Spalten mitwandern.
 *
 * `[data-ort]` grenzt es sauber gegen die Kundenliste ab: Die trägt
 * dieselbe Klasse, hat aber vier Spalten statt sieben, und die Breiten
 * unten wären dort die falschen. Die Namensspalte bekommt wieder keine
 * Breite und nimmt, was übrig bleibt. */
.tabelle.kameraliste[data-ort] {
  table-layout: fixed;
  width: 100%;
  min-width: 67rem;   /* Summe der festen Spalten, sonst quetscht es sie */
}
/* Die Spalten von links nach rechts: Griff, Vorschau, die drei Sinnbilder,
 * Kamera, Letztes Bild, Für den Kunden, Freigegeben — und ganz zuletzt eine
 * Spalte ohne Inhalt.
 *
 * Die letzte Spalte ist der eigentliche Kniff. Vorher hatte der Kameraname
 * keine Breite und bekam damit alles, was übrig blieb: bei 1440 Punkten
 * knapp 580 davon. Der Name selbst ist selten länger als zwanzig Zeichen,
 * also stand zwischen ihm und dem Rest der Zeile ein halber Bildschirm
 * Nichts, und die Bedienknöpfe klebten am äußersten Rand. Jetzt hat jede
 * Spalte mit Inhalt ihre Breite, und der Überschuss sammelt sich hinten in
 * einer leeren Spalte, wo er niemandem im Weg steht. */
.kameraliste[data-ort] th.symbolspalte,
.kameraliste[data-ort] td.symbolspalte { width: 9rem; }   /* vier Sinnbilder */
/* Luft zum Vorschaubild („mach den abstand etwas luftiger", 09.09.2026).
 * Das Bild ragt um seinen linken Zellenabstand über die Spaltengrenze
 * hinaus – gemessen endet es 5 Punkte hinter der Achse –, und der
 * gewöhnliche Zellenabstand von 0,8rem ließ danach nur 8 Punkte. Mit
 * 1,8rem sind es rund 24: die Sinnbilder gehören sichtbar zur Zeile, nicht
 * zum Bild. Nach rechts bleibt der Abstand zur Kamera, wie er ist. */
.kameraliste[data-ort] td.symbolspalte { padding-left: 1.8rem; }
.kameraliste[data-ort] th:nth-child(4),
.kameraliste[data-ort] td:nth-child(4) { width: 17rem; }
.kameraliste[data-ort] th:nth-child(5),
.kameraliste[data-ort] td:nth-child(5) { width: 10rem; }
.kameraliste[data-ort] th:nth-child(6),
.kameraliste[data-ort] td:nth-child(6) { width: 13rem; }
.kameraliste[data-ort] th:nth-child(7),
.kameraliste[data-ort] td:nth-child(7) { width: 11rem; }
.kameraliste[data-ort] td:nth-child(4) { overflow-wrap: anywhere; }
/* Ohne Breite — sie nimmt, was übrig bleibt, und zeigt nichts an. */
.kameraliste[data-ort] th.restspalte,
.kameraliste[data-ort] td.restspalte { width: auto; }

.kameraliste .bildspalte { width: 74px; padding-right: 0; }
/* Der Rahmen folgt seit dem 09.09.2026 dem Bild, nicht umgekehrt.
 *
 * Vorher war er für alle Zeilen 66 × 37 (16:9), und was nicht 16:9 war,
 * wurde mit `contain` hineingelegt – ganz zu sehen, aber mit schwarzem
 * Rand: Box 4 K2 liefert 4:3 (640 × 480) und stand als schmales Bild
 * zwischen zwei schwarzen Balken („das Format passt nicht"). `cover`
 * stattdessen hätte beschnitten: bei Geisenfeld (1,22) oben und unten je
 * ein Achtel, bei den Panoramakameras (3,56) links und rechts je ein
 * Drittel.
 *
 * Jetzt bekommt jedes Bild einen Rahmen in seinem eigenen Verhältnis
 * (`--verhaeltnis`, aus `seitenverhaeltnis` der Kamera – dasselbe, das die
 * Kacheln benutzen): Die HÖHE ist fest (37), damit die Zeilen gleich hoch
 * bleiben, die Breite ergibt sich – 4:3 wird 49 breit, 16:9 bleibt 66.
 * Nur die Panoramakameras kämen breiter als die Spalte; die werden bei 66
 * gekappt und damit flacher (18 hoch) statt breiter. Dann füllt das Bild
 * seinen Rahmen genau: kein Rand, kein Beschneiden.
 *
 * `passt`/`eingepasst` aus der Vorlage bleiben als Klassen stehen (die
 * Suche nach Zeilen fragt sie nicht), bedeuten aber nichts mehr. */
/* Der Verweis um das Bild hält die Zeilenhöhe: 37 hoch, auch wenn das
 * Bild darin flacher ist (Panorama 66 × 19). Sonst wäre die Panoramazeile
 * 14 Punkte niedriger als ihre Nachbarn – nachgemessen 46 zu 60. */
.kameraliste .bildspalte > a {
  display: flex;
  align-items: center;
  min-height: 37px;
}
.kameraliste .bildspalte img {
  display: block;
  /* Als Flex-Kind nicht schrumpfen: Die Zelle ist innen 61 breit, das Bild
   * 66 – es ragt seit jeher fünf Punkte über die Spaltenachse, und das ist
   * so gewollt (gemessen 09.09.2026). Ohne `flex: none` drückte der Verweis
   * es auf 60 × 34 zusammen. */
  flex: none;
  /* Breite aus der Höhe, bei 66 gekappt; die Höhe folgt dann dem
   * Verhältnis. `height: 37px` mit `max-width` daneben hielt beides fest
   * und beschnitt die Panoramakameras wieder (nachgemessen 09.09.2026:
   * 66 × 37 statt 66 × 18). */
  width: min(66px, calc(37px * var(--verhaeltnis, 1.7778)));
  height: auto;
  aspect-ratio: var(--verhaeltnis, 16 / 9);
  object-fit: cover;
  border-radius: 4px;
  background: var(--grund-tief);
}
.kameraliste .bildspalte .kein-bild {
  display: grid;
  place-items: center;
  width: 66px;
  aspect-ratio: 16 / 9;
  border: 1px dashed var(--linie);
  border-radius: 4px;
  color: var(--schrift-leise);
  font-size: .8rem;
}

/* ---------------------------------------- Bildschau (02.09.2026)
 * Das 4K-Standbild über der Seite statt in einem neuen Tab. Der Grund ist
 * fast schwarz, aber nicht ganz: Ein Nachtbild ist selbst dunkel, und auf
 * reinem Schwarz sieht man seine Kanten nicht mehr. */
.bildschau {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: clamp(.8rem, 3vw, 2.5rem);
  background: rgba(6, 6, 8, .88);
  backdrop-filter: blur(3px);
}
.bildschau figure { margin: 0; display: flex; flex-direction: column; gap: .6rem; max-width: 100%; }
.bildschau-flaeche {
  position: relative;
  display: grid;
  place-items: center;
  min-width: min(22rem, 80vw);
  min-height: 8rem;
  border-radius: var(--rund-gross);
  overflow: hidden;
  background: #000;
}
.bildschau img {
  display: block;
  max-width: 100%;
  /* Höhe deckeln, damit die Beschriftung unter dem Bild sichtbar bleibt –
   * sonst schiebt ein 4K-Bild sie aus dem Fenster. */
  max-height: calc(100vh - 8rem);
  object-fit: contain;
}
.bildschau-laedt { margin: 0; padding: 2.5rem 1.5rem; color: #d8dac9; font-size: .9rem; }
.bildschau figcaption {
  color: #d8dac9;
  font-size: .9rem;
  font-weight: 600;
  text-align: center;
}
.bildschau-zu {
  position: absolute;
  top: .6rem; right: .8rem;
  width: 40px; height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}
.bildschau-zu:hover { background: rgba(255, 255, 255, .22); }

/* ---------------------------- Kameras in der Liste ziehen (02.09.2026)
 * Ein eigener Griff, nicht die ganze Zeile: In der Zeile stehen Verweise,
 * und wer einen davon anklickt, will ihn öffnen und nicht ziehen. */
.kameraliste .griffspalte { width: 26px; padding-right: 0; }
.kameraliste .griff {
  display: block;
  color: var(--linie);
  font-size: 1rem;
  line-height: 1;
  cursor: grab;
  touch-action: none;            /* sonst blättert das Handy statt zu ziehen */
  user-select: none;
  padding: .2rem .1rem;
}
.kameraliste tr:hover .griff { color: var(--schrift-leise); }
.kameraliste tr.zieht {
  opacity: .55;
  background: color-mix(in srgb, var(--gelb) 12%, transparent);
}
.kameraliste tr.zieht .griff { cursor: grabbing; }
/* Kurz aufleuchten, wenn die neue Folge gesichert ist – eine Bewegung ohne
 * Rückmeldung lässt einen zweifeln, ob sie ankam. */
@keyframes reihegesichert { from { background: color-mix(in srgb, var(--gut) 22%, transparent); } }
.kameraliste tr.gesichert { animation: reihegesichert 1.1s ease-out; }

/* ------------------------------- Baustelle aufklappen (02.09.2026)
 * In der Listenansicht steht zuerst nur der Name; die Kameras kommen beim
 * Klick. Das Dreieck des Browsers wird ersetzt: Es sitzt je nach Browser
 * woanders und passt nicht zum Namensschild. */
.ortsblock > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.ortsblock > summary::-webkit-details-marker { display: none; }
/* Der Pfeil steht **vor** dem Schild, nicht darin: Im Schild säße er auf
 * weißem Grund und würde mit dem gelben Balken konkurrieren. */
.ortsblock > summary::before {
  content: "";
  flex: 0 0 auto;
  width: 8px; height: 8px;
  border-right: 2px solid var(--schrift-leise);
  border-bottom: 2px solid var(--schrift-leise);
  transform: rotate(-45deg);
  transition: transform .15s ease;
}
.ortsblock[open] > summary::before { transform: rotate(45deg); }
.ortsblock > summary:hover::before { border-color: var(--gelb); }
.ortsblock > summary:focus-visible { outline: 2px solid var(--gelb); outline-offset: 3px; border-radius: var(--rund); }
/* Luft zwischen Griff und Inhalt – **am Griff**, nicht an den Kindern:
 * Die Adresszeile trägt ihren Abstand als `style="margin:0"` im Element,
 * und ein solcher schlägt jede Regel aus dem Stylesheet. Sie klebte
 * dadurch am Namensschild (02.09.2026 bemängelt). */
.ortsblock[open] > summary { margin-bottom: .9rem; }

/* Die Beschriftung der Bildschau trägt jetzt auch den Lupenknopf – Name
 * links, Knopf rechts, damit der Name mittig bleibt, wo er allein steht. */
.bildschau figcaption {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.bildschau-lupe {
  color: #d8dac9;
  border: 1px solid rgba(255, 255, 255, .25);
  background: rgba(255, 255, 255, .08);
}
.bildschau-lupe:hover { background: rgba(255, 255, 255, .16); color: #fff; }
.bildschau-lupe.an {
  background: var(--gelb);
  border-color: var(--gelb);
  color: var(--auf-gelb);
}

/* Firmenkopf über einer Personenliste. Die Karte hat `padding: 0`, damit
 * die Tabelle bis an den Rand läuft – der Kopf holt sich seine Polsterung
 * deshalb selbst. */
.firmenkopf {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
  margin: 0;
  padding: .9rem 1.2rem;
  border-bottom: 1px solid var(--linie);
}

/* „Mein Passwort“ steht am rechten Rand der Unterleiste: Es ist das
 * einzige, was den eigenen Zugang betrifft und nicht die Kunden – und was
 * nicht dazugehört, stellt man nicht mitten hinein. */
/* `.rechts` in der Unterleiste ist seit dem 03.09.2026 unbenutzt:
 * „Mein Passwort" stand ganz rechts und rutschte damit weit weg von den
 * übrigen Punkten – gewünscht war direkt neben OneNote. Die Regel bleibt
 * stehen, falls wieder etwas nach rechts soll, und schadet nicht. */
.unterleiste a.rechts { margin-left: auto; }

/* Das kleine „weg“ hinter einem Eintrag der Liste „Eingezeichnet“. Leise,
 * bis man darauf zeigt – es soll nicht mit dem Wert konkurrieren, den es
 * betrifft. */
.stiftweg {
  border: 0;
  background: none;
  padding: 0 .25rem;
  margin-left: .4rem;
  font-size: .72rem;
  color: var(--schrift-leise);
  text-decoration: underline;
  cursor: pointer;
}
.stiftweg:hover { color: var(--schlecht); }

/* Zahlen, die weiterführen. Sie sehen aus wie die Merkzeichen daneben –
 * es sind dieselben – reagieren aber auf den Zeiger, und die gerade
 * gewählte trägt die Hausfarbe. */
.ueberblick a:hover { border-color: var(--gelb); color: var(--schrift); text-decoration: none; }
.ueberblick a.an {
  border-color: color-mix(in srgb, var(--gelb) 60%, var(--linie));
  background: color-mix(in srgb, var(--gelb) 12%, transparent);
  color: var(--gelb-schrift);
  font-weight: 600;
}
@media (max-width: 640px) {
  .ueberblick a {
    min-height: 44px; display: inline-flex; align-items: center;
    padding: .1rem .75rem; font-size: .82rem;
  }
}

/* ======================================================== Schaufenster
 *
 * Die Seite, die der Vertrieb einem möglichen Kunden schickt
 * (`vorlagen/schaufenster.html`). Sie erbt nicht vom Verwaltungsrahmen und
 * braucht deshalb eigene Maße – aber **dieselben Farben**: Alles kommt aus
 * den Variablen, damit Hell und Dunkel von selbst stimmen.
 *
 * Eigene Namen mit dem Vorsatz `schau`, keine vorhandenen weiterbenutzt.
 * `.kalender` hat einmal gezeigt, was passiert, wenn ein neuer Baustein
 * einen schon vergebenen Namen nimmt: Er erbt fremde Regeln und klappt
 * unsichtbar zusammen.
 */
.schau {
  max-width: 62rem;
  margin: 0 auto;
  padding: 1.5rem 1.1rem 4rem;
}
.schaukopf { padding: 1.5rem 0 .5rem; }
.schaukopf .marke {
  font-weight: 800; letter-spacing: .02em; font-size: 1.15rem;
  color: var(--schrift);
}
.schaukopf .marke span {
  font-weight: 400; color: var(--schrift-leise); margin-left: .4rem;
}
.schaukopf h1 {
  margin: 1.2rem 0 .6rem;
  font-size: clamp(1.6rem, 4.5vw, 2.4rem);
  line-height: 1.15; letter-spacing: -.02em;
}
.schaukopf .vorspann {
  margin: 0; max-width: 46rem;
  font-size: 1.02rem; line-height: 1.6; color: var(--schrift-leise);
}

.schautafel {
  margin: 2.6rem 0 0;
  padding-top: 1.8rem;
  border-top: 1px solid var(--linie);
}
.schautafel > h2 {
  display: flex; align-items: center; gap: .55rem;
  margin: 0 0 .5rem; font-size: 1.2rem;
}
.schautafel > h2 .sinnbild { color: var(--gelb-schrift); flex: none; }
.schautafel > p { margin: 0 0 1.1rem; max-width: 46rem; line-height: 1.6; }

/* Die Tafel, auf die es ankommt – die Verpixelung. Ein getönter Rahmen
 * statt einer zweiten Schriftgröße: Er hebt sie heraus, ohne den Lesefluss
 * zu unterbrechen. Getönt aus `--gelb`, nicht als zweiter Farbwert
 * hingeschrieben, damit sie in beiden Themen stimmt. */
.schauwichtig {
  border: 1px solid color-mix(in srgb, var(--gelb) 40%, var(--linie));
  border-radius: var(--rund);
  background: color-mix(in srgb, var(--gelb) 6%, transparent);
  padding: 1.5rem 1.4rem;
}
.schauliste { margin: 0 0 1rem; padding-left: 1.1rem; line-height: 1.65; }
.schauliste li { margin-bottom: .5rem; }

/* Bilder: **immer** gedeckelt. Sie liegen mit 1400 Punkten Breite vor und
 * liefen ohne das aus jedem Fenster unter 1400 heraus – am Handy ein
 * seitlich schiebbarer Bildschirm, der am Schreibtisch nie auffällt. */
.schau img, .schau video { max-width: 100%; height: auto; display: block; }

.schaukameras { display: flex; flex-direction: column; gap: 1.6rem; }
.schaukameras figure { margin: 0; }
.schaukameras img, .schautag img, .schaufilm {
  width: 100%; border-radius: var(--rund); background: var(--grund-tief);
}
.schaukameras figcaption, .schautag figcaption {
  margin-top: .5rem; font-size: .92rem; color: var(--schrift-leise);
}
.schaukameras figcaption strong, .schautag figcaption strong {
  color: var(--schrift);
}
.schautag { margin: 0; }
/* Feste Form, solange das Bild noch lädt: Ohne sie springt der ganze
 * Abschnitt, sobald die erste Aufnahme ankommt. */
.schautag img { aspect-ratio: 16 / 9; object-fit: cover; }

.schauregler {
  width: 100%; margin: 1rem 0 .2rem; accent-color: var(--gelb);
  /* Am Handy mit dem Daumen zu treffen. */
  height: 2rem;
}
.schauzeiten {
  display: flex; justify-content: space-between;
  font-size: .82rem; color: var(--schrift-leise);
  font-variant-numeric: tabular-nums;
}

.schaukacheln {
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.schaukacheln > div {
  border: 1px solid var(--linie); border-radius: var(--rund);
  padding: .9rem 1rem; background: var(--karte);
}
.schaukacheln strong { display: block; margin-bottom: .25rem; }
.schaukacheln span { font-size: .9rem; color: var(--schrift-leise); line-height: 1.5; }

.schaufuss {
  margin-top: 3.5rem; padding-top: 1.4rem;
  border-top: 1px solid var(--linie);
  font-size: .9rem; color: var(--schrift-leise);
}
.schaufuss p { margin: .2rem 0; }
.schaufuss a { color: var(--gelb-schrift); }

@media (max-width: 640px) {
  .schau { padding: 1rem .9rem 3rem; }
  .schautafel { margin-top: 2rem; padding-top: 1.4rem; }
  .schauwichtig { padding: 1.1rem 1rem; }
}

/* Der Demo-Link in der Verwaltung: ganz oder gar nicht. Eine abgeschnittene
 * Adresse ist keine Adresse – dieselbe Lehre wie bei der Kameraliste. */
.markefeld {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: 0;
}
.markefeld code {
  flex: 1; min-width: 0; overflow-wrap: anywhere;
  background: var(--karte); border: 1px solid var(--linie);
  border-radius: var(--rund); padding: .5rem .7rem; font-size: .85rem;
}
.befehl {
  background: var(--karte); border: 1px solid var(--linie);
  border-radius: var(--rund); padding: .6rem .8rem; margin: .4rem 0 0;
  font-size: .82rem; overflow-x: auto;
}

/* ---------------------------------------------------- Demo-Zugang: Band
 *
 * Über jeder Kundenseite, wenn das Demokonto angemeldet ist. Es klebt
 * bewusst **nicht** mit: Der Kopf tut das schon, und zwei klebende Streifen
 * fressen am Handy den halben Bildschirm (Lehre vom 02.09.2026 zur
 * Kopfleiste). Getönt aus `--gelb`, damit es in beiden Themen stimmt. */
.demoband {
  background: color-mix(in srgb, var(--gelb) 14%, var(--grund));
  border-bottom: 1px solid color-mix(in srgb, var(--gelb) 45%, var(--linie));
}
.demoinhalt {
  max-width: 80rem; margin: 0 auto; padding: .55rem 1.1rem;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  font-size: .9rem;
}
.demomarke {
  background: var(--gelb); color: var(--auf-gelb);
  font-weight: 700; font-size: .72rem; letter-spacing: .08em;
  text-transform: uppercase; padding: .15rem .5rem; border-radius: 999px;
}
.demoschritt {
  font-size: .78rem; color: var(--schrift-leise);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Der erklärende Satz darf schrumpfen, der Knopf nicht. Ohne `min-width: 0`
 * hält ein Flexkind an seiner Inhaltsbreite fest und schiebt die Zeile aus
 * dem Bild – dreimal an einem Abend gemessen. */
.demoinhalt .demotext { color: var(--schrift-leise); flex: 1 1 16rem; min-width: 0; }
.demoinhalt .demofertig { flex: 1 1 auto; }
.demoweiter { margin-left: auto; flex: none; }
/* Der Weg aus der Demo. Er stand hier nicht, und genau das war der Fehler:
 * Der Demo-Zugang überschreibt die Sitzung dessen, der den Link öffnet
 * (08.09.2026 gemeldet). Zurückhaltend gehalten — er ist der Ausgang, nicht
 * das Angebot —, aber er muss dastehen, ohne dass man ihn sucht. `auto` am
 * linken Rand schiebt ihn ans Ende, auch wenn davor kein Weiter-Knopf steht
 * (auf der letzten Station der Führung). */
.demoraus {
  margin-left: auto; flex: none; white-space: nowrap;
  background: transparent; color: inherit;
  border: 1px solid color-mix(in srgb, currentColor 40%, transparent);
}

@media (max-width: 640px) {
  .demoinhalt { padding: .5rem .9rem; gap: .4rem .55rem; }
  /* Am Handy zuerst die Anweisung, dann der Knopf über die volle Breite –
   * 44 Punkte hoch wie jeder andere. */
  .demoweiter, .demoraus { margin-left: 0; width: 100%; text-align: center;
                min-height: 44px; display: flex; align-items: center;
                justify-content: center; }
}

/* Suchfeld im Kopf der Kameraliste (03.09.2026).
 * Ein Feld, kein Knopf: Es filtert beim Tippen, es gibt nichts abzusenden. */
/* Das Suchfeld steht seit dem 09.09.2026 in einer eigenen Zeile
 * (`.adminleiste`) und darf dort breiter werden: 18 rem statt 14, und es
 * wächst mit, wenn Platz da ist. Neben der Überschrift musste es schmal
 * bleiben; jetzt ist die Zeile seine. */
.kamerasuche {
  display: inline-flex; align-items: center; gap: .45rem;
  border: 1px solid var(--linie); border-radius: var(--rund);
  padding: 0 .6rem; background: var(--karte);
  min-width: 0; flex: 0 1 18rem;
}
.kamerasuche:focus-within { border-color: var(--gelb); }
.kamerasuche .sinnbild { color: var(--schrift-leise); flex: none; }
.kamerasuche input {
  border: 0; background: none; color: var(--schrift);
  font: inherit; font-size: .88rem; padding: .42rem 0;
  min-width: 0; width: 100%; outline: none;
}
.suchstand { margin: -.4rem 0 1rem; }
.suchstand.warnt { color: var(--achtung); }
@media (max-width: 640px) {
  /* Am Handy zuerst suchen, dann anlegen: Das Feld nimmt die ganze Zeile,
     damit der Platzhalter lesbar bleibt (bei 375 Punkten sonst „Box od…"). */
  .kamerasuche { flex: 1 1 100%; order: -1; }
}

/* ------------------------------------------------------- Scharfschaltplan
 *
 * Der anklickbare Wochenplan der Einbruchmeldung (06.09.2026). Ein ZWEITES
 * Bauteil neben dem Anzeigeraster weiter oben in dieser Datei – jenes bleibt
 * unverändert das reine Anzeigebild (aria-hidden, eine Zeile hat ein Element statt
 * achtundvierzig). Hier ist jedes Kästchen eine echte Schaltfläche: Sie
 * lässt sich mit der Tastatur ansteuern, trägt ihren Zustand in
 * aria-pressed und sagt beim Vorlesen, welche halbe Stunde sie meint.
 *
 * Gelb heißt scharf – hier ausnahmsweise als Zustand, weil es dieselbe
 * Fläche ist, die der Betreiber in der Oberfläche der Kamera anklickt und
 * die das Anzeigeraster darüber schon gelb zeigt. Wären es zwei Farben,
 * sähen zwei Bilder desselben Plans verschieden aus. */
.planbereich { margin: .6rem 0 .2rem; }
/* Im schmalen Fenster rollt das Raster waagerecht – die Seite nicht.
 * Achtundvierzig Kästchen brauchen Platz, und ein Kästchen unter drei
 * Punkten Breite trifft niemand mehr.
 *
 * `planrolle-plan` und nicht `planrolle`: Diesen Namen gibt es weiter oben
 * schon (Zeile 4493, der Jahresplan der Boxenseite). Zwei Regeln desselben
 * Namens haben einander überschrieben – der Jahresplan verlor sein
 * senkrechtes Rollen und behielt nur die Höhengrenze, der Scharfschaltplan
 * erbte umgekehrt eine Höhengrenze, die hier niemand wollte (06.09.2026). */
.planrolle-plan { overflow-x: auto; overflow-y: hidden; padding-bottom: .2rem; }
.planraster { min-width: 34rem; }   /* 27 + 7rem breitere Tagesspalte */
.planraster .skala {
  /* 8.9rem = Tagesspalte 8.5rem + gap .4rem; 5.2rem = .tagknoepfe 4.8rem
   * + gap .4rem. Die Achse deckt sich damit genau mit dem Kästchenraster. */
  position: relative; height: 1rem; margin-left: 8.9rem; margin-right: 5.2rem;
  font-size: .68rem; color: var(--schrift-leise);
}
.planraster .skala b { position: absolute; font-weight: 400; transform: translateX(-50%); }
.planraster .skala b:first-child { transform: none; }
.planraster .skala b:last-child { transform: translateX(-100%); }
.planraster .zeile { display: flex; align-items: center; gap: .4rem; margin-bottom: 3px; }
/* Die Tagesspalte trägt seit dem 10.09.2026 zwei Zeilen: das Kürzel und
 * darunter die Zeiten dieses Tages. Vorher standen die Zeiten in sieben
 * Sätzen UNTER dem Raster („Mo 00:00-24:00 scharf"), und das sah aus wie
 * eine Fußnotenliste. Jetzt steht die Auskunft dort, wo man ohnehin
 * hinsieht: neben der Zeile, zu der sie gehört.
 *
 * `width` und die `margin-left` der Stundenachse gehören zusammen (siehe
 * `.planraster .skala`): Die Achse beginnt genau dort, wo das Kästchenraster
 * beginnt. Wer eine der beiden Zahlen ändert, muss die andere mitziehen,
 * sonst stehen die Stundenzahlen nicht mehr über ihren Kästchen. */
.planraster .tag {
  width: 8.5rem; flex: none; font-size: .72rem; color: var(--schrift-leise);
  display: flex; flex-direction: column; gap: .1rem; line-height: 1.25;
}
.planraster .tag b {
  font-weight: 400; font-size: .68rem;
  font-variant-numeric: tabular-nums;
}
.planraster .tag b.warnt { color: var(--achtung); font-weight: 600; }
/* `touch-action: pan-y` auf den Kästchen: Ein waagerechter Wisch malt, ein
 * senkrechter blättert die Seite weiter. Zuerst stand hier `none` (Vorbild:
 * verpixelung_foto.html) – dort ist Malen der einzige Zweck der Seite, hier
 * steckt das Malfeld mitten in einer langen Seite zum Blättern, und ein
 * senkrechter Wisch liess sie einfrieren und malte quer durch fünf
 * Wochentage (06.09.2026). Waagerecht gerollt wird über den Rand daneben. */
.planraster .kaesten {
  display: grid; grid-template-columns: repeat(48, 1fr);
  flex: 1 1 auto; min-width: 0; touch-action: pan-y;
  border: 1px solid var(--linie); border-radius: 3px; overflow: hidden;
  background: var(--karte-hell);
}
.planraster .kasten {
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; border-left: 1px solid var(--linie-leise);
  border-radius: 0; box-shadow: none; padding: 0; margin: 0;
  height: 20px; min-height: 0; min-width: 0; width: auto;
  font-size: 0; line-height: 0; color: transparent; white-space: normal;
  transition: background-color .08s linear;
}
.planraster .kasten:first-child { border-left: 0; }
/* Stundenlinien alle zwei Kästchen – so bleibt die halbe Stunde erkennbar
 * und die Stunde zählbar. Alle zwölf (sechs Stunden) eine kräftigere. */
.planraster .kasten:nth-child(2n + 1) { border-left-color: var(--linie); }
/* Alle vier Stunden etwas deutlicher – dort steht am Handy noch eine Zahl
 * über dem Raster, und dann soll man sie auch wiederfinden. Die Reihenfolge
 * zählt: gleiche Gewichtung, die spätere Regel gewinnt, also muss diese
 * VOR der kräftigen Sechs-Stunden-Linie stehen. */
.planraster .kasten:nth-child(8n + 1) { border-left-color: var(--linie-leise); }
.planraster .kasten:nth-child(12n + 1) { border-left-color: var(--schrift-leise); }
.planraster .kasten[aria-pressed="true"] { background: var(--gelb); }
.planraster .kasten:hover { filter: none; background: var(--linie); }
.planraster .kasten[aria-pressed="true"]:hover { background: var(--gelb); filter: brightness(1.08); }
.planraster .kasten:active:not(:disabled) { transform: none; }
.planraster .kasten:focus-visible {
  outline: 2px solid var(--schrift); outline-offset: -2px; position: relative; z-index: 1;
}
.planraster .kasten:disabled { opacity: 1; cursor: default; }
/* Nur Anzeige: Diese Kamera führt Zeiten, die nicht auf halbe Stunden
 * fallen. Dann ist das Raster blass und nimmt keine Klicks an. */
.planraster.nuranzeige .kaesten { opacity: .55; }
/* Ohne die Knöpfe „ganz"/„frei" (sie fehlen im Nur-Anzeige-Fall ganz) braucht
 * die Stundenskala den Platz rechts nicht mehr freizuhalten. */
.planraster.nuranzeige .skala { margin-right: 0; }
.planraster .tagknoepfe { flex: none; display: flex; gap: .1rem; width: 4.8rem; }
.planraster .tagknoepfe button { padding: .15rem .35rem; font-size: .7rem; }
/* Der mitschreibende Satz je Tag: „Sa 00:00-05:30, 17:00-24:00 – 2 Blöcke".
 * Er ist die einzige Auskunft für alle, die das Raster nicht sehen können,
 * und deshalb `aria-live` – er meldet sich, sobald sich der Tag ändert. */
/* Seit dem 10.09.2026 nur noch fürs Vorlesen da, nicht mehr im Bild.
 *
 * Der Betreiber wollte die sieben „Mo, Di, Mi …"-Sätze unter dem Diagramm
 * weghaben. Löschen wäre falsch gewesen: Sie sind die EINZIGE Auskunft für
 * alle, die das Raster nicht sehen können – die Stundenachse ist
 * `aria-hidden`, das Anzeigeraster ganz. Dieselbe Angabe steht jetzt
 * sichtbar in der Tagesspalte (`.planraster .tag b`); hier bleibt sie für
 * die Vorlesehilfe stehen, samt `aria-live`, das sich bei jeder Änderung
 * meldet. Die Klammer aus 1px, overflow und clip-path ist der übliche Weg:
 * `display: none` nähme sie auch der Vorlesehilfe weg.
 *
 * Was sichtbar bleiben MUSS, steht ohnehin woanders: Die Sperrmeldung bei
 * zu vielen Blöcken sitzt in `#planwarnung` über dem Raster, nicht hier. */
.planzeilen {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.planzeilen p { margin: 0 0 .15rem; font-size: .76rem; color: var(--schrift-leise); }
.planzeilen p.warnt { color: var(--achtung); font-weight: 600; }
.planzeilen p b { color: var(--schrift); font-weight: 600; }

/* Der Wortlaut der Störungsmail auf der Kameraseite (10.09.2026) – zum
 * Lesen, nicht zum Bearbeiten. Eigener Grund und eigener Rahmen, damit
 * niemand ihn für eine Beschreibung der Seite hält: Das ist der Text, der
 * einem Kunden zuginge.
 *
 * `white-space: pre-line` und nicht `<pre>`: Der Text trägt echte
 * Absätze, aber keine feste Zeilenbreite – `<pre>` bräche nicht um und
 * ragte aus der Karte heraus. */
.stoerprobe {
  background: var(--karte-hell);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .8rem .95rem;
  margin-top: .4rem;
}
.stoertext {
  white-space: pre-line;
  margin: 0;
  font-size: .88rem;
  line-height: 1.55;
}

/* Der Schalter, der alles andere aushebelt, steht seit dem 09.09.2026 ganz
 * oben in der Karte – und ist ein Schalter, kein Knopf mit Textfeld
 * daneben. Abgesetzt bleibt er trotzdem: eigener Rahmen, und wenn die
 * Meldung aus ist, ein roter.
 *
 * Die alte Klasse `.einbruchschalter` (Block unten, mit Abtippfeld) ist
 * weg; sie wurde nirgendwo sonst benutzt. */
.meldeschalter {
  display: flex;
  align-items: flex-start;
  gap: .9rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  padding: .8rem 1rem;
  margin: .2rem 0 1.1rem;
}
.meldeschalter.aus {
  border-color: var(--schlecht);
  background: color-mix(in srgb, var(--schlecht) 7%, transparent);
}
.meldeschalter .schalter { flex: none; margin-top: .1rem; }
.meldeschaltertext { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.meldeschaltertext strong { font-size: .95rem; }
.meldungaus {
  display: inline-block; color: var(--schlecht); font-weight: 700;
  font-size: 1rem; letter-spacing: .02em;
}
/* Gegenlicht: Schieber und Zahl in einer Zeile, die Zahl bleibt stehen. */
.schieberzeile { display: flex; align-items: center; gap: .7rem; max-width: 24rem; }
.schieberzeile input[type="range"] { flex: 1 1 auto; min-width: 0; }
.schieberzeile .wert {
  flex: none; width: 2.6rem; text-align: right;
  font-family: var(--zahlschrift); font-size: .85rem;
}
/* Gewollt gegen tatsächlich, wenn die Kamera etwas anderes zurückmeldet. */
.abweichliste { margin: .4rem 0 0; padding-left: 1.1rem; font-size: .82rem; }
.staendeliste { list-style: none; margin: .4rem 0 0; padding: 0; }
.staendeliste li {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .35rem 0; border-top: 1px solid var(--linie-leise); font-size: .82rem;
}
.staendeliste li:first-child { border-top: 0; }

@media (max-width: 640px) {
  /* Am Handy ist der Bildschirm schmaler als das Raster; es rollt dann
   * waagerecht in seinem eigenen Kasten, die Seite nicht. Lieber etwas mehr
   * Rollweg als Kästchen von fünf Punkten: Bei 375 Punkten Fensterbreite
   * kommt eine halbe Stunde sonst auf 5,6 Punkte, und die trifft niemand.
   * Höher werden sie auch – gezogen wird quer, angetippt senkrecht. */
  .planraster { min-width: 37rem; }   /* 30 + 7rem breitere Tagesspalte */
  /* Alle zwei Stunden eine Zahl ist am Handy zu eng: 15 Punkte je Stunde,
   * eine zweistellige Zahl misst zwölf. Jede zweite weg, dann steht dort
   * 0, 4, 8, 12, 16, 20, 24. */
  .planraster .skala b:nth-of-type(2n) { display: none; }
  .planraster .kasten { height: 28px; }
}

/* ====================================================== Vertrieb
 *
 * Die Kundengewinnung, übernommen aus der Zulieferung „TRIOBILD Akquise".
 * Dort brachte sie ihr eigenes Stylesheet mit — 66 KB und ein Lindgrün als
 * Akzent. Beides ist hier weg: Die Farben kommen aus den Werten oben, und
 * damit stimmt auch das Marken-Gelb wieder. Ein zweites Grün neben dem
 * Gelb hätte aus einem Portal zwei gemacht.
 *
 * Alle Klassen tragen ein `v` am Anfang. Nicht aus Sparsamkeit, sondern
 * damit beim nächsten Bauabschnitt niemand raten muss, was zum Vertrieb
 * gehört und was allen Seiten.
 */

/* ---- Priorität und Stufe als Merker ---------------------------------- */
.vprio {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 1.4rem; height: 1.4rem; border-radius: 50%;
  font-size: .72rem; font-weight: 700; letter-spacing: 0;
  border: 1px solid var(--linie);
  color: var(--schrift-leise);
}
/* A ist das Marken-Gelb, B und C treten zurück. Drei Farben für drei
 * Prioritäten wären drei Meldungen — es ist aber nur eine Rangfolge. */
.v-prio-a { background: var(--gelb); color: var(--auf-gelb); border-color: var(--gelb); }
.v-prio-b { background: var(--karte-hell); }
.v-prio-c { background: transparent; opacity: .7; }

.vstufe, .vmerker {
  display: inline-block; padding: .16rem .5rem; border-radius: 999px;
  font-size: .74rem; line-height: 1.5; white-space: nowrap;
  background: var(--karte-hell); border: 1px solid var(--linie);
  color: var(--schrift-leise);
}
.vmerker.gut { color: var(--gut); border-color: color-mix(in srgb, var(--gut) 40%, transparent); }
.vmerker.schlecht { color: var(--schlecht); border-color: color-mix(in srgb, var(--schlecht) 40%, transparent); }
.vmerker.achtung { color: var(--achtung); border-color: color-mix(in srgb, var(--achtung) 40%, transparent); }
.v-gewonnen { color: var(--gut); border-color: color-mix(in srgb, var(--gut) 40%, transparent); }
.v-verloren { color: var(--schlecht); border-color: color-mix(in srgb, var(--schlecht) 40%, transparent); }
.v-entscheidung, .v-angebot { color: var(--gelb-schrift); border-color: color-mix(in srgb, var(--gelb) 35%, transparent); }

/* ---- Die Firmenliste in „Heute" -------------------------------------- */
.vliste { display: flex; flex-direction: column; }
.vzeile {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem 1.1rem; border-top: 1px solid var(--linie-leise);
  color: inherit; text-decoration: none;
}
.vzeile:first-child { border-top: 0; }
.vzeile:hover { background: var(--karte-hell); }
.vname { display: flex; flex-direction: column; gap: .15rem; min-width: 0; flex: 1 1 auto; }
.vname strong { font-size: .95rem; }
.vschritt { flex: 0 0 auto; text-align: right; display: flex; flex-direction: column; gap: .15rem; }

/* ---- Die Pipeline-Tafel ---------------------------------------------- */
/* Waagerecht rollend, nicht umbrechend: Sechs Spalten, die auf einem
 * schmalen Bildschirm untereinander stehen, sind keine Tafel mehr, sondern
 * sechs Listen — und der Vergleich zwischen den Stufen ist genau der Zweck. */
.vtafel {
  display: flex; gap: 1rem; overflow-x: auto;
  padding-bottom: .8rem; margin-bottom: 1.5rem;
  scrollbar-width: thin;
}
.vspalte {
  flex: 0 0 15.5rem; display: flex; flex-direction: column;
  background: var(--kartengrund); border: 1px solid var(--linie);
  border-radius: var(--rund-gross); overflow: hidden;
}
.vspalte > header {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  padding: .8rem .9rem .6rem; border-bottom: 1px solid var(--linie-leise);
}
.vspalte > header strong { font-size: .9rem; }
.vhinweis { flex: 1 0 100%; }
.vspalte.nimmt { border-color: var(--gelb); }
.vspaltenkorb {
  display: flex; flex-direction: column; gap: .5rem;
  padding: .6rem; min-height: 4rem; flex: 1 1 auto;
}
.vkarte {
  display: flex; flex-direction: column; gap: .25rem;
  padding: .6rem .7rem; background: var(--karte);
  border: 1px solid var(--linie); border-radius: var(--rund);
  cursor: grab;
}
.vkarte:hover { border-color: color-mix(in srgb, var(--gelb) 50%, var(--linie)); }
.vkarte.zieht { opacity: .4; cursor: grabbing; }
.vkarte > a {
  display: flex; align-items: center; gap: .45rem;
  color: inherit; text-decoration: none; font-weight: 600; font-size: .88rem;
}

/* ---- Die Firmenseite -------------------------------------------------- */
.vspalten2 { display: grid; grid-template-columns: 1.6fr 1fr; gap: 1.5rem; align-items: start; }
@media (max-width: 900px) { .vspalten2 { grid-template-columns: 1fr; } }

.vabschnitt, .vnaechster { padding: 1.1rem; }
.vnaechster p { margin: 0; }
.vunter {
  margin: 1.1rem 1.1rem .3rem; font-size: .82rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--schrift-leise);
}
.vabschnitt .vunter, form .vunter { margin-left: 0; margin-right: 0; }
.vpunkte { margin: .2rem 1.1rem 1rem; padding-left: 1.1rem; font-size: .85rem; }
.vpunkte li { margin-bottom: .4rem; }
.vschlagworte { display: flex; flex-wrap: wrap; gap: .3rem; margin: .8rem 0; }

.vkontakte { list-style: none; margin: 0; padding: 0; }
.vkontakte li {
  display: flex; flex-direction: column; gap: .15rem;
  padding: .7rem 1.1rem; border-top: 1px solid var(--linie-leise);
}
.vkontakte li:first-child { border-top: 0; }

/* Der Verlauf. Neueste oben, und keine Möglichkeit, ihn zu ändern. */
.vverlauf { list-style: none; margin: 0; padding: 0; }
.vverlauf li {
  display: grid; grid-template-columns: 6.5rem 1fr auto; gap: .7rem;
  align-items: baseline;
  padding: .6rem 1.1rem; border-top: 1px solid var(--linie-leise);
  font-size: .88rem;
}
.vverlauf li:first-child { border-top: 0; }
.vart { color: var(--schrift-leise); font-size: .8rem; }
.vtext { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .vverlauf li { grid-template-columns: 1fr; gap: .2rem; }
}

/* ---- Formulare -------------------------------------------------------- */
.vfeldreihe {
  display: flex; flex-wrap: wrap; gap: .8rem; margin-bottom: .8rem;
  align-items: flex-end;
}
.vfeldreihe label { display: flex; flex-direction: column; gap: .25rem; font-size: .82rem; }
.vfeldreihe label.weit { flex: 1 1 14rem; min-width: 0; }
.vfeldreihe input, .vfeldreihe select { min-width: 0; }

.vsuchform, .vneuform, .vnotizform { padding: 1.1rem; }
.vsuchform { display: flex; flex-wrap: wrap; gap: .8rem; align-items: flex-end; }
.vsuchform label { display: flex; flex-direction: column; gap: .25rem; font-size: .82rem; }
.vsuchform label.weit { flex: 1 1 18rem; }
.vnotizform { display: flex; gap: .6rem; border-top: 1px solid var(--linie-leise); }
.vnotizform input { flex: 1 1 auto; min-width: 0; }

/* „Schritt erledigt" ist zugeklappt, solange ein Termin steht, und offen,
 * wenn keiner steht — dort ist es das, was die Seite von einem will. */
.vschrittform { border-top: 1px solid var(--linie-leise); }
.vschrittform > summary {
  padding: .8rem 1.1rem; cursor: pointer; font-weight: 600; font-size: .88rem;
  display: flex; align-items: center; gap: .5rem;
}
.vschrittform > summary::-webkit-details-marker { display: none; }
.vschrittform > form { padding: 0 1.1rem 1.1rem; }

.vbearbeiten > summary { cursor: pointer; list-style: none; }
.vbearbeiten > summary::-webkit-details-marker { display: none; }
.vbearbeiten > form { padding: 1.1rem; }
.vbearbeiten label.weit { display: flex; flex-direction: column; gap: .25rem;
  font-size: .82rem; margin-bottom: .8rem; }
.vbearbeiten textarea { width: 100%; resize: vertical; }
.vkontaktfelder { margin-bottom: 1rem; }
.vgefahr {
  padding: 1.1rem; border-top: 1px solid var(--linie-leise);
  display: flex; align-items: center; gap: .8rem;
}
.vgefahr form { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }

/* ==================================================== Überwachungsbereiche
 *
 * Die Erkennungszonen der Einbruchmeldung, gezeichnet auf das Standbild der
 * Kamera (06.09.2026). Sie stehen hier ganz unten und nicht bei den übrigen
 * Bauteilen, weil in stil.css nur angehängt und nie mitten hineingeschrieben
 * wird — zwei Regeln desselben Namens haben sich hier schon einmal
 * gegenseitig überschrieben (siehe `planrolle-plan`).
 *
 * SVG für die Geometrie, echte <button> für die Ecken: Das Haus hat genau ein
 * Vorbild für eine bedienbare Fläche (das Planraster), und dort ist jedes
 * bewegliche Ding eine Schaltfläche mit aria-pressed/aria-label — Fokus,
 * :focus-visible und Vorlesbarkeit sind damit umsonst da. Ein
 * Leinwand-Kontext (canvas) täte das nicht und nähme auch keine
 * `var(--…)`-Farben; in verpixelung_foto.html stehen deshalb feste
 * rgba-Werte, die dem hellen Thema nicht folgen.
 */

/* Vier Bereichsfarben. Sie stehen hier unten und nicht oben bei den übrigen
 * Werten, weil in stil.css nur angehängt wird (06.09.2026). `--schlecht`
 * wäre für einen Bereich die falsche Bedeutung — Rot heisst auf allen Seiten
 * „hier stimmt etwas nicht", ein Überwachungsbereich ist aber kein Fehler.
 * Farbe ist nie das einzige Merkmal: Jeder Umriss trägt seine Nummer, und der
 * gewählte ist durchgezogen statt gestrichelt. Ob die vier nebeneinander gut
 * zu unterscheiden sind, sieht sich der Betreiber an.
 *
 * EIN Satz Werte für beide Themen, und das ist hier die Ausnahme mit Grund:
 * Diese vier Farben liegen ausschliesslich auf dem Kamerabild, und dessen
 * Fläche ist `--grund-tief` — die bleibt im hellen Thema ausdrücklich
 * schwarz (siehe den Wert ganz oben und `.vorschaurahmen`). Ein zweiter,
 * dunkler Satz für „hell" war zuerst hier und wurde am 06.09.2026 im
 * Browser nachgesehen: Er verschwand auf der dunklen Bühne fast völlig.
 * Auf hellem Grund steht von diesen Farben nur der 3-Punkt-Strich am
 * Auswahlknopf — genauso, wie `.hinweis` dort schon eine gelbe Kante
 * trägt. */
:root {
  --bereich-1: var(--gelb);
  --bereich-2: #4aa3ff;
  --bereich-3: #b98cff;
  --bereich-4: #38d2c0;
}

.bereichsblock { margin: .6rem 0 .2rem; }

/* Keine feste Höhe: Die Kameras liefern 16:9, 4:3 und — bei den
 * Doppelsensor-Geräten — 32:9. Die Bühne ist genau so hoch wie ihr Bild,
 * dasselbe Vorgehen wie beim `.vorschaurahmen`. Das SVG liegt mit
 * preserveAspectRatio="none" deckungsgleich darauf, in jeder Fenstergrösse
 * und bei jedem Seitenverhältnis, ohne eine einzige Umrechnung.
 *
 * `touch-action: pan-y` und NICHT `none`: Die Auskunftsseite ist lang und
 * wird am Handy geblättert. Eine Bühne, die den halben Bildschirm füllt und
 * das Blättern einfriert, macht die Seite unbenutzbar — dieselbe Lehre wie
 * beim Planraster. `none` steht deshalb nur auf den Griffen: Wer eine Ecke
 * fasst, hat sie schon getroffen. */
.bereichsbuehne {
  position: relative;
  line-height: 0;             /* sonst steht unter dem Bild ein Zeilenrest */
  background: var(--grund-tief);
  touch-action: pan-y;
  border-radius: var(--rund);
  overflow: hidden;
  margin: .6rem 0 0;
}
.bereichsgrund { width: 100%; height: auto; display: block; }
/* Fällt das Standbild aus, wird auf einem Raster gezeichnet statt gar nicht:
 * Die Zahlen stimmen weiter, nur der Untergrund fehlt. Ohne die Seitenhöhe
 * hier fiele die Bühne auf null zusammen und nichts wäre mehr zu greifen. */
.bereichsbuehne.ohnebild { aspect-ratio: 16 / 9; }
.bereichsbuehne.ohnebild .bereichsgrund { display: none; }
.bereichsbuehne.ohnebild::before {
  content: ""; position: absolute; inset: 0;
  background-image:
    repeating-linear-gradient(0deg, var(--linie) 0 1px, transparent 1px 10%),
    repeating-linear-gradient(90deg, var(--linie) 0 1px, transparent 1px 10%);
  opacity: .5;
}
.bereichsnetz {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.bereichsgriffe { position: absolute; inset: 0; pointer-events: none; }

/* Der Umriss. `vector-effect="non-scaling-stroke"` steht als Merkmal am
 * Element selbst — ohne das verzerrt preserveAspectRatio="none" bei einem
 * 16:9-Bild auch die Strichbreite. */
.bereichsumriss {
  fill: var(--bereich-1); fill-opacity: .1;
  stroke: var(--bereich-1); stroke-width: 1.5; stroke-dasharray: 6 4;
}
.bereichsumriss.gewaehlt {
  fill-opacity: .2; stroke-width: 2.5; stroke-dasharray: none;
}
.bereichsumriss[data-nummer="2"] { fill: var(--bereich-2); stroke: var(--bereich-2); }
.bereichsumriss[data-nummer="3"] { fill: var(--bereich-3); stroke: var(--bereich-3); }
.bereichsumriss[data-nummer="4"] { fill: var(--bereich-4); stroke: var(--bereich-4); }

/* Griffe und Nummern liegen als HTML ÜBER dem SVG und nicht darin: So
 * bleiben sie rund und lesbar, während preserveAspectRatio="none" das Netz
 * darunter streckt. */
.bereichsgriff {
  position: absolute; transform: translate(-50%, -50%);
  /* 44 Punkte ist die Fingergrösse; der sichtbare Punkt darin ist klein. */
  width: 44px; height: 44px;
  pointer-events: auto; touch-action: none;
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; box-shadow: none; padding: 0; margin: 0;
  min-width: 0; min-height: 0; border-radius: 50%; cursor: move;
  font-size: 0; line-height: 0; color: transparent;
}
.bereichsgriff::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%;
  background: var(--bereichsfarbe, var(--gelb));
  border: 2px solid var(--grund-tief);
}
.bereichsgriff:hover { filter: none; background: none; }
.bereichsgriff:hover::after { transform: scale(1.25); }
.bereichsgriff:active:not(:disabled) { transform: translate(-50%, -50%); }
.bereichsgriff:focus-visible {
  outline: 2px solid var(--schrift); outline-offset: 2px;
}
.bereichsnummer {
  position: absolute; transform: translate(-50%, -50%);
  pointer-events: none; font-size: .8rem; font-weight: 700; line-height: 1;
  color: var(--bereich-1);
  text-shadow: 0 0 3px var(--grund-tief), 0 0 3px var(--grund-tief),
               0 0 3px var(--grund-tief);
}
.bereichsnummer[data-nummer="2"] { color: var(--bereich-2); }
.bereichsnummer[data-nummer="3"] { color: var(--bereich-3); }
.bereichsnummer[data-nummer="4"] { color: var(--bereich-4); }

/* Die Auswahl: genau einer der vier ist gewählt. Ein farbiger Strich links
 * sagt, welche Farbe dieser Bereich im Bild trägt — sonst müsste man raten,
 * welcher Umriss zu welchem Knopf gehört. */
.bereichswahl { display: flex; flex-wrap: wrap; gap: .4rem; margin: .6rem 0 0; }
.bereichsknopf {
  display: flex; flex-direction: column; align-items: flex-start; gap: .1rem;
  padding: .35rem .7rem; font-size: .82rem; text-align: left;
  background: var(--karte-hell); color: var(--schrift);
  border: 1px solid var(--linie); border-left: 3px solid var(--bereich-1);
  border-radius: var(--rund);
}
.bereichsknopf[data-nummer="2"] { border-left-color: var(--bereich-2); }
.bereichsknopf[data-nummer="3"] { border-left-color: var(--bereich-3); }
.bereichsknopf[data-nummer="4"] { border-left-color: var(--bereich-4); }
.bereichsknopf[aria-pressed="true"] {
  border-color: var(--schrift-leise); border-left-width: 3px;
  background: var(--karte); box-shadow: inset 0 0 0 1px var(--linie);
}
.bereichsknopf[aria-pressed="true"] { font-weight: 600; }
.bereichsknoepfe { gap: .4rem; margin: .6rem 0 0; align-items: center; }

.bereichswerte { margin: .8rem 0 0; }
.bereichsziele { gap: .4rem 1rem; }

/* Je Bereich ein mitschreibender Satz — für alle, die das Bild nicht sehen
 * können, ist er die einzige Auskunft. Vorbild `.planzeilen`. */
.bereichszeilen { margin: .5rem 0 0; }
.bereichszeilen p { margin: 0 0 .15rem; font-size: .76rem; color: var(--schrift-leise); }
.bereichszeilen p.gewaehlt { color: var(--schrift); }
.bereichszeilen p.warnt { color: var(--achtung); font-weight: 600; }

@media (max-width: 640px) {
  /* Die Griffe bleiben 44 Punkte — kleiner trifft am Handy niemand. Die
   * Knopfreihe bricht statt dessen um. Keine feste Höhe für die Bühne: Sie
   * ist so hoch wie das Bild, das die Kamera liefert. */
  .bereichsknoepfe, .bereichswahl { gap: .35rem; }
  .bereichsknopf { flex: 1 1 8rem; }
}

/* ------------------------------ Vertrieb: Ausschreibungen und Nachweise */

/* Die Fahne links in einer Zeile: ein Zeichen, das den Zustand vorwegnimmt.
 * Nicht als Ersatz für den Text, sondern als Anker fürs Auge — wer die
 * Liste überfliegt, sucht die ✕ und die ▲. */
.vfahne {
  flex: none; width: 1.1rem; text-align: center;
  font-size: .8rem; line-height: 1;
}
.vfahne.schlecht { color: var(--schlecht); }
.vfahne.achtung { color: var(--achtung); }
.vfahne.gut { color: var(--gut); }
.vfahne.leise { color: var(--schrift-leise); }

/* Der Punktestand des Kurzchecks hinter dem Titel. Klein und blass: eine
 * Einschätzung, keine Meldung. */
.vpunkte {
  margin-left: .4rem; padding: .05rem .35rem; border-radius: 999px;
  font-size: .7rem; font-weight: 500;
  background: var(--karte-hell); color: var(--schrift-leise);
  border: 1px solid var(--linie);
}
.vpflicht {
  margin-left: .4rem; padding: .05rem .35rem; border-radius: 999px;
  font-size: .65rem; text-transform: uppercase; letter-spacing: .05em;
  background: var(--karte-hell); color: var(--schrift-leise);
  border: 1px solid var(--linie);
}

/* Stufen der Ausschreibung. Dieselbe Formensprache wie bei den Firmen. */
.v-aus-gewonnen { color: var(--gut); border-color: color-mix(in srgb, var(--gut) 40%, transparent); }
.v-aus-verloren { color: var(--schlecht); border-color: color-mix(in srgb, var(--schlecht) 40%, transparent); }
.v-aus-beworben, .v-aus-nachgefasst { color: var(--gelb-schrift); border-color: color-mix(in srgb, var(--gelb) 35%, transparent); }
.v-aus-verworfen { border-style: dashed; opacity: .75; }

/* ---- Die fünf Fristen ------------------------------------------------
 * Die Angebotsfrist groß und allein, die anderen vier als Kacheln. Nicht
 * weil sie wichtiger wäre, sondern weil alles andere an ihr hängt. */
.vfristkopf {
  display: flex; flex-direction: column; gap: .2rem;
  padding: 1.1rem; border-bottom: 1px solid var(--linie-leise);
  border-left: 3px solid var(--linie);
}
.vfristkopf strong { font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.vfristkopf.schlecht { border-left-color: var(--schlecht); }
.vfristkopf.schlecht strong, .vfristkopf.schlecht .klein:not(.leise) { color: var(--schlecht); }
.vfristkopf.achtung { border-left-color: var(--achtung); }
.vfristkopf.achtung strong { color: var(--achtung); }

.vfristen {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: .6rem; padding: 1.1rem;
}
.vfrist {
  display: flex; flex-direction: column; gap: .15rem;
  padding: .6rem .7rem; border: 1px solid var(--linie);
  border-radius: var(--rund); background: var(--karte);
}
.vfrist strong { font-variant-numeric: tabular-nums; font-size: .95rem; }
.vfrist.jetzt { border-color: color-mix(in srgb, var(--schlecht) 55%, var(--linie)); }
.vfrist.nah { border-color: color-mix(in srgb, var(--achtung) 55%, var(--linie)); }

/* ---- Der Kurzcheck --------------------------------------------------- */
.vfrage {
  padding: .8rem 0; border-top: 1px solid var(--linie-leise);
}
.vfrage:first-of-type { border-top: 0; }
.vfragekopf { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.vfragekopf strong { font-size: .9rem; }
/* Die Frage, die sich selbst beantwortet, sieht anders aus als die, die
 * jemand beantworten muss — sonst sucht man das Bedienelement. */
.vfrage.automatisch {
  background: var(--karte-hell); border-radius: var(--rund);
  padding: .8rem; margin: .4rem 0;
}
.vantworten { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .45rem; }
.vantworten label {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .3rem .7rem; border: 1px solid var(--linie);
  border-radius: 999px; font-size: .82rem; cursor: pointer;
}
.vantworten label:hover { border-color: var(--linie-leise); background: var(--karte-hell); }
.vantworten input { margin: 0; }
.vantworten input:checked + span { font-weight: 600; }
.vantworten label:has(input:checked) {
  border-color: var(--gelb); background: color-mix(in srgb, var(--gelb) 12%, transparent);
}

.vhaken {
  display: flex; align-items: flex-start; gap: .5rem;
  margin: .2rem 0 1rem; cursor: pointer;
}
.vhaken input { margin-top: .2rem; flex: none; }

/* ---- Auswertung: woran es gescheitert ist ---------------------------- */
.vgruende { display: flex; flex-direction: column; gap: .45rem; margin: .5rem 0 1rem; }
.vgrund { display: grid; grid-template-columns: 14rem 1fr 2rem; gap: .7rem; align-items: center; }
.vbalken {
  display: block; height: .55rem; background: var(--karte-hell);
  border-radius: 999px; overflow: hidden;
}
.vbalken i { display: block; height: 100%; background: var(--gelb); border-radius: 999px; }
.vurteil {
  margin: 0; padding: .8rem 1rem; font-size: .9rem;
  border-left: 3px solid var(--gelb); background: var(--karte-hell);
  border-radius: 0 var(--rund) var(--rund) 0;
}
@media (max-width: 640px) {
  .vgrund { grid-template-columns: 1fr 2rem; }
  .vgrund .vbalken { grid-column: 1 / -1; }
}

/* ---- Eckdaten als Beschreibungsliste --------------------------------- */
.vdaten { display: grid; grid-template-columns: auto 1fr; gap: .35rem .9rem; margin: 0 0 .8rem; }
.vdaten dt { color: var(--schrift-leise); font-size: .8rem; }
.vdaten dd { margin: 0; font-size: .85rem; overflow-wrap: anywhere; }

/* ---- Die Nachweismappe ------------------------------------------------
 * Jeder Nachweis eine aufklappbare Zeile: zu ist er eine Statusmeldung,
 * offen ein Formular. Sechsundzwanzig dauerhaft offene Formulare wären
 * eine Wand, durch die niemand hindurchsieht. */
.vnachweise { display: flex; flex-direction: column; }
.vnachweis { border-top: 1px solid var(--linie-leise); }
.vnachweis:first-child { border-top: 0; }
.vnachweis > summary {
  display: flex; align-items: center; gap: .8rem;
  padding: .7rem 1.1rem; cursor: pointer; list-style: none;
}
.vnachweis > summary::-webkit-details-marker { display: none; }
.vnachweis > summary:hover { background: var(--karte-hell); }
.vnachweis[open] > summary { background: var(--karte-hell); }
.vnachweiskoerper { padding: .3rem 1.1rem 1.1rem; }
.vnachweiskoerper form { margin-top: .6rem; }
/* Abgelaufen ist die einzige Zeile, die sich auch zugeklappt meldet. */
.vnachweis.schlecht > summary { border-left: 3px solid var(--schlecht); padding-left: calc(1.1rem - 3px); }
.vnachweis.achtung > summary { border-left: 3px solid var(--achtung); padding-left: calc(1.1rem - 3px); }

/* Die vier Kalender-Schaltflächen: je Gruppe eine, mit ihrer Anzahl und
 * ihrer Vorwarnzeit darunter. Untereinander statt nebeneinander, weil die
 * Erklärung mitläuft und in eine Zeile nicht passt. */
.vkalender { display: grid; gap: .6rem; margin-top: .8rem; }
.vkalender .knopf {
  display: flex; flex-direction: column; align-items: flex-start; gap: .15rem;
  text-align: left; padding: .7rem .9rem; height: auto; white-space: normal;
}

/* Auswahllisten neben dem Anschreiben: Vorlage und Ansprechpartner. */
.vwahl { list-style: none; margin: 0; padding: 0; }
.vwahl li { border-top: 1px solid var(--linie-leise); }
.vwahl li:first-child { border-top: 0; }
.vwahl a {
  display: block; padding: .5rem .2rem; font-size: .85rem;
  color: inherit; text-decoration: none;
}
.vwahl a:hover { color: var(--gelb-schrift); }
.vwahl a.hier { font-weight: 600; color: var(--gelb-schrift); }
.vbriefform { padding: 1.1rem; }
.vbriefform label { display: flex; flex-direction: column; gap: .25rem;
  font-size: .82rem; margin-bottom: .8rem; }
.vbriefform textarea {
  width: 100%; resize: vertical; font-family: var(--zahlschrift, monospace);
  font-size: .82rem; line-height: 1.55;
}

/* ---- Wissen: der Argumentations-Baukasten ----------------------------
 * Lesetext, kein Formular. Deshalb eine begrenzte Zeilenlänge: Über volle
 * Bildschirmbreite findet das Auge den Zeilenanfang nicht wieder. */
.wissenkoerper { padding: 1.4rem 1.6rem 1.8rem; max-width: 60rem; }
.wissenkoerper h2 {
  margin: 2.2rem 0 .3rem; font-size: 1.15rem;
  padding-top: 1.4rem; border-top: 1px solid var(--linie-leise);
}
.wissenkoerper h2:first-of-type { margin-top: .2rem; padding-top: 0; border-top: 0; }
.wissenkoerper h3 { margin: 1.2rem 0 .3rem; font-size: .95rem; }
.wissenkoerper p { font-size: .88rem; line-height: 1.65; }
.wissenraster {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem; margin: 1rem 0;
}
.wissenkarte {
  padding: 1rem 1.1rem; background: var(--karte); border: 1px solid var(--linie);
  border-radius: var(--rund-gross);
}
.wissenkarte h3 { margin-top: 0; }
.wissenkarte p { margin: .4rem 0; }
/* Die Zahl ist das Argument. Sie darf groß sein — aber im Marken-Gelb und
 * nicht in einer Signalfarbe: Es ist eine Zahl, keine Meldung. */
.wissenzahl {
  font-size: 1.9rem; font-weight: 600; line-height: 1.1;
  color: var(--gelb-schrift); font-variant-numeric: tabular-nums;
  letter-spacing: -.02em; margin: .2rem 0;
}
.wissenquelle { display: block; margin-top: .5rem; }
.wissenliste { font-size: .88rem; line-height: 1.7; padding-left: 1.2rem; }
.wissenliste li { margin-bottom: .35rem; }
.wissenkopf { display: flex; align-items: baseline; gap: .6rem; margin-top: 1.4rem; }
.wissennummer {
  flex: none; font-variant-numeric: tabular-nums; font-weight: 600;
  color: var(--gelb-schrift); font-size: .9rem;
}
.wissenbalken { display: block; height: 1px; background: var(--linie-leise); margin: 1.6rem 0; }

/* ---- Verpixelung Video (07.09.2026) ----------------------------------
 *
 * Die Videoseite bekam am 07.09.2026 alles, was die Fotoseite kann und was
 * beim Livebild einen Sinn ergibt: eine Kachelreihe zur Wahl des
 * Zeichengrunds und eine Ebene, die zeigt, was WIRKLICH im Kameraprofil
 * steht. Beides braucht ein paar Regeln, die es hier noch nicht gab.
 *
 * `.warnt` steht bereits weiter oben und wird deshalb hier NICHT noch
 * einmal erklärt – zwei Erklärungen derselben Farbe wären eine zu viel.
 *
 * Nichts Bestehendes wird geändert. Der Block hängt am Dateiende. */

.videoflaeche-kacheln {
  display: flex;
  gap: .4rem;
  overflow-x: auto;
  padding: .5rem .2rem;
  scrollbar-width: thin;
}
.videoflaeche-kachel {
  flex: 0 0 auto;
  border: 2px solid transparent;
  border-radius: 6px;
  background: none;
  padding: 0;
  line-height: 0;
  cursor: pointer;
}
.videoflaeche-kachel:hover { filter: none; border-color: var(--linie); }
.videoflaeche-kachel.gewaehlt { border-color: var(--akzent); }

/* Kein Bild zu holen – weder vom NAS noch von der Kamera. Dann wird auf
 * einem grauen Feld gezeichnet: Die Fläche wirkt genauso, man sieht nur
 * nicht, worauf. Das Seitenverhältnis muss hier stehen, weil ein <img> ohne
 * Quelle sonst null hoch wäre und die Leinwände darüber mit ihm
 * verschwänden. */
.videoflaeche-leergrund {
  background: var(--flaeche-leise, #2a2d31);
  aspect-ratio: 16 / 9;
  border-radius: 8px;
}

/* Auf Telefonbreite legen sich die Regler sonst quer und schieben die
 * Knöpfe aus der Zeile. */
.videoflaeche-werkzeuge input[type="range"] { max-width: 8.5rem; }
@media (max-width: 520px) {
  .videoflaeche-werkzeuge input[type="range"] { max-width: 6.5rem; }
  .videoflaeche-kachel img { width: 84px !important; }
}

/* Die Deckung der Zeichenfläche liegt auf der Leinwand, nicht in der Farbe
 * (07.09.2026). Vorher trug die Malfarbe selbst Alpha 0,55; `faerben()`
 * legt sie beim Laden aber mit `source-in` über die abgelegte Maske, und
 * dort ist das Ergebnis-Alpha das Produkt beider Alphas. Jedes Laden
 * schwächte damit den Alphakanal um 45 % — 140, 77, 42, 23 —, und ab 23
 * fällt selbst eine ganz gedeckte Zelle unter die Schwelle der Zerlegung:
 * Nach vier Speichervorgängen wurde im Livebild nichts mehr verschleiert.
 * Hier wirkt die Deckung nur auf die Anzeige und geht nicht in die Ablage
 * ein. Die Ansicht ist dieselbe wie vorher.
 *
 * Eigene Klasse statt `#flaeche`: Die Fotoseite nennt ihre Leinwand
 * genauso, und sie gehört nicht hierher. */
.videoflaeche-malen { opacity: .55; }

/* Die Farben der Legende unter der Bühne. Auf dem Bild selbst bleiben sie,
 * wie sie sind – dort liegt immer ein schwarzer Grund. In der Legende
 * stehen sie auf der Karte, und auf der hellen Karte (--karte: #fbfbf6)
 * kam das Hellblau nach WCAG auf 1,39:1 und das Gelb auf 1,56:1: Die
 * Quadrate waren nicht mehr auszumachen, ausgerechnet der Schlüssel zu
 * „was wirklich verschleiert wird" (07.09.2026). */
:root {
  --legende-gemalt: #4682f0;
  --legende-ist: #78e6ff;
  --legende-nicht-suchen: #f0c828;
}
:root[data-thema="hell"] {
  --legende-gemalt: #1f4fbf;
  --legende-ist: #0f6f8c;
  --legende-nicht-suchen: #8a6200;
}

/* ---- Nachtrag: die zwei Felder, die später gesetzt werden --------------
 *
 * `.kamerasuche input` und `.belegstift input` bekommen weiter oben eine
 * eigene, kleinere Schrift — und stehen im Stylesheet HINTER dem
 * Fingerabschnitt. Bei gleicher Genauigkeit gewinnt die spätere Regel,
 * also hätte der Fingerabschnitt dort nichts ausgerichtet: Diese beiden
 * Felder wären bei 14 Punkten geblieben, und Safari auf dem iPhone hätte
 * die Seite beim Antippen weiter herangezoomt (09.09.2026 nachgemessen).
 * Deshalb hier, ganz am Ende. */
@media (hover: none) and (pointer: coarse) {
  .kamerasuche input, .belegstift input,
  .suchfeld input[type="search"], .belegartwahl select,
  .tafelkopf .tafelsuche { font-size: 16px; }
}

/* Der Lagesatz auf der Betriebsseite (20.09.2026): ein Satz, der die Frage
 * „läuft alles?" beantwortet, in der Farbe des Zustands. */
.hinweis.lage { font-size: 1.05rem; padding: .8rem 1rem; margin: 0 0 1.1rem; }

/* Das ⓘ mit Einzelheiten zum Drüberfahren (20.09.2026): Bereiche, Geräte,
 * Downloads, Fehlversuche stehen nicht mehr als Zeilen und Marken unter
 * jedem Namen, sondern erscheinen erst, wenn die Maus darüber steht – am
 * Telefon beim Antippen (tabindex, :focus). */
.mehr {
  position: relative;
  display: inline-block;
  margin-left: .3rem;
  color: var(--schrift-leise);
  cursor: help;
  font-size: .95rem;
  line-height: 1;
  vertical-align: .05em;
}
.mehr .mehrtext {
  display: none;
  position: absolute;
  left: 0;
  top: 1.4rem;
  z-index: 20;
  min-width: 16rem;
  max-width: 24rem;
  padding: .6rem .8rem;
  background: var(--karte);
  color: var(--schrift);
  border: 1px solid var(--linie);
  border-radius: var(--rund);
  box-shadow: var(--schatten-schwebend);
  font-size: .8rem;
  font-weight: 400;
  line-height: 1.5;
  white-space: normal;
}
.mehr:hover .mehrtext, .mehr:focus .mehrtext, .mehr:focus-within .mehrtext { display: block; }
/* Vom Skript im Rahmen an den Schirm gehängt – so ragt der Kasten aus
   rollenden Tabellen heraus statt darin abgeschnitten zu werden. */
.mehr .mehrtext.schwebt { position: fixed; z-index: 60; }
.mehr .mehrtext b { font-weight: 600; }

/* Nutzertabellen (Portalstatistik, Auswertung) am Telefon (21.09.2026).
   Bis dahin galt `min-width: max-content` aus `.tabellenrolle` – Chrome
   machte die Tafel 850 px breit und ließ sie in der Karte rollen, Safari
   auf dem iPhone quetschte die sechs festen Spalten auf 290 px, sodass
   „Arbeitszeit", „Abrufe" und „Volumen" übereinander lagen. Oberhalb von
   640 px bleibt die Tafel eine Tabelle mit festem Mindestmaß, darunter wird
   jede Zeile zur Karte: Name und E-Mail oben, die Zahlen als Dreierreihe
   mit Beschriftung aus `data-t`; `.breit` nimmt die ganze Zeile, bei
   `.mitknopf` steht der Knopf der letzten Spalte rechts neben dem Namen. */
.tabellenrolle > .tabelle.nutzer { min-width: 46rem; }
@media (max-width: 640px) {
  /* Auch ohne Rolle: Auf der Weblink-Statistik stand die Tafel ohne
     `.tabellenrolle` in der Karte und behielt ihre 46rem – die Zeilen
     liefen 736 Punkte breit über den rechten Rand hinaus, Volumen und
     „Öffnen" lagen außerhalb des Bildes (22.09.2026). */
  .tabelle.nutzer, .tabellenrolle > .tabelle.nutzer { min-width: 0; }
  .tabelle.nutzer, .tabelle.nutzer tbody { display: block; }
  .tabelle.nutzer thead, .tabelle.nutzer colgroup { display: none; }
  .tabelle.nutzer tr {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .5rem .6rem;
    align-items: start;
    padding: .85rem 0;
    border-bottom: 1px solid var(--linie-leise);
  }
  .tabelle.nutzer tr:last-child { border-bottom: 0; }
  .tabelle.nutzer td { display: block; padding: 0; border: 0; }
  .tabelle.nutzer tr:hover td { background: none; }
  .tabelle.nutzer td.rechts { text-align: left; }
  .tabelle.nutzer td:first-child, .tabelle.nutzer td.breit { grid-column: 1 / -1; }
  .tabelle.nutzer td[data-t]::before {
    content: attr(data-t);
    display: block;
    font-size: .68rem;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--schrift-leise);
  }
  .tabelle.nutzer.mitknopf td:first-child { grid-column: 1 / 3; }
  .tabelle.nutzer.mitknopf td:last-child { grid-column: 3; grid-row: 1; text-align: right; }
  /* In einer bündigen Karte (Kopf `.firmenkopf`, Karte ohne Polster) brauchen
     die Karten je Zeile denselben Seitenabstand wie der Kopf – sonst kleben
     Name und Zahlen am runden Rand („das sieht nicht schön aus", 22.09.2026). */
  .firmenkopf ~ .tabelle.nutzer tr,
  .firmenkopf ~ .tabellenrolle .tabelle.nutzer tr,
  .firmenkopf ~ .tabelle.nutzerliste tr { padding-inline: 1.2rem; }
}

/* Schwebende Leiste unten links am Telefon (mittel/zurueck.js, 21.09.2026):
   Zurück und Neu laden. Unten, weil der Daumen dort liegt; links, weil
   rechts Meldungen und der Vollbildknopf sitzen. Etwas vom Rand abgerückt
   – ganz in der Ecke „sah das gequetscht aus". Der Abstand nach unten
   rechnet die Leiste des iPhone mit ein. Sichtbar mit `.da`, sonst
   weggeschoben; im Vollbild nie. */
.schwebeleiste {
  position: fixed;
  left: calc(1.15rem + env(safe-area-inset-left, 0px));
  bottom: calc(1.35rem + env(safe-area-inset-bottom, 0px));
  z-index: 190;
  display: flex;
  align-items: stretch;
  padding: .25rem;
  gap: .2rem;
  border-radius: 999px;
  border: 1px solid var(--linie);
  background: color-mix(in srgb, var(--karte) 88%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18), 0 1px 3px rgba(0, 0, 0, .12);
  opacity: 0;
  transform: translateY(1.2rem);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.schwebeleiste.da { opacity: 1; transform: none; pointer-events: auto; }
.schwebeleiste button {
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--schrift);
  cursor: pointer;
}
.schwebeleiste button:active { background: var(--karte-hell); }
.schwebeleiste button + button { position: relative; }
.schwebeleiste button + button::before {
  content: "";
  position: absolute;
  left: -.1rem;
  top: .6rem;
  bottom: .6rem;
  width: 1px;
  background: var(--linie);
}
.schwebeleiste svg { display: block; }
.schwebeleiste .zurueckknopf svg { margin-right: 2px; }
.schwebeleiste .neuladenknopf.dreht svg { animation: schwebedreh .9s linear infinite; }
@keyframes schwebedreh { to { transform: rotate(360deg); } }
body.vollbild-offen .schwebeleiste,
.zoom-an ~ .schwebeleiste { display: none; }
@media (prefers-reduced-motion: reduce) {
  .schwebeleiste { transition: none; }
  .schwebeleiste .neuladenknopf.dreht svg { animation: none; }
}

/* Lagebild-Kachel Livestreams: Eine laufende Zahl ist grün, nicht rot –
   Zuschauer sind gewollt. */
.lagenzahl.gut { color: var(--gut); }

/* Feste Tafeln in der Rolle behalten ihr Mindestmaß (21.09.2026).
   `.tabellenrolle > .tabelle { min-width: max-content }` gewinnt gegen das
   Mindestmaß der Tafel selbst (gleiche Spezifität, spätere Regel), und
   Safari auf dem iPhone rechnet `max-content` bei `table-layout: fixed`
   auf die Kartenbreite: Die Benutzerliste stand mit 290 px Breite da, der
   Name lief Buchstabe für Buchstabe nach unten. Mit dem eigenen Maß rollt
   die Tafel in der Karte, wie gedacht; die Benutzerliste wird darunter zur
   Karte je Person. */
.tabellenrolle > .tabelle.kundenliste { min-width: 46rem; }
.tabellenrolle > .tabelle.kameraliste[data-ort] { min-width: 67rem; }

/* Benutzerliste am Telefon: je Person eine Karte – Name mit E-Mail und ⓘ,
   der Knopf rechts daneben, „Zuletzt da" darunter. */
@media (max-width: 640px) {
  .tabelle.nutzerliste, .tabelle.nutzerliste tbody { display: block; }
  .tabelle.nutzerliste thead { display: none; }
  .tabelle.nutzerliste tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .3rem .75rem;
    align-items: start;
    padding: .8rem 0;
    border-bottom: 1px solid var(--linie-leise);
  }
  .tabelle.nutzerliste tr:last-child { border-bottom: 0; }
  .tabelle.nutzerliste td { display: block; padding: 0; border: 0; width: auto; white-space: normal; }
  .tabelle.nutzerliste tr:hover td { background: none; }
  .tabelle.nutzerliste td:nth-child(1) { grid-column: 1; grid-row: 1; }
  .tabelle.nutzerliste td:nth-child(3) { grid-column: 2; grid-row: 1; text-align: right; }
  .tabelle.nutzerliste td:nth-child(2) { grid-column: 1 / -1; }
  .tabelle.nutzerliste td[data-t]::before { content: attr(data-t) " · "; }
  .tabelle.nutzerliste td[colspan] { grid-column: 1 / -1; }
}

/* Unterleiste am Telefon als Aufklappmenü (21.09.2026, vorlagen/rahmen.html).
   Am Rechner: Knopf weg, der Kasten um die Punkte ist `display: contents`,
   also steht die Reihe wie zuvor. Bis 640 px: eine Zeile „Technik ·
   Livestreams ▾", ein Tipp klappt die Punkte darunter auf, untereinander
   mit 44 Punkten Höhe. */
.unterklappe { display: none; }
.unterleiste-punkte { display: contents; }
@media (max-width: 640px) {
  .unterleiste { display: block; margin: .2rem 0 1.1rem; }
  .unterleiste-bereich { display: none; }
  .unterklappe {
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    min-height: 44px;
    padding: .45rem .85rem;
    border: 1px solid var(--linie);
    border-radius: var(--rund);
    background: var(--karte);
    color: var(--schrift);
    font: inherit;
    font-size: .9rem;
    text-align: left;
    cursor: pointer;
  }
  .unterklappe-bereich { color: var(--gelb-schrift); font-weight: 600; }
  .unterklappe-hier { color: var(--schrift-leise); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .unterklappe-hier::before { content: "· "; }
  .unterklappe-pfeil { margin-left: auto; color: var(--schrift-leise); transition: transform .15s ease; }
  .unterleiste.offen .unterklappe { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
  .unterleiste.offen .unterklappe-pfeil { transform: rotate(180deg); }
  .unterleiste-punkte { display: none; }
  .unterleiste.offen .unterleiste-punkte {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--linie);
    border-top: 0;
    border-radius: 0 0 var(--rund) var(--rund);
    background: var(--karte);
    padding: .25rem 0;
  }
  .unterleiste-punkte a { padding: 0 .85rem; }
  .unterleiste-punkte a.hier { text-decoration: none; background: var(--gelb-hell, var(--karte-hell)); }
}

/* Portalstatistik: Der Name in der Hausfarbe, damit das Auge die Personen
   sofort findet (21.09.2026 gewünscht). Dieselbe Farbe wie der offene
   Bereich in der Kopfzeile – hell wie dunkel lesbar. */
.personname { color: var(--gelb-schrift); font-size: 1.02em; }

/* Optimierter Zeitraffer (23.09.2026). Die Kachel ist die des Gesamtfilms,
   nur mit gelber Kante – das fertige, von Hand bearbeitete Stück soll sich
   von den gerechneten Filmen abheben, ohne eine zweite Kachelform zu
   brauchen. Der Fortschrittsbalken gehört zum Hochladen in Stücken auf der
   Kameraseite. */
.gesamtknopf.optimiert { border-left: 5px solid var(--gelb); }
.filmwahl .abschnitt.optimiert .abschnittskopf { color: var(--schrift); font-weight: 600; }
.optimiert-zeile { flex-wrap: wrap; }
.optimiert-zeile .fortschritt {
  flex: 1 1 100%;
  height: .45rem;
  margin-top: .5rem;
  border-radius: 999px;
  background: var(--karte-hell);
  overflow: hidden;
}
.optimiert-zeile .fortschritt > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--gelb);
  transition: width .2s ease;
}

/* Bilderleiste im Vollbild des Bauarchivs (seiten/bauarchiv.html,
   23.09.2026): nur hochkant, nur im Vollbild, nur bei 1×. Dezent: kein
   Kasten, ein weicher Schatten von unten, Kacheln leicht durchscheinend,
   die gewählte voll und mit gelbem Rand, darunter die Uhrzeit. Sie rollt
   waagerecht für sich (`touch-action: pan-x`), Wischen und Zoom der Fläche
   lassen sie in Ruhe. */
.vollleiste {
  position: absolute;
  left: 0;
  right: 0;
  /* Höher als der Rand (23.09.2026, „du musst die Slide-Bar etwas höher
     setzen"): Unten liegen auf dem iPhone der Balken zum Nachhausegehen
     und die Leiste von Safari – dort war sie halb verdeckt und schlecht
     zu treffen. */
  bottom: calc(env(safe-area-inset-bottom, 0px) + 2.4rem);
  z-index: 4;
  display: none;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  padding: 1rem 0 .4rem;
  background: linear-gradient(to top, rgba(0, 0, 0, .45), rgba(0, 0, 0, 0));
  touch-action: pan-x;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.vollleiste::-webkit-scrollbar { display: none; }
.vollleiste .spur {
  display: flex;
  gap: .5rem;
  width: max-content;
  padding-inline: calc(50vw - 2rem);   /* erstes und letztes Bild bis zur Mitte */
}
.vollleiste button {
  flex: 0 0 auto;
  width: 4rem;
  padding: 0;
  border: 0;
  background: none;
  display: grid;
  gap: .2rem;
  justify-items: center;
  opacity: .62;
  scroll-snap-align: center;
  transition: opacity .15s ease, transform .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.vollleiste button img,
.vollbild-ersatz .vollleiste button img,
.bildflaeche:fullscreen .vollleiste button img {
  display: block;
  width: 4rem;
  height: auto;
  max-width: none;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: .35rem;
  border: 1.5px solid rgba(255, 255, 255, .25);
  filter: none;
  transform: none;
}
.vollleiste button span {
  font-size: .62rem;
  line-height: 1;
  color: #e8e8e4;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .8);
}
.vollleiste button.an { opacity: 1; transform: translateY(-.15rem) scale(1.1); }
.vollleiste button.an img,
.vollbild-ersatz .vollleiste button.an img,
.bildflaeche:fullscreen .vollleiste button.an img { border-color: var(--gelb); }
.vollleiste button.an span { color: var(--gelb); }
@media (orientation: portrait) {
  .vollbild-ersatz .vollleiste,
  .bildflaeche:fullscreen .vollleiste { display: block; }
  /* Vergrößert füllt das Bild den Schirm – dann läge die Leiste im Bild. */
  .vollbild-ersatz.gezoomt .vollleiste,
  .bildflaeche:fullscreen.gezoomt .vollleiste { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .vollleiste button { transition: none; }
}

/* „Verpixelung prüfen" – Bewerten (24.09.2026). Am Rechner Bild links,
   Knöpfe rechts in einer Spalte, Tasten im Knopf. Am Telefon (Betreiber:
   „dass ich das auch dort trainieren kann") das Bild in voller Breite,
   darunter die drei Urteile als große Knöpfe nebeneinander, dann die
   Nebenknöpfe; Tastenhinweise und der Erklärabsatz fallen weg. */
.bewerten-fund {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 15rem;
  gap: 1rem;
  align-items: start;
}
.bewerten-knoepfe { display: flex; flex-direction: column; gap: .5rem; }
@media (max-width: 640px) {
  .bewerten-hinweis { display: none; }
  .bewerten-fund { grid-template-columns: minmax(0, 1fr); gap: .7rem; }
  .bewerten-knoepfe {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .5rem;
  }
  .bewerten-knoepfe .knopf { justify-content: center; padding: .85rem .4rem; font-size: .95rem; }
  .bewerten-knoepfe .knopf.urteil { padding: 1.1rem .4rem; font-size: 1.05rem; }
  .bewerten-knoepfe .taste, .bewerten-knoepfe .lang { display: none; }
  .bewerten-knoepfe .knopf { white-space: normal; line-height: 1.15; }
  .bewerten-stand { grid-column: 1 / -1; text-align: center; }
}
/* Bewerten – ein Fenster (25.09.2026 abends): drei große Urteile, darunter
   ein Strich, dann die Nebenknöpfe. Am Rechner als Spalte neben dem Bild,
   am Telefon als Reihen unter dem Bild. Betreiber: „übersichtlicher und
   luftiger". */
.ueknoepfe { display: flex; flex-direction: column; gap: .7rem; }
.ue-gruppe { display: grid; gap: .55rem; }
.ue-gruppe.haupt { grid-template-columns: minmax(0, 1fr); }
/* `ue-urteil`, nicht `urteil`: Die Klasse `.urteil` ist die Karte für
   Prüfurteile (Hintergrund var(--karte)) – auf dem gelben Knopf wurde der
   Text im Dunkeln unsichtbar (25.09.2026, Bild des Betreibers). */
.ue-gruppe.haupt .knopf { justify-content: center; padding: .9rem .6rem; font-size: 1rem; }
.ue-gruppe.neben { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ue-gruppe.neben .knopf { justify-content: center; padding: .55rem .4rem; }
.ue-trenner { border-top: 1px solid var(--linie); margin: .15rem 0; }
.ueknoepfe .bewerten-stand { text-align: center; }
.uefortschritt { margin: -.2rem 0 .7rem; }
.uefortschritt .balken { height: 8px; border-radius: 4px; background: var(--linie); overflow: hidden; }
.uefortschritt .balken span { display: block; height: 100%; background: var(--gelb); border-radius: 4px; transition: width .4s; }
.uefortschritt p { margin: .35rem 0 0; }
@media (max-width: 640px) {
  .ue-gruppe.haupt { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ue-gruppe.haupt .knopf { padding: 1rem .3rem; font-size: .95rem; white-space: normal; line-height: 1.15; }
  .ue-gruppe.neben { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ue-gruppe.neben .knopf { padding: .6rem .2rem; font-size: .85rem; white-space: normal; }
  .ueknoepfe .taste { display: none; }
}
/* Übersehene Menschen (25.09.2026): das ganze verpixelte Bild mit einer
   Zeichenfläche darüber – Rahmen ziehen mit Maus oder Finger. */
.rahmenfeld { position: relative; touch-action: none; user-select: none; -webkit-user-select: none; }
.rahmenfeld img { display: block; max-width: 100%; height: auto; margin: 0 auto; }
.rahmenfeld.voll img { width: 100%; }
/* Lage und Maß der Leinwand setzt das Skript auf das Bild – nicht auf den
   Kasten: Ein schmaler Ausschnitt in der Mitte eines breiten Kastens
   bekam sonst Rahmen um den Versatz verschoben (25.09.2026). */
.rahmenfeld canvas { position: absolute; left: 0; top: 0; cursor: crosshair; }
.rahmenliste { display: flex; flex-wrap: wrap; gap: .35rem; margin: .4rem 0 0; }
.rahmenliste button { font-size: .8rem; padding: .2rem .5rem; }

/* Freie Weblinks nach Boxen (27.09.2026): Aufbau wie die Kameraliste –
   gelber Strich, weißes Namensschild – und darin jeder Weblink als
   schmale Karte. Kopf mit Titel, Aufrufen und Griffen in einer Zeile,
   darunter die Adresse, dann Kameras, Takt und Module in einer Reihe. */
.gruppe > .weblink { padding: .9rem 1.1rem; margin: 0; }
.gruppe > .weblink + .weblink { margin-top: -.6rem; }
.weblinkkopf { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.weblinkkopf h3 { margin: 0; font-size: 1rem; overflow-wrap: anywhere; }
.weblinkkopf .dehnen { flex: 1 1 auto; }
.weblinkkopf .knopf, .weblinkkopf button { padding: .3rem .75rem; font-size: .82rem; }
.weblink .linkfeld { margin-top: .55rem; }
.weblinkreihe { display: flex; flex-wrap: wrap; gap: .5rem 1.4rem; margin-top: .6rem; align-items: baseline; }
.weblinkreihe > div { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; }
.weblinkreihe > div > .klein { margin-right: .2rem; }
@media (max-width: 640px) {
  .weblinkkopf .dehnen { flex-basis: 100%; height: 0; }
}
/* Aufklappbare Boxen der freien Weblinks (27.09.2026). */
.weblinkblock > .weblink { margin: 0 0 .8rem; }
.weblinkblock > .weblink:last-child { margin-bottom: 0; }
.weblinkblock > summary { flex-wrap: wrap; }
.weblinkblock .zugeklappt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 12rem; }
.weblinkblock[open] .zugeklappt { display: none; }
.baustelle.weblinkbox { margin-bottom: .8rem; }
.weblinkbox .gruppe { margin-bottom: 0; padding: .55rem .8rem; gap: 0; }

/* Dashboard „Freie Weblinks heute" als Liste (27.09.2026). */
.freiheute { list-style: none; margin: 0; padding: 0; }
.freiheute > li { padding: .75rem 1.15rem; border-top: 1px solid var(--linie); }
.freiheute-kopf { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem; }
.freiheute-kopf .leise, .freiheute-link .leise { font-size: .85rem; }
.freiheute-link { margin-top: .3rem; font-size: .9rem; overflow-wrap: anywhere; }
.freiheute-link a { margin-right: .4rem; }

/* Nicht-suchen-Flächen für Fotos als Liste (27.09.2026). */
.flaechenliste { list-style: none; margin: .4rem 0 0; padding: 0; }
.flaechenliste > li { padding: .6rem 0; border-top: 1px solid var(--linie); }
.flaechenkopf { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .8rem; }
.flaechenkopf .dehnen { flex: 1 1 auto; }
.flaechengrund { margin-top: .2rem; overflow-wrap: anywhere; }

/* iPhone: keine automatische Textvergrößerung (27.09.2026). Safari bläst
   sonst Text in Blöcken auf, die breiter sind als der Schirm – etwa in
   Tabellen im Rollrahmen. Auf der Speicherseite stand die Beschreibung
   dadurch in doppelter Größe und lief rechts aus dem Bild. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* `.tabelle.stapeln` (27.09.2026): Am Rechner eine gewöhnliche Tabelle,
   unter 640 px wird jede Zeile zur Karte – erste Zelle (und `.breit`)
   über die ganze Breite, die Zahlen als Dreierreihe mit Beschriftung aus
   `data-t`. Wie `.tabelle.nutzer`, aber ohne dessen feste Spalten am
   Rechner, damit lange Beschreibungen dort ihren Platz behalten. */
@media (max-width: 640px) {
  .tabellenrolle:has(> .tabelle.stapeln) { overflow: visible; }
  .tabelle.stapeln, .tabelle.stapeln > tbody { display: block; width: 100%; min-width: 0; }
  .tabelle.stapeln > thead { display: none; }
  .tabelle.stapeln > tbody > tr {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .45rem .6rem;
    align-items: start;
    padding: .75rem 0;
    border-bottom: 1px solid var(--linie-leise);
  }
  .tabelle.stapeln > tbody > tr:last-child { border-bottom: 0; }
  .tabelle.stapeln > tbody > tr > td { display: block; padding: 0; border: 0; max-width: none !important; }
  .tabelle.stapeln > tbody > tr:hover > td { background: none; }
  .tabelle.stapeln > tbody > tr > td.rechts { text-align: left; }
  .tabelle.stapeln > tbody > tr > td:first-child,
  .tabelle.stapeln > tbody > tr > td.breit { grid-column: 1 / -1; }
  .tabelle.stapeln > tbody > tr > td[data-t]::before {
    content: attr(data-t);
    display: block;
    font-size: .68rem;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--schrift-leise);
  }
  .tabelle.stapeln code { overflow-wrap: anywhere; }
  .tabelle.stapeln > tbody > tr.unterzeile { padding-top: 0; }
}

/* Boxwahl beim Anlegen einer Baustelle (28.09.2026): Kästchen in Spalten,
   am Handy untereinander. */
.boxwahl { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: .35rem .9rem; margin-top: .35rem; }
.boxwahl .ankreuz { margin: 0; }
