/* Die Gestaltungssprache des Hauses.
   ==================================

   Vorbild ist das cube-Frontend (git@git.rz.anduro.de:cubeEvaluation/cube.git). Dort ist die
   Sprache in docs/docs/cubeEvaluation/frontend/dark-elevation-color-system.md beschrieben, hier
   steht die Kurzfassung, damit niemand dafuer in ein fremdes Repository schauen muss.

   Das offene Projekt dahinter ist Radix Themes (radix-ui.com/themes). Radix selbst ist React und
   laesst sich hier nicht einbinden, uebernommen wird deshalb die Bildsprache und nicht der Code.
   Zwei Gedanken tragen alles:

   1. Durchscheinende Flaechen. Eine Karte ist eine halbdurchsichtige Auflage ueber einem sanften
      Farbverlauf, dazu eine Weichzeichnung des Hintergrundes. Die Seite scheint durch, die Karte
      entsteht durch ihre Kante und nicht durch eine eigene Fuellfarbe.
   2. Hoehe entsteht aus Ring und Schatten, nicht aus einem Rahmen. Statt eines harten 1px-Randes
      traegt eine Karte einen haarfeinen durchscheinenden Ring und einen weichen Schlagschatten.

   Inhalt bleibt flach. Gruppen werden durch getrennte Karten, Kopfbaender und Trennlinien
   auseinandergehalten, nie durch Kaesten in Kaesten. Eingabefelder sind gerahmte durchscheinende
   Felder und keine schwarzen Loecher.

   Grundlage ist Bootstrap 5.3 in der Fassung, die unter static/vendor liegt. Alles hier setzt nur
   Farbmarken und einige wenige Regeln darauf. Kein zweites Rahmenwerk, keine Bauteilbibliothek. */

/* --- Farbmarken -------------------------------------------------------------------------- */

:root,
[data-bs-theme="dark"] {
  --bs-body-bg: #1a1a1c;
  --bs-card-bg: #262c34;
  --bs-border-color: rgba(255, 255, 255, 0.09);

  /* Bootstraps gedaempftes Grau liegt auf dunklem Grund bei rund 2,8 zu 1 und damit unter der
     Lesbarkeitsschwelle von 4,5 zu 1. Beide Marken werden angehoben. */
  --bs-secondary-color: #adb5bd;
  --bs-tertiary-color: #969ca3;

  --cube-surface: rgba(255, 255, 255, 0.032);
  --cube-surface-2: rgba(255, 255, 255, 0.05);
  --cube-surface-3: rgba(255, 255, 255, 0.08);
  --cube-field: rgba(0, 0, 0, 0.22);
  --cube-ring: rgba(255, 255, 255, 0.09);
  --cube-ring-soft: rgba(255, 255, 255, 0.05);
  --cube-header-bg: rgba(255, 255, 255, 0.025);
  --cube-blur: blur(20px);
  --cube-shadow-card:
    0 0 0 1px var(--cube-ring),
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 6px 18px -8px rgba(0, 0, 0, 0.6);

  /* Das Kupfer aus dem Logo. Es ist die Farbe der Marke und wird nie zu einem Zustand, sonst
     stuende neben Gruen, Gelb, Rot und Blau eine fuenfte Farbe, die etwas zu bedeuten scheint.
     Gesetzt wird es an zwei Stellen: im Logo selbst und am Streifen der geoeffneten Box in der
     Seitenleiste. */
  --marke-kupfer: #c08c62;

  /* Radix Indigo als Akzent. */
  --bs-primary: #3e63dd;
  --bs-primary-rgb: 62, 99, 221;
  --bs-link-color: #93a8fb;
  --bs-link-color-rgb: 147, 168, 251;
  --bs-link-hover-color: #b3c2fd;

  /* --- Die vier Toene und wofuer sie stehen ---------------------------------------------------

     Ein Ton besteht aus drei Werten und nie aus einem: eine durchscheinende Flaeche, ein Ring in
     derselben Farbe und eine Schrift, die darauf lesbar ist. Genau so baut Radix seine weichen
     Marken, und genau daran hing die Uneinheitlichkeit vorher. Bootstrap kennt nur die volle
     Farbe, also stand neben einer durchscheinenden Karte eine giftgruene Pille.

     Die Schriftwerte sind Stufe 11 der Radix-Palette im dunklen Modus. Sie sind dafuer gemacht,
     auf der zugehoerigen Flaeche zu liegen, und halten dort die 4,5 zu 1.

     Was welcher Ton sagt, ist festgelegt und wird nicht je Seite neu entschieden:
     gruen  laeuft, verbunden, erkannt
     gelb   eingerichtet werden muesste etwas, es laeuft aber weiter
     rot    hier faellt gerade etwas aus
     blau   der Akzent, eine Auswahl oder ein Verweis
     grau   eine Angabe ohne Wertung, etwa eine Anzahl */
  --ton-gruen-flaeche: rgba(48, 164, 108, 0.16);
  --ton-gruen-rand: rgba(48, 164, 108, 0.34);
  --ton-gruen-schrift: #3dd68c;

  --ton-gelb-flaeche: rgba(255, 197, 61, 0.15);
  --ton-gelb-rand: rgba(255, 197, 61, 0.32);
  --ton-gelb-schrift: #ffca16;

  --ton-rot-flaeche: rgba(229, 72, 77, 0.17);
  --ton-rot-rand: rgba(229, 72, 77, 0.36);
  --ton-rot-schrift: #ff9592;

  --ton-blau-flaeche: rgba(62, 99, 221, 0.2);
  --ton-blau-rand: rgba(62, 99, 221, 0.4);
  --ton-blau-schrift: #9eb1ff;

  --ton-grau-flaeche: rgba(255, 255, 255, 0.06);
  --ton-grau-rand: rgba(255, 255, 255, 0.11);
  --ton-grau-schrift: #b0b4ba;

  /* Bootstraps Rosa fuer <code> stammt aus einem hellen Entwurf und ist die einzige Farbe im
     Portal, die in keiner Palette steht. Gesetzt wird es an einer Stelle: dem Passwort einer
     Cloudfreigabe, und das soll auffallen, weil es abgeschrieben wird. */
  --bs-code-color: #9eb1ff;
}

/* --- Grundflaeche ------------------------------------------------------------------------ */

body {
  background: linear-gradient(168deg, #313135 0%, #202023 46%, #141416 100%);
  background-attachment: fixed;
  min-height: 100vh;
}

body,
input,
select,
textarea,
button,
.card,
.form-control {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans",
    "Helvetica Neue", Arial, sans-serif;
}

/* --- Karten ------------------------------------------------------------------------------ */

.card {
  background-color: var(--cube-surface);
  -webkit-backdrop-filter: var(--cube-blur);
  backdrop-filter: var(--cube-blur);
  border: 0;
  border-radius: 14px;
  box-shadow: var(--cube-shadow-card);
}

/* Kopfband einer Karte: links der Name, rechts die Marke oder der eine Knopf, der dazugehoert.
   Es bricht um, statt den Knopf zusammenzuquetschen. Auf 320 Bildpunkten stand "Zur Kenntnis
   genommen" sonst zweizeilig neben einem einzeiligen Titel, und beide waren zu schmal. */
.card > .card-header {
  background: var(--cube-header-bg);
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--cube-ring);
  padding: 11px 16px;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  font-weight: 600;
}

.card > .card-header:first-child {
  border-top-left-radius: 14px;
  border-top-right-radius: 14px;
}

/* Ein buendiger Rumpf mit einer Tabelle darin muss auf die runde Unterkante beschnitten werden,
   sonst deckt der eckige Hintergrund der Tabelle die Ecken ab und die Rundung sieht kaputt aus.
 *
 * **overflow: hidden gehoert hier nicht hin.** Es stand bis zum 29.08.2026 in dieser Regel und
 * hat auf schmalen Bildschirmen Spalten verschluckt: `.card > .table-responsive:last-child` wiegt
 * schwerer als Bootstraps `.table-responsive`, das overflow-x: auto setzt. Auf einem Telefon von
 * 375 Bildpunkten lagen damit 400 Bildpunkte der Statistiktabelle hinter der Kante, unerreichbar,
 * ohne Balken und ohne Wischen. Die Tabelle sah aus, als endete sie dort.
 *
 * Die Rundung braucht kein Beschneiden. `border-radius` allein beschneidet den Hintergrund der
 * Tabelle bereits, weil er hinter dieser Kante liegt. */
.card > .card-body.p-0:last-child,
.card > .table-responsive:last-child {
  border-bottom-left-radius: 14px;
  border-bottom-right-radius: 14px;
}

.card > .card-body.p-0:last-child {
  overflow: hidden;
}

/* Eine Tabelle, die breiter ist als ihre Karte, wird gewischt. Damit das jemand merkt, liegt an
   der Kante ein Schatten, und zwar nur an der Seite, an der noch etwas kommt.
 *
 * Der Schatten steckt im Hintergrund und nicht in einem Pseudoelement. `background-attachment:
 * scroll` heisst bei einem scrollenden Element: die Flaeche klebt an dessen Kante und wandert
 * nicht mit dem Inhalt. Genau das ist hier gesucht, und es braucht dafuer weder eine zusaetzliche
 * Ebene noch position: sticky in einem Element, dessen einziges Kind eine Tabelle ist.
 *
 * Welche Seite leuchtet, setzt das Skript in base.html an data-wischbar. Ohne Skript bleibt das
 * Wischen selbst erhalten, es fehlt nur der Hinweis darauf. */
.table-responsive {
  scrollbar-width: thin;
  background-repeat: no-repeat;
  background-attachment: scroll;
  background-size: 22px 100%;
}

.table-responsive[data-wischbar="rechts"] {
  background-image: linear-gradient(270deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
  background-position: right center;
}

.table-responsive[data-wischbar="links"] {
  background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
  background-position: left center;
}

.table-responsive[data-wischbar="beide"] {
  background-image:
    linear-gradient(90deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)),
    linear-gradient(270deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
  background-position: left center, right center;
}

/* Eine abgesetzte Flaeche innerhalb einer Karte. Genau eine Stufe, nie geschachtelt. */
.abgesetzt {
  background-color: var(--cube-surface-2);
  border: 0;
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

/* --- Tabellen ---------------------------------------------------------------------------- */

.card .table > thead > tr > th,
.card .table-responsive > .table > thead > tr > th {
  background: var(--cube-header-bg);
  color: var(--bs-secondary-color);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  border-top: 0;
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--cube-ring);
  padding: 8px 12px;
  white-space: nowrap;
}

.card .table {
  --bs-table-border-color: var(--cube-ring-soft);
  --bs-table-bg: transparent;
}

.card .table > tbody > tr > td {
  font-size: 0.875rem;
  padding-top: 12px;
  padding-bottom: 12px;
  vertical-align: middle;
}

/* Ein Verweis in einer Tabelle traegt den Akzent und keinen Unterstrich. Bootstrap unterstreicht
   jeden Verweis, und in einer Spalte mit dreissig Eventnamen untereinander wird daraus ein
   Streifenmuster. Der Unterstrich kommt beim Ueberfahren, dort ist er die Auskunft, dass es sich
   um einen Verweis handelt. */
.card .table a {
  text-decoration: none;
}

.card .table a:hover,
.card .table a:focus-visible {
  text-decoration: underline;
}

/* --- Eingaben ---------------------------------------------------------------------------- */

.form-control,
.form-select {
  background-color: var(--cube-field);
  border-color: var(--cube-ring);
}

.form-control:focus,
.form-select:focus {
  background-color: var(--cube-field);
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), 0.25);
}

.form-check-input:checked {
  background-color: var(--bs-primary);
  border-color: var(--bs-primary);
}

/* --- Marken ---------------------------------------------------------------------------------

   Eine Marke ist eine weiche Pille und keine Signalfarbe. Bootstrap liefert `text-bg-success` und
   Verwandte als volle Flaeche mit weisser Schrift, und daneben steht in diesem Portal eine Karte,
   die durchscheint. Das passte nicht zusammen: eine Box, die einfach nur laeuft, trug eine
   Leuchtpille, waehrend die Karte darunter fast unsichtbar war.

   Uebersetzt wird an dieser einen Stelle und nicht in vierzig Vorlagen. Die Klassen bleiben, wie
   sie sind, nur ihr Aussehen wechselt. Wer eine neue Marke setzt, schreibt weiter
   `badge text-bg-success` und bekommt die Hausform.

   Die Auswahl ist absichtlich klein. Fuenf Toene reichen fuer alles, was dieses Portal zu sagen
   hat, und mehr Toene heissen nur, dass niemand mehr weiss, was welcher bedeutet. */

.badge {
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.01em;
  border-radius: 6px;
  padding: 0.3em 0.6em;
}

/* Das !important ist hier keine Nachlaessigkeit. Bootstrap setzt `text-bg-*` selbst mit
   !important, und ohne dieselbe Stufe bliebe die volle Farbe stehen, egal wie genau der Selektor
   ist. Genau daran ist der erste Anlauf am 29.08.2026 gescheitert. */
.badge.text-bg-success {
  color: var(--ton-gruen-schrift) !important;
  background-color: var(--ton-gruen-flaeche) !important;
  box-shadow: inset 0 0 0 1px var(--ton-gruen-rand);
}

.badge.text-bg-warning {
  color: var(--ton-gelb-schrift) !important;
  background-color: var(--ton-gelb-flaeche) !important;
  box-shadow: inset 0 0 0 1px var(--ton-gelb-rand);
}

.badge.text-bg-danger {
  color: var(--ton-rot-schrift) !important;
  background-color: var(--ton-rot-flaeche) !important;
  box-shadow: inset 0 0 0 1px var(--ton-rot-rand);
}

.badge.text-bg-primary {
  color: var(--ton-blau-schrift) !important;
  background-color: var(--ton-blau-flaeche) !important;
  box-shadow: inset 0 0 0 1px var(--ton-blau-rand);
}

.badge.text-bg-secondary,
.badge.text-bg-light,
.badge.text-bg-dark {
  color: var(--ton-grau-schrift) !important;
  background-color: var(--ton-grau-flaeche) !important;
  box-shadow: inset 0 0 0 1px var(--ton-grau-rand);
}

/* Dieselben Toene fuer die Hinweiskaesten. Ein Hinweis ist eine breite Marke und soll nicht
   anders aussehen als eine schmale. */
.alert-success {
  --bs-alert-color: var(--ton-gruen-schrift);
  --bs-alert-bg: var(--ton-gruen-flaeche);
  --bs-alert-border-color: var(--ton-gruen-rand);
  box-shadow: inset 0 0 0 1px var(--ton-gruen-rand);
}

.alert-warning {
  --bs-alert-color: var(--ton-gelb-schrift);
  --bs-alert-bg: var(--ton-gelb-flaeche);
  --bs-alert-border-color: var(--ton-gelb-rand);
  box-shadow: inset 0 0 0 1px var(--ton-gelb-rand);
}

.alert-danger {
  --bs-alert-color: var(--ton-rot-schrift);
  --bs-alert-bg: var(--ton-rot-flaeche);
  --bs-alert-border-color: var(--ton-rot-rand);
  box-shadow: inset 0 0 0 1px var(--ton-rot-rand);
}

.alert-secondary,
.alert-info {
  --bs-alert-color: var(--bs-body-color);
  --bs-alert-bg: var(--cube-surface-2);
  --bs-alert-border-color: var(--cube-ring);
}

/* Den Ton tragen das Symbol und die Ueberschrift, nicht der ganze Kasten. Vier Zeilen durchgehend
   in Bernstein lesen sich wie ein Alarm, und eine Box, die gerade nichts sendet, ist keiner. */
.alert .small {
  color: var(--bs-secondary-color);
}

/* Farbige Schrift ohne Flaeche, etwa die Meldung einer Box im Stoerungskasten. Bootstraps
   Vollwerte sind auf hellem Grund gerechnet und liegen auf diesem hier zu dunkel. */
.text-success { color: var(--ton-gruen-schrift) !important; }
.text-warning { color: var(--ton-gelb-schrift) !important; }
.text-danger { color: var(--ton-rot-schrift) !important; }

/* --- Schaltflaechen ---------------------------------------------------------------------- */

.btn {
  background-image: none;
  text-shadow: none;
  box-shadow: none;
  border-radius: 8px;
}

.btn-primary {
  --bs-btn-bg: #3e63dd;
  --bs-btn-border-color: #3e63dd;
  --bs-btn-hover-bg: #3656c4;
  --bs-btn-hover-border-color: #3656c4;
  --bs-btn-active-bg: #2f4aa8;
  --bs-btn-active-border-color: #2f4aa8;
  --bs-btn-disabled-bg: #3e63dd;
  --bs-btn-disabled-border-color: #3e63dd;
}

/* Weiche neutrale Schaltflaeche nach Radix-Art statt eines Umrisses. */
.btn-secondary,
.btn-outline-secondary {
  --bs-btn-bg: rgba(255, 255, 255, 0.05);
  --bs-btn-border-color: transparent;
  --bs-btn-color: #ced4da;
  --bs-btn-hover-bg: rgba(255, 255, 255, 0.09);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-hover-color: #fff;
  --bs-btn-active-bg: rgba(255, 255, 255, 0.12);
  --bs-btn-active-border-color: transparent;
}

/* Radix kennt drei Staerken und dieses Portal deshalb auch: voll fuer die eine Haupthandlung
   einer Karte, weich fuer alles Uebrige, weich und farbig fuer das, was einen Ton braucht.
 *
 * Am 29.08.2026 standen in der Steuerungskarte drei Formen nebeneinander: zwei volle Indigoknoepfe,
 * ein blau umrandeter und ein grauer. Ein Umriss ist in dieser Sprache nicht vorgesehen, er kommt
 * aus Bootstrap und sieht neben einer durchscheinenden Karte aus wie ein Fremdkoerper. Die
 * Umrissklassen behalten ihren Namen und tragen ab hier die weiche Form. */
.btn-outline-primary {
  --bs-btn-bg: var(--ton-blau-flaeche);
  --bs-btn-border-color: var(--ton-blau-rand);
  --bs-btn-color: var(--ton-blau-schrift);
  --bs-btn-hover-bg: rgba(62, 99, 221, 0.3);
  --bs-btn-hover-border-color: var(--ton-blau-rand);
  --bs-btn-hover-color: #d3ddff;
  --bs-btn-active-bg: rgba(62, 99, 221, 0.38);
  --bs-btn-active-border-color: var(--ton-blau-rand);
  --bs-btn-active-color: #d3ddff;
}

.btn-outline-danger {
  --bs-btn-bg: var(--ton-rot-flaeche);
  --bs-btn-border-color: var(--ton-rot-rand);
  --bs-btn-color: var(--ton-rot-schrift);
  --bs-btn-hover-bg: rgba(229, 72, 77, 0.28);
  --bs-btn-hover-border-color: var(--ton-rot-rand);
  --bs-btn-hover-color: #ffd1cf;
  --bs-btn-active-bg: rgba(229, 72, 77, 0.36);
  --bs-btn-active-border-color: var(--ton-rot-rand);
  --bs-btn-active-color: #ffd1cf;
}

.btn-outline-warning {
  --bs-btn-bg: var(--ton-gelb-flaeche);
  --bs-btn-border-color: var(--ton-gelb-rand);
  --bs-btn-color: var(--ton-gelb-schrift);
  --bs-btn-hover-bg: rgba(255, 197, 61, 0.26);
  --bs-btn-hover-border-color: var(--ton-gelb-rand);
  --bs-btn-hover-color: #ffe7a0;
  --bs-btn-active-bg: rgba(255, 197, 61, 0.34);
  --bs-btn-active-border-color: var(--ton-gelb-rand);
  --bs-btn-active-color: #ffe7a0;
}

/* Rot bleibt voll, wo wirklich etwas verschwindet. Ein Loeschknopf soll sich von einem weichen
   Knopf daneben unterscheiden lassen, ohne dass man die Beschriftung liest. */
.btn-danger {
  --bs-btn-bg: #e5484d;
  --bs-btn-border-color: #e5484d;
  --bs-btn-hover-bg: #cf3d42;
  --bs-btn-hover-border-color: #cf3d42;
  --bs-btn-active-bg: #b93337;
  --bs-btn-active-border-color: #b93337;
}

/* --- Reiter ------------------------------------------------------------------------------ */

.nav-tabs {
  border-bottom: 0;
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  background: var(--cube-surface-2);
  border-radius: 10px;
}

.nav-tabs .nav-item {
  margin: 0;
}

.nav-tabs .nav-link {
  border: 0;
  border-radius: 7px;
  color: var(--bs-secondary-color);
  padding: 6px 14px;
  font-weight: 500;
  margin: 0;
}

.nav-tabs .nav-link:hover {
  color: var(--bs-body-color);
  background: var(--cube-surface);
  border-color: transparent;
}

.nav-tabs .nav-link.active {
  background: var(--cube-surface-3);
  color: var(--bs-body-color);
  box-shadow: inset 0 0 0 1px var(--cube-ring), 0 1px 2px rgba(0, 0, 0, 0.3);
}

/* --- Listen ------------------------------------------------------------------------------ */

.list-group {
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--cube-shadow-card);
}

.list-group-item {
  background-color: var(--cube-surface);
  -webkit-backdrop-filter: var(--cube-blur);
  backdrop-filter: var(--cube-blur);
  border: 0;
  box-shadow: inset 0 -1px 0 var(--cube-ring-soft);
}

.list-group-item-action:hover,
.list-group-item-action:focus {
  background-color: var(--cube-surface-3);
  color: var(--bs-body-color);
}

/* --- Hinweise und Kopfleiste ------------------------------------------------------------- */

.alert {
  border: 0;
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.breadcrumb {
  --bs-breadcrumb-divider-color: var(--bs-tertiary-color);
  font-size: 0.875rem;
}

.tooltip-inner {
  background-color: #26262a;
  color: #e7eaee;
  border-radius: 7px;
  padding: 6px 10px;
  font-size: 0.8rem;
  text-align: left;
  white-space: pre-line;
  max-width: 320px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}

/* --- Eigene Bauteile ---------------------------------------------------------------------
   Alles ab hier gibt es in Bootstrap nicht und laesst sich auch nicht aus vorhandenen Klassen
   zusammensetzen. */

/* --- Die Boxauswahl, ruhige Fassung ---------------------------------------------------------

   Seit dem 31.08.2026 die eingebundene Ansicht. Hier wird ausgewaehlt und nicht ausgewertet,
   also bleibt die Liste grau: Farbe traegt die Meldeleuchte und das, was gerade nicht stimmt.
   Vorher trug jede Zeile zwei Verlaeufe, und auf einer Seite mit sechs Boxen waren das zwoelf
   farbige Flaechen, von denen keine etwas sagte, das nicht auch im Text stand.

   Die bunte Fassung steht weiter unten unter `.boxenliste` und ist aufgehoben. Ihre Vorlage
   liegt in templates/uebersicht/_liste_bunt.html. */

/* Die vier Zahlen ueber der Liste. */
.auswahlkopf {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
  margin-bottom: 0.35rem;
}

.auswahlkopf b {
  color: var(--bs-body-color);
  font-weight: 600;
}

.auswahlliste {
  display: flex;
  flex-direction: column;
}

.auswahlzeile {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.875rem 0.375rem;
  box-shadow: inset 0 -1px 0 var(--cube-ring);
  color: var(--bs-body-color);
  text-decoration: none;
}

.auswahlzeile:last-child {
  box-shadow: none;
}

.auswahlzeile:hover,
.auswahlzeile:focus-visible {
  background: var(--cube-surface);
  color: var(--bs-body-color);
}

.auswahlzeile-box {
  flex: 1 1 auto;
  min-width: 0;
}

.auswahlzeile-name {
  font-weight: 600;
}

.auswahlzeile-name .kennung {
  font-weight: 400;
  font-size: 0.8125rem;
  color: var(--bs-tertiary-color);
  margin-left: 0.5rem;
}

.auswahlzeile-lage {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  min-width: 0;
  margin-top: 0.125rem;
}

/* Derselbe Trennpunkt wie in der bunten Fassung: er haengt am vorigen Wort und steht nicht vor
   dem naechsten, und vor einer Marke steht keiner. Warum, steht weiter unten bei .boxzeile-lage. */
.auswahlzeile-lage > *:not(:last-child):not(.badge)::after {
  content: "·";
  margin-left: 0.5rem;
  color: var(--bs-tertiary-color);
}

.auswahlzeile-lage > *:not(:last-child):not(.badge):has(+ .badge)::after {
  content: none;
}

.auswahlzeile-wort {
  font-weight: 600;
}

/* Gelb steht in der Leuchte und nicht im Wort. Eine laufende Box mit einem offenen Vorfall ist
   verbunden, und genau das sagt das Wort. Am 29.08.2026 festgelegt: "eine box die online ist und
   eine störung hat soll grün in der übersicht dagestellt werden nicht orange." */
.auswahlzeile:has(.leuchte-an) .auswahlzeile-wort,
.auswahlzeile:has(.leuchte-warn) .auswahlzeile-wort { color: var(--ton-gruen-schrift); }
.auswahlzeile:has(.leuchte-aus) .auswahlzeile-wort { color: var(--bs-tertiary-color); }

.auswahlzeile-netz {
  color: var(--bs-tertiary-color);
}

.auswahlzeile-event {
  flex: 0 0 auto;
  width: 42%;
  max-width: 21rem;
  min-width: 0;
}

.auswahlzeile-eventname {
  font-size: 0.9375rem;
}

.auswahlzeile-event.leer .auswahlzeile-eventname {
  color: var(--bs-tertiary-color);
}

.auswahlzeile-pfeil {
  flex: none;
  color: var(--bs-tertiary-color);
  font-size: 0.8125rem;
}

/* Auf einem Telefon steht das Event unter der Box und nicht daneben. Nebeneinander blieben fuer
   beide Haelften rund 160 Bildpunkte, und ein Eventname passt dort nicht hinein. Der Pfeil faellt
   weg, die ganze Zeile ist ohnehin der Verweis.
 *
 * **Ein Raster und kein Umbruch.** Mit `flex-wrap: wrap` sprang die Meldeleuchte auf eine eigene
 * Zeile, sobald in der Lagezeile zwei Marken standen: der Kasten daneben bringt seine volle
 * Textbreite als Grundgroesse mit, und die passte dann nicht mehr neben die acht Bildpunkte der
 * Leuchte. Am 31.08.2026 auf 375 Bildpunkten gesehen. Ein Raster stellt nicht zur Debatte, was
 * wohin gehoert. */
@media (max-width: 639.98px) {
  .auswahlzeile {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "leuchte box"
      ".       event";
    column-gap: 0.75rem;
    row-gap: 0.25rem;
    align-items: start;
  }

  .auswahlzeile > .leuchte {
    grid-area: leuchte;
    margin-top: 0.45rem;
  }

  .auswahlzeile-box {
    grid-area: box;
  }

  .auswahlzeile-event {
    grid-area: event;
    width: auto;
    max-width: none;
  }

  .auswahlzeile-pfeil {
    display: none;
  }
}

/* --- Die Boxenliste, bunte Fassung -----------------------------------------------------------

   **Nicht mehr eingebunden**, siehe oben. Die Regeln bleiben stehen, damit ein Zeileneinsatz in
   dashboard.html genuegt, um zurueckzuschalten.


   Eine Zeile je Box, in zwei Haelften: links die Box, rechts ihr Event. Getrennt durch einen
   Verlauf ueber 45 Grad und nicht durch einen Strich.

   Der Zustand steckt im Verlauf an der linken Kante. Bis zum 28.08.2026 stand dort ein farbiger
   Punkt und rechts zwei Marken, also drei Zustaende an zwei Orten. Das Wort steht jetzt links
   daneben, denn eine Farbe allein ist keine Auskunft: Grau und Gruen unterscheiden sich fuer
   manche Augen nicht, und ein Punkt sagt ohnehin nicht, was er bedeutet. */

/* Die Zeilen stehen einzeln und nicht aneinandergeklebt. Bootstrap baut eine Liste als einen
   Block mit Trennlinien, hier traegt jede Box ihre eigene Karte. Zwei Verlaeufe je Zeile, der
   Zustand links und das Event rechts, laufen sonst ohne Pause in den der naechsten Zeile.
   Am 28.08.2026 beanstandet. */
.boxenliste {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  /* Rundung und Schatten wandern von der Liste auf die einzelne Zeile. Blieben sie hier, zoege
     der Schatten eine Kante um die Zwischenraeume mit. */
  border-radius: 0;
  overflow: visible;
  box-shadow: none;
}

.boxzeile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  /* Gleiche Hoehe fuer jede Zeile. Eine Box ohne Event traegt zwei Zeilen weniger, und eine Liste,
     in der die Zeilen springen, liest sich unruhig. */
  min-height: 6.5rem;
  overflow: hidden;
}

/* Die Trennlinie der Liste faellt weg, jede Zeile bekommt Rundung und Schatten. Die Rundung
   schneidet dabei beide Verlaeufe mit ab, dafuer sorgt overflow: hidden oben. */
.boxenliste > .boxzeile {
  border-radius: 14px;
  box-shadow: var(--cube-shadow-card);
}

/* Die linke Flaeche: der Verbindungszustand, gruen oder grau, und sonst nichts.
 *
 * Sie laeuft ueber 135 Grad aus und nicht mehr waagerecht. Am 29.08.2026 verlangt: "der verlauf
 * soll in 45 grad uhrzeigersinn auslaufen." Von den vorherigen 90 Grad im Uhrzeigersinn um 45
 * gedreht sind das 135, und damit steht die auslaufende Kante parallel zu der des Events rechts
 * und zu der des Stoerungsbandes dazwischen.
 *
 * Etwas breiter als vorher: eine Diagonale ueber 35 Prozent Breite und 6,5 rem Hoehe waere so
 * steil, dass sie wie eine Kante aussieht statt wie ein Verlauf. */
.boxzeile::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 42%;
  background: linear-gradient(135deg, var(--zustandston), transparent 72%);
  /* Dieselbe Geometrie wie beim Event rechts, nur spiegelverkehrt: die obere rechte Ecke liegt
     bei 50 Prozent des Verlaufs und traegt damit noch Farbe, obwohl er dort auslaufen soll. Die
     Maske blendet die rechte Kante waagerecht aus. */
  -webkit-mask-image: linear-gradient(90deg, #000 55%, transparent);
  mask-image: linear-gradient(90deg, #000 55%, transparent);
  pointer-events: none;
}

.boxzeile-online {
  --zustandston: rgba(48, 164, 108, 0.28);
}

/* Offline ist grau und nicht rot. Eine Box, die nachts aus ist, hat keine Stoerung. */
.boxzeile-offline {
  --zustandston: rgba(255, 255, 255, 0.07);
}

/* Einen zweiten Farbton fuer die Stoerung gibt es nicht mehr. Am 29.08.2026 verworfen: "lass den
 * orangenen verlauf weg." Die Zahl der offenen Vorfaelle steht als Marke unter dem Boxnamen, und
 * eine Box, die wirklich haengt, faellt ohnehin ueber den Verbindungszustand auf. */

.boxzeile-box {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.boxzeile-wort {
  font-weight: 600;
}

/* Die Angaben einer Zeile nebeneinander, mit Trennpunkt dazwischen. Ohne ihn steht dort eine
   Reihe ohne Gliederung, mit einem festen Zeichen im Text braeche sie an der falschen Stelle um. */
.boxzeile-lage {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  min-width: 0;
}

/* Der Trennpunkt haengt am vorigen Wort und steht nicht vor dem naechsten.
 *
 * Vorher stand er als ::before am folgenden Glied, und auf einem Telefon brach die Reihe genau
 * dort um. Die neue Zeile fing dann mit einem einzelnen Punkt an und sah aus wie ein Tippfehler.
 * Am Ende der vorigen Zeile faellt derselbe Punkt kaum auf. Am 29.08.2026 bei der Durchsicht auf
 * 375 Bildpunkten gefunden.
 *
 * Eine Marke bekommt keinen, und vor einer steht auch keiner. Sie traegt ihre eigene Kante, ein
 * Punkt davor waere ein zweiter Trenner an derselben Stelle.
 *
 * Zwei Regeln und nicht eine. Ein Browser ohne :has() verwirft den ganzen Selektor, in dem es
 * steht. Getrennt faellt dort nur die zweite Regel weg, und die Reihe hat weiterhin Trennpunkte,
 * einen davon an einer Stelle zu viel. Zusammengefasst haette sie gar keine mehr. */
.boxzeile-lage > *:not(:last-child):not(.badge)::after,
.seitenkopf-lage > *:not(:last-child):not(.badge)::after {
  content: "·";
  margin-left: 0.5rem;
  color: var(--bs-tertiary-color);
}

/* Dieselben drei Bedingungen noch einmal. Ohne sie waere diese Regel schwaecher als die obige und
   bliebe wirkungslos: :has() zaehlt so viel wie sein Inhalt, hier eine einzige Klasse. */
.boxzeile-lage > *:not(:last-child):not(.badge):has(+ .badge)::after,
.seitenkopf-lage > *:not(:last-child):not(.badge):has(+ .badge)::after {
  content: none;
}

/* Die rechte Haelfte. Sie traegt seit dem 28.08.2026 eine eigene Farbe, Indigo nach Violett aus
   der Radix-Palette, und nicht mehr dasselbe Grau wie jede andere Flaeche. Der Zustand links ist
   gruen, gelb oder grau, das Event blau. Damit sagt schon die Farbe, welche Haelfte man ansieht.

   Der Verlauf blendet ueber 135 Grad ein und endet nicht an einer Kante, sondern verliert sich
   schraeg im Grund der Zeile. Dieselbe Neigung wie die Flaeche links und das Stoerungsband in der
   Mitte. Am 29.08.2026 verlangt: "und der eventverlauf auch in 45 grad einblenden."

   Vorher standen hier 45 Grad, also die Gegendiagonale. Die beiden Kanten liefen damit
   aufeinander zu, und die Zeile sah in der Mitte aus wie ein Trichter. Parallel gelesen ist sie
   ruhiger.

   Das negative margin holt die Flaeche bis an den Rand der Zeile heran, die Polsterung der Liste
   bleibt als Innenabstand erhalten. */
.boxzeile-event {
  position: relative;
  flex: 0 0 auto;
  width: 46%;
  max-width: 26rem;
  min-width: 0;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.15rem;
  margin: -1rem -1rem -1rem 0;
  /* Links mehr Polsterung als rechts. Der Text beginnt erst dort, wo der Verlauf traegt, sonst
     stuende er im durchsichtigen Teil und sae aus wie ein Ausrutscher. */
  padding: 1rem 1rem 1rem 3.5rem;
}

/* Der Verlauf steckt seit dem 29.08.2026 in einem eigenen Element und nicht mehr im Hintergrund
   der Flaeche. Der Grund ist die Maske darunter: sie wuerde sonst den Text mit ausblenden.
 *
 * **Warum ueberhaupt eine Maske.** Beanstandet: "der event verlauf ist links am anfang noch
 * abgeschnitten." Genau so war es, und es lag nicht am Winkel, sondern an der Geometrie einer
 * Diagonale. Bei 135 Grad liegt die Startecke oben links, die Endecke unten rechts, und **beide
 * anderen Ecken liegen bei 50 Prozent**. Die untere linke Ecke traegt damit die halbe Farbe,
 * obwohl der Verlauf dort erst anfangen soll. Die Elementkante schneidet ihn dort hart ab.
 *
 * Am Winkel laesst sich das nicht beheben, jede Diagonale trifft eine Ecke. Ein groesserer
 * durchsichtiger Anteil auch nicht: er muesste bis 50 Prozent reichen und liesse nur noch ein
 * Dreieck uebrig. Die Maske blendet stattdessen die linke Kante ueber ein Viertel der Breite
 * waagerecht aus, unabhaengig vom Winkel. Der 45-Grad-Charakter bleibt, die Kante ist weg. */
.boxzeile-event::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Die Deckung bleibt unter 40 Prozent. Darueber faellt der gedaempfte Text der zweiten Zeile
     unter das Lesbarkeitsmass von 4,5 zu 1, gemessen gegen #adb5bd auf dem Grund der Zeile. */
  background: linear-gradient(135deg,
                              rgba(110, 86, 207, 0) 8%,
                              rgba(110, 86, 207, 0.22) 52%,
                              rgba(62, 99, 221, 0.38) 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26%);
  mask-image: linear-gradient(90deg, transparent 0, #000 26%);
  pointer-events: none;
}

/* Der Text liegt ueber dem Verlauf. Ohne das deckte das Pseudoelement ihn ab, es ist das letzte
   Kind und steht damit in derselben Ebene weiter oben. */
.boxzeile-event > * {
  position: relative;
}

/* Auf einem schmalen Bildschirm stehen die beiden Haelften untereinander. Ein 45-Grad-Verlauf
   ueber eine Zeile von 320 Bildpunkten Breite waere ein Strich, kein Verlauf. */
@media (max-width: 767.98px) {
  .boxzeile {
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .boxzeile-event {
    /* Die zwei Zentimeter sind die Polsterung der Zeile links und rechts. Mit width: 100% waere
       die Flaeche nur so breit wie der Inhalt und endete vor der rechten Kante. */
    width: calc(100% + 2rem);
    max-width: none;
    margin: 0 -1rem -1rem;
    padding: 0.6rem 1rem;
  }

  /* Untereinander laeuft der Verlauf fast senkrecht und nicht ueber die Diagonale. Die Maske
     faellt weg: sie blendet eine linke Kante aus, die es hier nicht gibt, denn die Flaeche
     beginnt am Rand der Zeile und nicht neben einer zweiten Haelfte. */
  .boxzeile-event::before {
    background: linear-gradient(160deg,
                                rgba(110, 86, 207, 0.05),
                                rgba(62, 99, 221, 0.3) 70%);
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* --- Es laedt ---------------------------------------------------------------------------------

   Rueckmeldung auf einen Klick, der die Seite wechselt. Am 28.08.2026 beanstandet: zwischen dem
   Klick und der neuen Seite passierte nichts, und wer nichts sieht, klickt ein zweites Mal.

   Zwei Anzeigen, ein Schalter. Der Balken am oberen Rand faellt aus dem Augenwinkel auf, auch
   wenn der Blick unten in der Liste liegt. Der Spinner daneben sagt, was er bedeutet. */

.ladeleiste {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 2000;
  overflow: hidden;
  background: var(--cube-ring-soft);
  pointer-events: none;
}

.ladeleiste-balken {
  height: 100%;
  width: 30%;
  background: linear-gradient(90deg, transparent, var(--bs-primary), transparent);
  animation: ladeleiste-lauf 1.1s linear infinite;
}

@keyframes ladeleiste-lauf {
  from { transform: translateX(-100%); }
  to { transform: translateX(430%); }
}

.ladezeichen {
  position: fixed;
  top: 0.9rem;
  right: 0.9rem;
  z-index: 2001;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
  font-size: 0.875rem;
  background-color: var(--bs-card-bg);
  box-shadow: var(--cube-shadow-card);
  pointer-events: none;
}

/* Bootstrap setzt [hidden] auf display: none !important. Die Regel steht hier trotzdem: ohne sie
   haengt die Anzeige an einer fremden Datei, und display: flex wuerde sie sonst wieder sichtbar
   machen. */
.ladeleiste[hidden],
.ladezeichen[hidden] {
  display: none;
}

/* Der Zeiger sagt dasselbe noch einmal, dort wo die Maus gerade steht. */
body.laedt,
body.laedt a,
body.laedt button {
  cursor: progress;
}

/* Wer Bewegung abgestellt hat, bekommt den Balken als ruhige Flaeche. Die Auskunft bleibt, die
   Animation faellt weg. */
@media (prefers-reduced-motion: reduce) {
  .ladeleiste-balken {
    width: 100%;
    animation: none;
    background: var(--bs-primary);
  }
}

/* --- Die Haken im Meldungsblock -------------------------------------------------------------

   Je Anlass eine Reihe Haken, einer je Adresse. Nebeneinander und umbrechend: untereinander
   waeren es bei fuenf Anlaessen und fuenf Adressen fuenfundzwanzig Zeilen. */
.meldungshaken {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 1.25rem;
}

.meldungshaken .form-check {
  margin-bottom: 0;
}

/* Chart.js braucht einen Eltern mit fester Hoehe, sonst waechst die Leinwand bei jedem
   Neuzeichnen weiter. maintainAspectRatio: false allein reicht dafuer nicht. */
.diagramm-flaeche {
  position: relative;
  height: 260px;
}

@media (max-width: 575.98px) {
  .diagramm-flaeche {
    height: 180px;
  }
}

/* Erreichbarkeitsbalken. Ein Balken aus Abschnitten mit Prozentbreiten.
   Offline ist grau und nicht rot: eine Box, die nachts aus ist, hat keine Stoerung. Rot bleibt
   dem vorbehalten, was wirklich schiefgegangen ist. */
.verfuegbarkeit-balken {
  height: 1.75rem;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  background-color: var(--cube-surface-2);
}

.verfuegbarkeit-segment {
  height: 100%;
  cursor: default;
}

.verfuegbarkeit-segment:hover {
  filter: brightness(1.35);
}

.verfuegbarkeit-online {
  background-color: var(--bs-success);
}

.verfuegbarkeit-offline {
  background-color: #4a4d52;
}

.verfuegbarkeit-legende {
  display: inline-block;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 3px;
  vertical-align: middle;
}

/* Platzhalter, solange von einer Box noch kein Livebild vorliegt. Feste Hoehe, damit die Karte
   nicht springt, sobald das erste Bild da ist. */
/* Kachel der Bildergalerie. Feste Hoehe, damit das Raster nicht nach jedem geladenen Bild
   springt, und ein Ring statt eines Rahmens. */
/* Eine Kachel in der Galerie zeigt das ganze Bild und schneidet nichts ab. Am 29.08.2026
   beanstandet: "die galerie soll die bilder ohne abrundung anzeigen und vollständig."

   `cover` fuellte die Kachel und schnitt dafuer oben und unten weg. Bei einer Collage im
   Querformat verschwand damit die halbe Aufnahme, und wer nachsehen wollte, was auf dem Bild ist,
   musste es erst oeffnen. `contain` zeigt alles und laesst links und rechts Grund stehen. Die
   Rundung faellt weg: ein Foto hat gerade Kanten. */
.kachelbild {
  width: 100%;
  height: 130px;
  object-fit: contain;
  border-radius: 0;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  background-color: rgba(0, 0, 0, 0.35);
}

/* Der Wartehinweis, solange ein Befehl unterwegs ist. Er sitzt fest am unteren Rand, damit er
   auch dann sichtbar bleibt, wenn die Seite gescrollt ist. */
.laeuft-leiste {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 1080;
  max-width: min(92vw, 40rem);
  background: rgba(30, 30, 34, 0.94);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-radius: 12px;
  box-shadow: 0 0 0 1px var(--cube-ring), 0 10px 30px -10px rgba(0, 0, 0, 0.8);
  padding: 0.65rem 1rem;
}

/* Ein Formular, dessen Knopf gerade wartet, nimmt keine weitere Eingabe an. */
.wartet {
  pointer-events: none;
  opacity: 0.6;
}

/* Ein Formular, dessen Anfrage gerade laeuft. htmx setzt die Klasse und nimmt sie danach wieder
   weg, deshalb kann der Kreisel hier stehen statt im Skript: von Hand gesetzt bliebe er stehen,
   denn die Seite wechselt seit dem 30.08.2026 nicht mehr. */
form.htmx-request {
  pointer-events: none;
  opacity: 0.6;
}

form.htmx-request button[type="submit"]::before {
  content: "";
  display: inline-block;
  width: .875rem;
  height: .875rem;
  margin-right: .5rem;
  vertical-align: -2px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: .75s linear infinite spinner-border;
}

/* --- Ausgegraut, solange die Box nicht verbunden ist -------------------------------------------

   Die Werte bleiben lesbar, die Knoepfe nicht bedienbar. Eine weggefallene Box zeigte vorher
   dieselbe Seite wie eine laufende, und ein Klick legte einen Befehl an, den niemand abholte.

   Gesperrt wird an data-befehl. Das traegt jedes Formular, das einen Auftrag an die Box anlegt,
   und nur diese. Das Verwerfen der Bilderkopie im Portal, das Quittieren einer Stoerung und das
   Leeren der Warteschlange bleiben bedienbar, sie wirken hier und nicht dort.

   pointer-events sperrt den Zeiger, den Rest erledigt das Skript mit dem echten disabled. Ohne
   Skript bleibt der Riegel im Dienst, siehe befehle.BoxNichtErreichbar. */
.befehle-gesperrt form[data-befehl] {
  opacity: 0.45;
}

.befehle-gesperrt form[data-befehl] button,
.befehle-gesperrt form[data-befehl] input,
.befehle-gesperrt form[data-befehl] select,
.befehle-gesperrt form[data-befehl] textarea,
.befehle-gesperrt form[data-befehl] label {
  pointer-events: none;
}

.befehle-gesperrt form[data-befehl] button {
  cursor: not-allowed;
}

/* Balken fuer den Papiervorrat und den Abgleich mit der Cloud. */
.fortschritt-schmal {
  height: 0.5rem;
  border-radius: 4px;
  background-color: var(--cube-surface-2);
}

/* Jeder Balken liegt in derselben Rinne. Bootstraps Vorgabe ist ein flaches Grau, das auf dieser
   Grundflaeche wie ein Fremdkoerper wirkt: die Rinne gehoert zur Karte und nicht auf sie. */
.progress {
  background-color: var(--cube-field);
  box-shadow: inset 0 0 0 1px var(--cube-ring-soft);
  border-radius: 4px;
}

/* --- Seitenleiste und Pfad ----------------------------------------------------------------

   Die Navigation stand bis zum 31.08.2026 als Band oben. Sechs Punkte in derselben Farbe, und
   der Unterschied zwischen den Orten, an denen gearbeitet wird, und dem, wo nur nachgesehen
   wird, war nur Leerraum. Links tragen die Punkte Gruppen mit Ueberschrift, und darunter passen
   die Boxen selbst hin, jede mit ihrer Meldeleuchte.

   Unterhalb von lg wird die Leiste zur Schublade. Das macht `offcanvas-lg` aus Bootstrap, kein
   eigenes Skript. Darum die zwei Medienabfragen: unter lg legt Bootstrap die Leiste fest an den
   Rand, ab lg ist sie ein gewoehnlicher Kasten und wird hier zur Spalte gemacht. */

.portalrahmen {
  min-height: 100vh;
}

.seitenleiste {
  --bs-offcanvas-width: 272px;
  /* **Das !important gehoert hierhin.** Sobald die Leiste ab lg kein Offcanvas mehr ist, setzt
     Bootstrap ihr `background-color: transparent !important`. Ohne dieselbe Waffe haette sie auf
     dem Bildschirm keine eigene Flaeche: links staende nur der Grundverlauf der Seite, getrennt
     durch einen Haarstrich. Am 31.08.2026 beim Nachmessen gefunden, auf dem Bild war es kaum zu
     sehen, weil der Verlauf links ohnehin dunkler ist. */
  background-color: rgba(15, 15, 18, 0.88) !important;
  -webkit-backdrop-filter: var(--cube-blur);
  backdrop-filter: var(--cube-blur);
  box-shadow: inset -1px 0 0 var(--cube-ring);
}

/* Die eigene Klasse steht mit im Selektor, sonst gewinnt Bootstraps `.offcanvas-lg
   .offcanvas-body` mit seinem `flex-direction: row`. */
.seitenleiste-innen.offcanvas-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px 12px 12px;
  overflow-y: auto;
}

.leistenmarke {
  display: flex;
  align-items: center;
  padding: 2px 8px 12px;
}

.leistenmarke img {
  width: 148px;
  height: auto;
  display: block;
}

/* Die Suche steht in der Leiste und nicht im Inhalt. Die Leiste ist die einzige Flaeche, die auf
   jeder Seite an derselben Stelle steht. */
.leistensuche {
  position: relative;
  margin-bottom: 6px;
}

.leistensuche i {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.8125rem;
  color: var(--bs-tertiary-color);
  pointer-events: none;
}

.leistensuche input {
  width: 100%;
  border: 0;
  border-radius: 9px;
  padding: 8px 11px 8px 32px;
  background: var(--cube-field);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  color: var(--bs-body-color);
  font-size: 0.875rem;
}

.leistensuche input::placeholder {
  color: var(--bs-tertiary-color);
}

.leistensuche input:focus {
  outline: 0;
  box-shadow: inset 0 0 0 1px rgba(var(--bs-primary-rgb), 0.6);
}

.leistengruppe {
  font-size: 0.65rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bs-tertiary-color);
  padding: 15px 10px 7px;
}

.leistenpunkt,
.leistenbox {
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  border-radius: 9px;
  color: var(--bs-secondary-color);
  text-decoration: none;
  min-width: 0;
}

.leistenpunkt {
  padding: 9px 12px;
  font-size: 0.9375rem;
}

.leistenpunkt > i {
  width: 17px;
  text-align: center;
  flex: none;
}

.leistenpunkt:hover,
.leistenbox:hover {
  color: var(--bs-body-color);
  background: var(--cube-surface-2);
}

.leistenpunkt.active {
  color: var(--bs-body-color);
  background: rgba(var(--bs-primary-rgb), 0.2);
  box-shadow: inset 0 0 0 1px rgba(var(--bs-primary-rgb), 0.45);
}

.leistenpunkt.active > i {
  color: var(--bs-link-color);
}

/* Der Streifen an der Kante sagt, wo man ist, ohne dass die Flaeche dafuer kraeftiger werden
   muss. Indigo fuer den Ort, Kupfer aus der Marke fuer die geoeffnete Box: zwei Marken, zwei
   Bedeutungen, und keine davon kommt den vier Zustandstoenen in die Quere. */
.leistenpunkt.active::before,
.leistenbox.active::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 7px;
  bottom: 7px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--bs-primary);
}

.leistenzahl {
  margin-left: auto;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--cube-surface-2);
  color: var(--bs-tertiary-color);
}

.leistenpunkt.active .leistenzahl {
  background: rgba(var(--bs-primary-rgb), 0.32);
  color: var(--bs-link-hover-color);
}

.leistenbox {
  padding: 7px 12px 7px 14px;
  font-size: 0.875rem;
}

.leistenbox.active {
  color: var(--bs-body-color);
  background: var(--cube-surface-2);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.leistenbox.active::before {
  background: var(--marke-kupfer);
}

.leistenkurz {
  margin-left: auto;
  padding-left: 6px;
  font-size: 0.7rem;
  color: var(--bs-tertiary-color);
  white-space: nowrap;
}

/* Die Meldeleuchte. Drei Zustaende, nicht mehr: sie laeuft, sie laeuft mit offenen Vorfaellen,
   sie ist weg. Das Wort steht daneben, eine Farbe allein ist keine Auskunft. */
.leuchte {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}

.leuchte-an { background: #30a46c; box-shadow: 0 0 0 3px rgba(48, 164, 108, 0.18); }
.leuchte-warn { background: #ffc53d; box-shadow: 0 0 0 3px rgba(255, 197, 61, 0.16); }
.leuchte-aus { background: #4a4d54; }

.leistenfuss {
  margin-top: auto;
  padding-top: 10px;
  box-shadow: inset 0 1px 0 var(--cube-ring);
}

/* Die Kopfzeile gibt es nur unterhalb von lg, siehe base.html. */
.mobilkopf {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: rgba(16, 16, 19, 0.9);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 -1px 0 var(--cube-ring);
}

.mobilmarke img {
  width: 104px;
  height: auto;
  display: block;
}

.mobilknopf {
  width: 36px;
  height: 36px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 9px;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  color: var(--bs-secondary-color);
  text-decoration: none;
}

.mobilknopf:hover {
  color: var(--bs-body-color);
  background: var(--cube-surface-2);
}

/* Ab lg steht die Leiste fest und der Inhalt daneben. Sie bleibt beim Blaettern stehen und
   scrollt notfalls in sich, damit auch zwanzig Boxen hineinpassen. */
@media (min-width: 992px) {
  .portalrahmen {
    display: flex;
    align-items: flex-start;
  }

  .seitenleiste {
    position: sticky;
    top: 0;
    flex: 0 0 260px;
    width: 260px;
    height: 100vh;
  }

  .seitenleiste-innen.offcanvas-body {
    height: 100%;
  }

  /* Der Inhalt braucht Luft zur Leiste. Die `.container` von Bootstrap richtet ihre Breite nach
     dem Fenster und nicht nach der Spalte, in der sie steht: neben einer 260 Bildpunkte breiten
     Leiste blieben von ihrer Rinne nur die eigenen 12 uebrig, und die Ueberschrift klebte an der
     Kante. */
  .portalinhalt {
    flex: 1 1 auto;
    min-width: 0;
    padding-inline: 18px;
  }
}

/* Der Pfad sitzt in einem eigenen Band ueber dem Inhalt. Vorher stand er als lose Zeile
   ueber der Ueberschrift, in der Vorgabefarbe von Bootstrap und auf jeder Seite anders weit
   eingerueckt. */
.pfadleiste {
  box-shadow: inset 0 -1px 0 var(--cube-ring);
  background: rgba(255, 255, 255, 0.015);
  padding: 0.5rem 0;
}

.breadcrumb {
  --bs-breadcrumb-divider: "›";
  --bs-breadcrumb-divider-color: var(--bs-tertiary-color);
  --bs-breadcrumb-item-active-color: var(--bs-body-color);
  font-size: 0.875rem;
}

.breadcrumb-item + .breadcrumb-item::before {
  padding-right: 0.5rem;
  padding-left: 0.5rem;
}

.breadcrumb-item a {
  color: var(--bs-secondary-color);
  text-decoration: none;
}

.breadcrumb-item a:hover {
  color: var(--bs-body-color);
  text-decoration: underline;
}

.breadcrumb-item.active {
  font-weight: 500;
}

/* --- Fehlerseite -------------------------------------------------------------------------- */

.fehlerseite {
  max-width: 34rem;
  margin: 3rem auto;
}

/* --- Frischezaehler ----------------------------------------------------------------------- */

/* Laeuft im Browser mit und zeigt, wie alt die Angaben auf der Seite sind. Bleibt die Box
   laenger als zwei Takte still, faellt das hier zuerst auf. */
.frischezaehler {
  font-variant-numeric: tabular-nums;
}

.frischezaehler.veraltet {
  color: var(--bs-warning);
}

/* --- Bildteile eines Events --------------------------------------------------------------- */

/* Immer dieselben fuenf Reihen, immer untereinander, je Reihe die Auswahl aus dem Bestand und
   der Weg, etwas Neues hochzuladen. */
.bildteil {
  padding: 0.9rem 0;
  box-shadow: inset 0 -1px 0 var(--cube-ring-soft);
}

.bildteil:last-child {
  box-shadow: none;
  padding-bottom: 0;
}

.bildteil:first-child {
  padding-top: 0;
}

.bildteile.laedt,
.bildteil.laedt {
  opacity: 0.55;
  pointer-events: none;
}

/* Das gewaehlte Bild in gross. Der Platzhalter daneben ist genauso hoch, damit die Reihe beim
   Auswaehlen nicht springt. */
.teilbild {
  width: 100%;
  height: 74px;
  object-fit: contain;
  border-radius: 8px;
  background-color: #fff;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.teilbild-leer {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--cube-surface-2);
  color: var(--bs-tertiary-color);
  font-size: 0.75rem;
}

/* Die Karte nimmt hineingezogene Dateien an. Kein eigener Kasten dafuer: der staende die meiste
   Zeit leer herum. Stattdessen faerbt sich die Flaeche, die ohnehin da ist. */
[data-ablage] {
  position: relative;
  border-radius: 12px;
  transition: box-shadow 0.12s ease, background-color 0.12s ease;
}

[data-ablage].ablage-offen {
  background-color: var(--cube-surface-2);
  box-shadow: inset 0 0 0 2px var(--bs-primary);
}

/* Der Hinweis liegt ueber der Karte und nimmt keine Zeigerereignisse an. Sonst loeste er beim
   Erscheinen ein dragleave aus, und die Flaeche flackerte. */
.ablagehinweis {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  border-radius: 12px;
  background-color: rgba(26, 26, 28, 0.72);
  color: var(--bs-body-color);
  font-size: 0.9rem;
}

/* Was die Automatik nicht unterbringen konnte. Es liegt noch im Browser und wartet darauf, dass
   ein Mensch die Reihe nennt. Weggeworfen wird nichts. */
.uebrigbereich {
  padding: 0.75rem;
  border-radius: 10px;
  background-color: var(--cube-surface-2);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.uebrigstueck {
  flex: 0 0 auto;
  width: 140px;
}

.uebrigstueck img {
  width: 100%;
  height: 78px;
  object-fit: contain;
  border-radius: 8px;
  background-color: #fff;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  display: block;
}

.uebrigname {
  font-size: 0.7rem;
  color: var(--bs-tertiary-color);
  margin: 0.3rem 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Der Vorrat einer Reihe. Waechst er, wird gescrollt, statt die Zeile umzubrechen. */
.kachelstreifen {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding-bottom: 0.35rem;
}

.kachel {
  flex: 0 0 auto;
  width: 78px;
  height: 54px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  overflow: hidden;
  background-color: #fff;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  cursor: pointer;
}

.kachel img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.kachel:hover {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}

.kachel.gewaehlt {
  box-shadow: 0 0 0 2px var(--bs-primary);
}

/* Die erste Kachel ist keine Auswahl, sondern der Weg, etwas Neues hineinzulegen. */
.kachel-neu {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--cube-surface-2);
  color: var(--bs-secondary-color);
  margin-bottom: 0;
}

.kachel-neu:hover {
  color: var(--bs-body-color);
  background-color: var(--cube-surface-3);
}

/* --- Die Eventauswahl im Reiter der Box ---------------------------------------------------- */

/* Kacheln und kein Auswahlmenue. Am 30.08.2026 verlangt: "es soll dort auch die auswahl
   verschwinden mit dem dropdown." Ein Event unterscheidet sich fuer das Auge am Rahmen, und ein
   Menue zeigt Namen. */
.eventwahl {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.eventkachel {
  width: 190px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  overflow: hidden;
  background-color: var(--cube-surface-2);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.eventkachel img {
  display: block;
  width: 100%;
  height: 107px;
  object-fit: cover;
  background-color: var(--cube-surface-3);
}

.eventkachel-name {
  display: block;
  padding: 8px 10px;
  font-size: .8125rem;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.eventkachel:hover {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
  background-color: var(--cube-surface-3);
}

.eventkachel:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

/* Eine gesperrte Box laesst sich nicht bespielen. Der Knopf sagt es, statt ins Leere zu fuehren. */
.eventkachel:disabled {
  opacity: .5;
  cursor: default;
}

/* --- Das Bild eines Events in der Liste -------------------------------------------------- */

/* Nicht jedes Event hat einen Hintergrund. Am 30.08.2026 verlangt: "events die kein bild
   verfuegbar haben brauchen eine platzhaltericon in der event ansicht." Vorher stand dort das
   kaputte Bildsymbol des Browsers, und das sieht nach einem Fehler aus. */
.eventbild {
  flex: 0 0 auto;
  width: 64px;
  height: 40px;
  object-fit: cover;
}

.eventbild-leer {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--cube-surface-2);
  color: var(--bs-tertiary-color);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

/* --- Engere Tabellen ---------------------------------------------------------------------- */

/* Die Vorgabe von 12 px oben und unten ist fuer eine Uebersicht mit fuenf Zeilen zu viel Luft.
   Am 26.08.2026 beanstandet: "der bestand hat zu grosse abstaende". */
.card .table-eng > tbody > tr > td {
  padding-top: 5px;
  padding-bottom: 5px;
}

.card .table-eng > thead > tr > th {
  padding-top: 5px;
  padding-bottom: 5px;
}

/* --- Die aktive Leitung ------------------------------------------------------------------- */

/* Welche Leitung gerade traegt, ist die Auskunft der Karte und steht deshalb gross oben, nicht
   als ein Feld unter vielen. */
.aktive-leitung {
  display: flex;
  align-items: center;
  padding: 0.6rem 0.9rem;
  border-radius: 10px;
  background-color: var(--cube-surface-2);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

/* --- Kleinere Galeriekachel --------------------------------------------------------------- */

.kachelbild-klein {
  height: 84px;
}

/* --- Galerie: was liegt wo -----------------------------------------------------------------
   Die Galerie zeigt jede Aufnahme der Box, auch die ohne Vorschau hier. Wo das Bild fehlt,
   steht eine Flaeche mit dem Ladeknopf statt einer Luecke. */
.kachel-ohne {
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
  background-color: var(--cube-surface-2);
}

.kachelzeile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .25rem;
  margin-top: .2rem;
}

.kachelname {
  font-size: .72rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Ein Stueck einer Aufnahme in einem von drei Zustaenden. Das Symbol bleibt dasselbe, nur die
   Deutlichkeit wechselt: im Portal ist es da, auf der Box ist es holbar, sonst gibt es nichts. */
.stueck {
  display: inline-flex;
  align-items: center;
  font-size: .72rem;
  line-height: 1;
  padding: .15rem;
  border-radius: 4px;
  text-decoration: none;
}

.stueck-hier {
  color: var(--bs-success);
}

.stueck-box {
  color: var(--bs-secondary-color);
}

.stueck-fehlt {
  color: var(--bs-secondary-color);
  opacity: .35;
}

/* Die Oberflaeche der Box in einem Rahmen. Feste Hoehe statt automatisch: der Rahmen liegt auf
   einer fremden Herkunft, seine Hoehe ist von hier aus nicht auszulesen. 78vh laesst die
   Reiterleiste stehen und fuellt den Rest. */
.oberflaechenrahmen {
  position: relative;
  height: 78vh;
  min-height: 420px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--cube-field);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.oberflaechenrahmen iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Die Sitzung auf der Box. Etwas niedriger als die Oberflaeche: ein Terminal braucht Zeilen und
   keine Bildflaeche, und darunter steht noch, was zu tun ist, wenn es nicht traegt. Der Grund ist
   schwarz und nicht durchscheinend, ttyd zeichnet auf eigenem Grund. */
.terminalrahmen {
  position: relative;
  height: 62vh;
  min-height: 360px;
  border-radius: 10px;
  overflow: hidden;
  background: #000;
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.terminalrahmen iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* --- Wann die Box im Einsatz war ---------------------------------------------------------------

   Am 29.08.2026 beauftragt: "hebe die nutzungzeit normailiert / bereinigt hervor." Gerechnet wird
   sie aus den Aufnahmen und nicht aus der Erreichbarkeit, siehe analytics.nutzungszeit. Die
   Zeilen stehen ueber dem Diagramm und sagen in Worten, was die Balken darunter zeigen. */
.einsatzband {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.einsatzband-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem 0.25rem;
  padding: 0.4rem 0.75rem;
  border-radius: 8px;
  font-size: 0.9375rem;
  background: var(--cube-surface-2);
  box-shadow: inset 0 0 0 1px var(--cube-ring-soft);
}

/* Der laufende Einsatz hebt sich ab. Ohne diese Marke saehe die Endzeit wie ein Abschluss aus,
   dabei ist sie nur die letzte Aufnahme. */
.einsatzband-jetzt {
  background: linear-gradient(90deg, rgba(48, 164, 108, 0.22), var(--cube-surface-2) 60%);
  box-shadow: inset 0 0 0 1px rgba(48, 164, 108, 0.4);
}

/* Ausgabe eines Befehls im Hinweis nach dem Klick. Am 29.08.2026 beanstandet: "befehl auf der box
   ausfuehren ist nutzlos wenn die antwort als unformatierter hinweis kommt." Die Ausgabe von
   `ip r` oder `systemctl status` lebt von ihren Spalten. */
.ausgabeblock {
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 22rem;
  overflow: auto;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  font-size: 0.8125rem;
  background: rgba(0, 0, 0, 0.28);
}

/* --- Der Weg eines Bildes: Box, Uebertragung, Cloud --------------------------------------------

   Drei Bloecke nebeneinander, der mittlere traegt die Knoepfe. Am 30.08.2026 standen zwischen
   ihnen kurzzeitig Pfeile, und die waren falsch: sie zeigten auf eine Zwischenstation, die es
   nicht gibt. Die Bilder gehen durch den Dienst hindurch in die Cloud, er haelt sie nicht. */
.bilderweg {
  display: flex;
  align-items: stretch;
  gap: 0.75rem;
}

.bilderweg-block {
  flex: 1 1 0;
  min-width: 0;
}

/* Unter 1200 Bildpunkten stehen die drei untereinander. */
@media (max-width: 1199.98px) {
  .bilderweg {
    flex-direction: column;
  }

}

/* Eine Ueberschrift innerhalb einer Karte, gebaut wie ein Kopfband, nur ohne eigene Flaeche.
   Sie traegt einen Abschnitt, der zur selben Sache gehoert und trotzdem eine eigene Marke und
   einen eigenen Knopf hat. Am 30.08.2026 fuer den Sicherungsstick verlangt: "der sicherungsstick
   sollte in den selbem block sein, am besten mit einer linie getrennt." */
.blockkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

/* --- Das Vorschaubild in der Aufnahmetabelle ---------------------------------------------------

   Am 30.08.2026 verlangt: "die kleinen bilder kommen einfach links in die tabelle, so das die
   bilder einfach untereinenader angezeigt werden." Eine feste Hoehe haelt die Zeilen gleich hoch,
   `contain` zeigt ein Hochformat vollstaendig statt es zu beschneiden. */
.zeilenbild {
  width: 4.5rem;
  height: 3rem;
  object-fit: contain;
  background-color: rgba(0, 0, 0, 0.35);
  box-shadow: inset 0 0 0 1px var(--cube-ring);
}

.zeilenbild-ohne {
  background: var(--cube-field);
}

/* --- Kontrollbilder in der Steuerung -----------------------------------------------------------

   Zwei Blicke nebeneinander, und sie zeigen Verschiedenes: das Livebild kommt aus dem
   Vorschaustrom, das Kamerabild durch das Objektiv der Spiegelreflexkamera. Jedes traegt ein
   Kreuz, mit dem es wieder verschwindet. Am 30.08.2026 verlangt. */
.kontrollbilder {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.75rem;
}

.kontrollbild {
  position: relative;
  min-width: 0;
}

/* Das Kreuz liegt in der Ecke des Bildes und nicht daneben. Daneben waere es eine zweite Zeile,
   und die Karte ist ohnehin die vollste der Uebersicht. */
.kontrollbild-weg {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  margin: 0;
  border-radius: 6px;
  padding: 0.15rem;
  background: rgba(20, 20, 22, 0.72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  line-height: 0;
}

/* Ein Knopf in der Ecke einer Kopfzeile: nur das Zeichen, quadratisch, in der Hoehe einer Marke.
   Am 30.08.2026 fuer den Drucker verlangt, gilt fuer jede Karte, die sich auffrischen laesst. */
.kopfknopf {
  --bs-btn-padding-y: 0.15rem;
  --bs-btn-padding-x: 0.4rem;
  line-height: 1;
}

/* --- Rueckmeldung in der Ecke ------------------------------------------------------------------

   Am 30.08.2026 verlangt: "erfolgreiche aktionen sollen direkt mit timeout als overlay toast in
   der ecke angezeigt werden." Vorher stand jede Rueckmeldung als Banner ueber der Seite und schob
   den Inhalt nach unten. Nach drei Klicks war der obere Bildschirmrand voll Text, und der Reiter,
   an dem gearbeitet wurde, lag ausserhalb des Bildes.

   Geglueckt geht von selbst weg, fehlgeschlagen bleibt stehen. Ein Fehler, der nach fuenf Sekunden
   verschwindet, ist ein Fehler, den niemand gelesen hat. */
.toastecke {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 1090;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
  max-width: min(92vw, 26rem);
  pointer-events: none;
}

.hinweistoast {
  pointer-events: auto;
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.7rem 0.85rem;
  border-radius: 10px;
  font-size: 0.9375rem;
  background: rgba(30, 30, 34, 0.96);
  -webkit-backdrop-filter: var(--cube-blur);
  backdrop-filter: var(--cube-blur);
  box-shadow: 0 0 0 1px var(--cube-ring), 0 12px 32px -12px rgba(0, 0, 0, 0.85);
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.hinweistoast.sichtbar {
  opacity: 1;
  transform: none;
}

.hinweistoast-gut {
  box-shadow: inset 0 0 0 1px var(--ton-gruen-rand),
    0 12px 32px -12px rgba(0, 0, 0, 0.85);
}

.hinweistoast-gut .hinweistoast-symbol { color: var(--ton-gruen-schrift); }

.hinweistoast-schlecht {
  box-shadow: inset 0 0 0 1px var(--ton-rot-rand),
    0 12px 32px -12px rgba(0, 0, 0, 0.85);
}

.hinweistoast-schlecht .hinweistoast-symbol { color: var(--ton-rot-schrift); }

.hinweistoast-text {
  flex: 1 1 auto;
  min-width: 0;
  word-break: break-word;
}

.hinweistoast .btn-close {
  flex: 0 0 auto;
  margin-top: 0.15rem;
}

/* Eine mehrzeilige Befehlsausgabe passt nicht in eine Ecke. Sie bleibt lesbar und bekommt eine
   eigene Rinne, statt den Toast ueber den halben Bildschirm zu ziehen. */
.hinweistoast .ausgabeblock {
  max-height: 12rem;
  font-size: 0.75rem;
}

/* Laeuft gerade ein Befehl, steht die Warteleiste am unteren Rand. Die Toasts ruecken darueber,
   statt sich mit ihr zu ueberlagern. Auf einem breiten Bildschirm liegt die Leiste mittig und
   reicht bis in die rechte Haelfte, die beiden treffen sich also wirklich. */
body:has(#laeuft-leiste:not(.d-none)) .toastecke {
  bottom: 5.5rem;
}

@media (max-width: 575.98px) {
  .toastecke {
    left: 0.75rem;
    right: 0.75rem;
    bottom: 0.75rem;
    max-width: none;
  }

  body:has(#laeuft-leiste:not(.d-none)) .toastecke {
    bottom: 5rem;
  }
}

/* --- Der Kopf einer Seite --------------------------------------------------------------------

   Links der Name und sein Zustand, darunter die Beiwerte, rechts die Knoepfe. Bis zum 29.08.2026
   baute jede Seite das mit eigenen Bootstrap-Klassen nach, und auf einem Telefon kam bei jeder
   etwas anderes heraus. Auf der Boxseite stand der Zahnradknopf allein auf einer dritten Zeile.

   Eine Klasse fuer alle. Sie legt die Knoepfe auf schmalen Bildschirmen neben den Titel statt
   darunter, denn sie sind schmal und der Titel ist es nicht. */
.seitenkopf {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem 1rem;
  margin-bottom: 1rem;
}

.seitenkopf-name {
  min-width: 0;
  flex: 1 1 auto;
}

.seitenkopf-name h1 {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

/* Die Beiwerte unter dem Namen. Dieselbe Reihe mit Trennpunkt wie in der Boxenliste. */
.seitenkopf-lage {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem 0.5rem;
  min-width: 0;
  font-size: 0.9375rem;
  color: var(--bs-secondary-color);
}

/* Der Trennpunkt der Reihe steht bei der Boxenliste, beide teilen ihn sich. */

.seitenkopf-knoepfe {
  display: flex;
  flex: 0 0 auto;
  gap: 0.5rem;
}

/* Der mitlaufende Zaehler bricht nicht um. "vor 1 min 9 s" auf zwei Zeilen mit einem einzelnen
   "s" unten sah aus wie ein Fehler. */
.frischezaehler {
  white-space: nowrap;
}

/* --- Schmale Bildschirme ---------------------------------------------------------------------

   Am 29.08.2026 beauftragt: "optimiere die mobile ansicht des portals." Nachgesehen wurde bei
   375 Bildpunkten, der Breite eines iPhone im Hochformat. Was dabei auffiel, steht hier, jeweils
   mit dem Befund davor.

   Der Ort dafuer ist diese eine Stelle und nicht jede Vorlage. Eine Regel, die an einer Vorlage
   haengt, gilt fuer die naechste nicht mehr, und dann sieht wieder jede Seite anders aus. */

@media (max-width: 767.98px) {

  /* **Die Reiter der Boxseite brachen auf drei Zeilen um.** Acht Reiter zu je rund 130
     Bildpunkten passen nicht in 375, und umgebrochen frassen sie 220 Bildpunkte, bevor der erste
     Inhalt kam. Sie stehen jetzt in einer Reihe, die gewischt wird.

     Der Balken bleibt verborgen. Der Hinweis darauf, dass es weitergeht, ist der angeschnittene
     Reiter an der Kante, und der ist deutlicher als ein zwei Bildpunkte hoher Strich. */
  .nav-tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    max-width: 100%;
    scrollbar-width: none;
  }

  .nav-tabs::-webkit-scrollbar {
    display: none;
  }

  .nav-tabs .nav-link {
    white-space: nowrap;
  }

  /* **Tabellen mit vielen Spalten.** Sie werden gewischt, siehe oben. Enger gesetzt passt eine
     Spalte mehr ins Bild, bevor ueberhaupt gewischt werden muss. */
  .card .table > tbody > tr > td,
  .card .table > thead > tr > th {
    padding-left: 8px;
    padding-right: 8px;
  }

  .card .table > tbody > tr > td {
    font-size: 0.8125rem;
  }

  /* **Eine Tabelle mit einer langen Textspalte ist auf einem Telefon keine Tabelle.**
     Im Portalprotokoll standen vier Spalten, und die vierte trug den eigentlichen Satz. Bei 375
     Bildpunkten lag genau die ausserhalb des Bildes, waehrend die Zeile ihre Hoehe trotzdem
     danach bemass. Sichtbar waren drei kurze Angaben und viel Leere.

     Eine Zeile wird deshalb zur Karte. Die Kopfzeile faellt weg, die kurzen Angaben stehen
     nebeneinander, und was mit data-breit ausgezeichnet ist, bekommt eine eigene Zeile darunter.
     Beschriftungen braucht es dafuer keine: eine Uhrzeit, eine Marke und ein Boxname sagen von
     selbst, was sie sind.

     Zahlenraster wie in der Statistik bleiben Tabellen. Dort traegt die Kopfzeile die Bedeutung,
     und ohne sie waeren die Zahlen wertlos. Die werden gewischt, siehe oben. */
  .table-stapelbar > thead {
    display: none;
  }

  .table-stapelbar > tbody > tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem 0.75rem;
    padding: 0.7rem 0.75rem;
    box-shadow: inset 0 -1px 0 var(--cube-ring-soft);
  }

  .table-stapelbar > tbody > tr:last-child {
    box-shadow: none;
  }

  .card .table-stapelbar > tbody > tr > td {
    display: block;
    width: auto;
    border: 0;
    padding: 0;
  }

  .card .table-stapelbar > tbody > tr > td[data-breit] {
    flex: 1 1 100%;
  }

  /* **Der Name eines Events war auf drei Silben zusammengeschnitten.** Er stand neben dem
     Freigabeblock, und der brauchte 180 der 375 Bildpunkte fuer sich. Untereinander bekommt
     jeder, was er braucht. */
  .eventzeile {
    flex-wrap: wrap;
  }

  .eventzeile-freigabe {
    flex: 1 1 100%;
    text-align: left !important;
  }

  /* Abschneiden hat auf einer Seite ohne Nachbarspalte keinen Zweck mehr. Was nicht in die Zeile
     passt, bricht um, statt hinter drei Punkten zu verschwinden. */
  .eventzeile .text-truncate {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
  }

  /* **Der Ladehinweis lag ueber dem Knopf fuer die Navigation.** Beide sitzen fest an der oberen
     rechten Ecke. Unten ist er ohnehin naeher am Daumen. */
  .ladezeichen {
    top: auto;
    bottom: 1rem;
    right: 50%;
    transform: translateX(50%);
  }

  /* **Fingerbreite.** Ein Knopf von 28 Bildpunkten Hoehe trifft man auf einem Touchscreen nur
     mit Glueck. Die Empfehlung liegt bei 44, das sprengt hier die Zeilen, 38 sind der Kompromiss
     zwischen Treffsicherheit und einer Karte, die noch aufs Bild passt. */
  .btn-sm {
    --bs-btn-padding-y: 0.375rem;
    --bs-btn-padding-x: 0.75rem;
    min-height: 2.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* **Die Oberflaeche der Box in einem Rahmen.** 78 Prozent der Fensterhoehe sind auf einem
     Telefon fast die ganze Seite, und darueber steht schon der Kopf. Etwas weniger laesst
     erkennen, dass die Seite weitergeht. */
  .oberflaechenrahmen {
    height: 62vh;
    min-height: 320px;
  }

  .terminalrahmen {
    height: 55vh;
    min-height: 280px;
  }

  /* **Der Wartehinweis stand fast am Rand.** Auf einem schmalen Bildschirm bleibt er innerhalb
     der Seite und wird nicht breiter als sie. */
  .laeuft-leiste {
    left: 0.75rem;
    right: 0.75rem;
    transform: none;
    max-width: none;
  }
}

/* Auf einem Bildschirm, der mit dem Finger bedient wird, bekommen auch die kleinen Ziele in den
   Kacheln der Galerie Platz. Gemessen wird am Zeigegeraet und nicht an der Breite: ein Tablet im
   Querformat ist breit und wird trotzdem angetippt. */
@media (pointer: coarse) {
  .stueck {
    padding: 0.4rem;
  }

  .kachelzeile {
    gap: 0.5rem;
  }
}

/* Die Ablage fuer die Bilder eines Events. Bewusst eine eigene Klasse und nicht [data-ablage]:
   bildteile.js greift sich mit document.querySelector("[data-ablage]") das erste Element dieser
   Art, und die Bildteile-Karte steht auf derselben Seite weiter oben. Zwei Ablagen mit demselben
   Merkmal haetten sich gegenseitig die Ereignisse weggenommen. */
.ablage {
  transition: box-shadow 0.12s ease, background-color 0.12s ease;
}

.ablage.ablage-offen {
  background-color: var(--cube-surface-2);
  box-shadow: inset 0 0 0 2px var(--bs-primary);
}
