/* poppins-latin-400-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/assets/poppins-latin-400-normal-cpxAROuN.woff2) format('woff2'), url(/assets/poppins-latin-400-normal-BOb3E3N0.woff) format('woff');
}/* poppins-latin-500-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 500;
  src: url(/assets/poppins-latin-500-normal-C8OXljZJ.woff2) format('woff2'), url(/assets/poppins-latin-500-normal-DGXqpDMm.woff) format('woff');
}/* poppins-latin-600-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 600;
  src: url(/assets/poppins-latin-600-normal-zEkxB9Mr.woff2) format('woff2'), url(/assets/poppins-latin-600-normal-BJdTmd5m.woff) format('woff');
}/* poppins-latin-700-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 700;
  src: url(/assets/poppins-latin-700-normal-Qrb0O0WB.woff2) format('woff2'), url(/assets/poppins-latin-700-normal-BVuQR_eA.woff) format('woff');
}/* poppins-latin-ext-400-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(/assets/poppins-latin-ext-400-normal-by3JarPu.woff2) format('woff2'), url(/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff) format('woff');
}/* poppins-latin-ext-500-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 500;
  src: url(/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2) format('woff2'), url(/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff) format('woff');
}/* poppins-latin-ext-600-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 600;
  src: url(/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2) format('woff2'), url(/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff) format('woff');
}/* poppins-latin-ext-700-normal */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-display: swap;
  font-weight: 700;
  src: url(/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2) format('woff2'), url(/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff) format('woff');
}@font-face {
  font-family: "Material Icons";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("/assets/material-icons-kAwBdRge.woff2") format("woff2"), url("/assets/material-icons-Dr0goTwe.woff") format("woff");
}
.material-icons {
  font-family: "Material Icons";
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "liga";
}
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

/* Poppins statt Roboto, durchgehend. MDC leitet fast jede Schriftangabe ueber
   --mdc-typography-font-family; die drei Ausreisser unten lesen stattdessen ihre eigene
   Variable und blieben sonst auf dem Roboto-Rueckfall stehen, der gar nicht mehr geladen wird. */
:root {
  --mdc-typography-font-family: 'Poppins', sans-serif;
  --mdc-banner-supporting-text-font: 'Poppins', sans-serif;
  --mdc-form-field-label-text-font: 'Poppins', sans-serif;
  --mdc-slider-label-label-text-font: 'Poppins', sans-serif;
}

body {
  font-family: 'Poppins', sans-serif;
  background-color: var(--mdc-theme-background, #f5f5f5);
  color: var(--mdc-theme-on-surface, #1a1a1a);
}

#app {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* The wrapper of every page behind the login - the six that sit in the shell; the auth pages do
   not use it. Same padding as the device list, so the content edge does not jump by 8 px when one
   switches pages.

   Left-aligned, not centred: with `margin: 0 auto` the cards drifted towards the middle on a wide
   screen while the list next door stayed left, and the page title in the bar pointed at nothing.
   The max-width stays - a two-column definition list is unreadable at 1600 px - it just stops
   growing instead of moving. */
.page {
  width: 100%;
  max-width: 1100px;
  margin: 0;
  padding: 22px 24px 34px;
  box-sizing: border-box;
}

/* Tabellen bekommen die ganze Breite, Lesbares nicht. Die 1100 px oben sind fuer Fliesstext,
   Formulare und zweispaltige Beschreibungslisten da - eine Tabelle dagegen hat Spalten, die den
   Platz brauchen, und ihre Zeilen bleiben auch bei 1900 px lesbar, weil jede Spalte fuer sich
   schmal ist. Geraeteliste und Ereignisse tragen das, alles andere nicht. */
.page--wide {
  max-width: none;
}

/* Compact (the shell sets the class from the store's breakpoint, see AppShell): 12 px at the sides
   instead of 24 - on 375 px the two gutters were 13 % of the screen. The tables scroll inside
   their own container regardless of width, so nothing here has to fit. */
.shell--compact .page {
  padding: 16px 12px 28px;
}

/* Die gestapelte Zeilenform. Keine der Listen hat unterhalb ihrer Zeilenbreite Platz fuer ihre
   Spalten - die Tabellen scrollten in ihrem Container, und in der Geraeteliste standen Zustand
   und Messwerte ausserhalb des Bildes. Zu schmal wird aus der Zeile ein Stapel: Marke links
   (bleibt), daneben zwei oder drei Zeilen, rechts die eine Zahl, die die Zeile traegt. Welche
   Zelle wohin geht, sagt jede Seite selbst ueber grid-template-areas - hier steht nur, was allen
   gemeinsam ist: kleinerer Spaltenabstand, kaum Zeilenabstand zwischen den gestapelten Zeilen,
   kein Mindestmass. Die Marke und das Zeilenmenue haengen oben, nicht mittig: Sie gehoeren zur
   ersten Zeile des Stapels.

   Die Klasse setzt die Liste selbst an ihrem .table-scroll (Aktion stackBelow), nach ihrer
   eigenen Breite und nicht nach dem Viewport: Mit offener Seitenleiste hat ein 1100-px-Fenster
   fuer die Geraeteliste 816 px, und ihre Zeilen brauchen 880 - gemeldet als ein Band von Breiten,
   in dem die Leiste noch eine Spalte war und die Liste seitlich scrollte. */
.table--stack .row {
  /* Kein min-width hier: Jede Seite setzt ihres in ihrer eigenen .row-Regel, und die gewinnt
     gegen diese bei gleicher Spezifitaet, weil sie spaeter im Bundle steht - gemessen, die Zeilen
     blieben 880 px breit. Die Seiten setzen das min-width: 0 deshalb selbst. */
  min-height: 0;
  column-gap: 12px;
  row-gap: 2px;
}

.table--stack .row--stack {
  align-items: start;
}

.table--stack .row--stack .mark {
  margin-top: 1px;
}

/* Am Telefon (Klasse der Huelle) ausserdem engere Polsterung, Zeilen und Gruppenzeilen zusammen. */
.shell--compact .row,
.shell--compact .row--head {
  padding: 10px 14px;
}

/* Touch targets on a phone. Named as a loose end when the narrow row form was built: the row
   menus are 26 to 30 px, which is right for a mouse and small for a thumb - the usual floor is
   44 px. Growing the button itself is not an option, it is what holds every list at 52 px (the
   48 px MDC default made the device rows 69). So the box stays and an ::after reaches past it;
   the extension stays inside the row, so it cannot swallow the row above or below. `.actions`
   is already positioned in every list, the pseudo-element anchors on the button itself. */
.shell--compact .actions__button,
.shell--compact .mdc-icon-button.row-menu {
  position: relative;
}

.shell--compact .actions__button::after,
.shell--compact .mdc-icon-button.row-menu::after {
  content: '';
  position: absolute;
  /* 26 + 2 x 9 = 44, and 30 + 2 x 7 = 44 for the device list's own button. */
  inset: -9px;
}

.shell--compact .mdc-icon-button.row-menu::after {
  inset: -7px;
}

.shell--compact .group {
  padding: 10px 14px 6px;
}

.spacer {
  flex: 1 1 auto;
}

/* ---- Listen: eine Handschrift fuer Geraeteliste, Ereignisse und Datenpunkte -------------------
   Filterzeile, Chip, Rahmen, Kopfzeile und Zeile stehen hier genau einmal. Vorher hatte jede der
   drei Seiten ihre eigene Kopie, und die drei waren gemessen auseinandergelaufen: 12 gegen 12,5 px
   am Chip, 13 gegen 13,5 px in der Zeile, Gap 14 gegen 16, Mindesthoehe 44 gegen 52, und zwei
   Farben fuer "ausgewaehlt". Was nur einmal steht, kann nicht auseinanderlaufen. Die Spalten
   bleiben Sache der Seite - sie sind das Einzige, was sich zwischen den Listen unterscheiden soll. */

.filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 18px;
}

.filters > * {
  white-space: nowrap;
}

/* **Die Beschriftung eines Bedienelements ist kein Text, den man mitnimmt.** Gemeldet, nachdem
   die Maus verrutscht ist: Ein Zug ueber die Chips markiert "Offene Stoerungen 2" blau, und ein
   Knopf, der sich wie ein Absatz verhaelt, sieht fuer einen Moment kaputt aus. Gilt fuer Chips,
   Segmentfelder und die Beschriftung vor einem Auswahlfeld - also fuer das, was eine Frage stellt.
   **Nicht fuer die Listen darunter**: Dort steht, was das Geraet liefert - ein Datenpunkt-
   Schluessel gehoert in die Zwischenablage, wenn jemand ihn in der Geraete-Oberflaeche sucht. Und
   nicht fuer `.segment__value`: Das ist ein Eingabefeld und traegt seinen Textcursor zu Recht. */
.chip,
.segment__item:not(.segment__value),
.filter-select__label {
  user-select: none;
}

/* **32 px, festgenagelt und nicht ausgerechnet.** Die Hoehe stand hier als 6 px Polsterung ueber
   und unter einer 12,5-px-Zeile - was 32 ergibt, solange die Schrift ihre Zeilenbox so hoch macht
   wie erwartet, und 33, sobald sie es nicht tut (gemeldet: Chips 33, Segment daneben 32). Das
   Auswahlfeld hat seine 32 als `height`, das Segment als `min-height` an seinen Feldern; der Chip
   war der einzige der drei, dessen Hoehe von der Schriftmetrik abhing. */
.chip {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  min-height: 32px;
  gap: 7px;
  padding: 0 13px;
  border: 1px solid var(--s4q-border-subtle);
  border-radius: 15px;
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--s4q-text-secondary);
  cursor: pointer;
  white-space: nowrap;
}

.chip:hover {
  background: var(--s4q-row-hover);
}

/* Ausgewaehlt ist Markenrot, nicht Stoerungsrot: ein gesetzter Filter ist keine Stoerung, und
   Stoerungsrot bleibt die eine Farbe, die nur eines bedeutet. Zwei der drei Seiten hatten das
   vertauscht. */
.chip--on {
  color: var(--s4q-accent-text);
  background: color-mix(in srgb, var(--mdc-theme-primary, #aa0000) 7%, transparent);
  border-color: color-mix(in srgb, var(--mdc-theme-primary, #aa0000) 45%, transparent);
}

.chip__count {
  font-variant-numeric: tabular-nums;
  color: var(--s4q-text-hint);
}

/* ---- Auswahlfeld in der Filterzeile -----------------------------------------------------------
   Ein nacktes <select> in der Handschrift der Chips daneben - kein MDC-Select, das waere 56 px
   hoch in einer 33 px hohen Zeile, und auf dem Telefon bekommt man so die Auswahl des Systems
   statt einer Menueflaeche.

   **32 px, und das ist die Hoehe von allem, was in diesem Portal in einer Reihe steht** - Chip,
   Auswahlfeld und Segment, nachgemessen. Gemeldet war die Datums-Auswahl in der aufgeklappten
   Zeile, die als einzige 42 px hoch war; drei Hoehen in einer Reihe lesen sich als drei
   unzusammenhaengende Bedienelemente.

   Es stand als `.grouping` scoped in DeviceList und war damit fuer jede andere Seite unsichtbar -
   die Zeitraum-Auswahl der Geraeteseite stand deshalb einmal voellig ungestylt da. Genau die Art
   Kopie, gegen die dieser Abschnitt gebaut ist.

   **Wann ein Select und wann Chips:** Chips fuer die zwei, drei Fragen, deren Antworten man
   nebeneinander sehen und mit einem Griff wechseln will - sie tragen ihre Zahl im Kopf. Ein
   Select, wo eine Auswahl aus einer laengeren, gleichrangigen Liste getroffen wird; fuenf Chips,
   von denen meistens drei auf 0 stehen, sind eine Zeile Laerm fuer eine Wahl, die selten faellt. */

.filter-select {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--s4q-text-secondary);
}

.filter-select__label {
  white-space: nowrap;
}

/* 32 px, gesetzt und nicht als Mindestmass: Ein `datetime-local` bringt seine eigene Innenhoehe
   mit und wurde damit 42 px hoch - neben zwei 32-px-Bloecken in derselben Zeile. `box-sizing`
   ausdruecklich, weil dieses Stylesheet keines global setzt. */
.filter-select__field {
  box-sizing: border-box;
  font: inherit;
  font-size: 12.5px;
  color: var(--mdc-theme-on-surface, #1b1b1b);
  background: var(--mdc-theme-surface, #fff);
  border: 1px solid var(--s4q-border-subtle);
  border-radius: 13px;
  padding: 0 8px;
  height: 32px;
  cursor: pointer;
}

.filter-select__field:hover {
  background: var(--s4q-fill-subtle);
}

.filter-select__field:focus-visible {
  outline: 2px solid var(--mdc-theme-primary, #aa0000);
  outline-offset: 1px;
}

/* Gesetzt heisst Markenrot, wie am Chip - sonst sieht eine Liste, die etwas verbirgt, aus wie eine
   vollstaendige. */
.filter-select__field--on {
  color: var(--s4q-accent-text);
  border-color: color-mix(in srgb, var(--mdc-theme-primary, #aa0000) 45%, transparent);
  background: color-mix(in srgb, var(--mdc-theme-primary, #aa0000) 7%, transparent);
}

/* ---- Segment: eine Ansicht umschalten, nicht filtern -------------------------------------------
   Vier sich ausschliessende Angebote, die *nichts speichern* - der Zeitraum eines Diagramms.
   Sie sahen als Chips genauso aus wie die Chips, mit denen daneben die Auswertung eines
   Datenpunkts gespeichert wird; zwei Bedeutungen in einer Form. Als ein zusammenhaengender Block
   mit Trennstrichen liest es sich als Schalter und nicht als Filterreihe, und es nimmt gut ein
   Drittel weniger Platz. */

.segment {
  display: inline-flex;
  box-sizing: border-box;
  min-height: 32px;
  flex: none;
  /* A safety net, not a layout: the five kinds fit on one line at the width the editor column
     has. Should a fallback font ever be wider, the block breaks into two rows instead of pushing
     the table into sideways scrolling. */
  flex-wrap: wrap;
  border: 1px solid var(--s4q-border-subtle);
  border-radius: 13px;
  overflow: hidden;
  background: var(--mdc-theme-surface, #fff);
}

/* Every item carries a top and a left line and is pulled a pixel up and left, so the first row's
   lines disappear under the frame and a second row - a phone, five kinds - gets a divider of its
   own instead of two items floating in a box.

   **32 px tall, like `.chip` and `.filter-select__field`.** Where they stand in one row - the
   window of the chart is a segment, a date field and a second segment - three heights within a
   hand's width read as three unrelated controls. */
.segment__item {
  display: inline-flex;
  align-items: center;
  /* Ausdruecklich: Knoepfe erben es vom Reset, ein <input> nicht - und ein Feld, das seine Hoehe
     auf den Inhalt rechnet, macht den Block einen Pixel hoeher als den daneben. */
  box-sizing: border-box;
  /* 31 plus der Rahmen des Kastens minus dem Pixel, um das die Felder hochgezogen sind: 32. */
  min-height: 31px;
  border: none;
  border-top: 1px solid var(--s4q-border-subtle);
  border-left: 1px solid var(--s4q-border-subtle);
  margin: -1px 0 0 -1px;
  background: none;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--s4q-text-secondary);
  padding: 0 8px;
  cursor: pointer;
  white-space: nowrap;
}

/* An item that is a glyph and not a word: a little tighter, because a chevron carries its own
   air, and the icon is sized down to the line the words sit on. */
.segment__item--icon {
  padding: 0 7px;
}

/* Ein Eingabefeld als Feld des Blocks: Der Zeitpunkt des Diagramms steht zwischen den Pfeilen, die
   ihn bewegen. Es bringt seinen eigenen Rahmen und seine eigene Fuellung mit - beides weg, den
   Rest erbt es von `.segment__item`. Kein `cursor: pointer`, man tippt hinein. */
.segment__value {
  background: none;
  color: inherit;
  cursor: text;
}

/* Genau eines von beiden ist markiert: das Feld, solange ein Zeitpunkt festgehalten ist, "Jetzt",
   solange das Fenster mitlaeuft. */
.segment__value--on {
  color: var(--s4q-accent-text);
}

.segment__item--icon .material-icons {
  font-size: 16px;
  line-height: 15px;
}

.segment__item:hover:not(:disabled) {
  background: var(--s4q-row-hover);
}

/* Spent, not hidden: a step that would leave the recording behind stays where it is, so the block
   does not change width under the hand. */
.segment__item:disabled {
  opacity: 0.4;
  cursor: default;
}

.segment__item--on {
  color: var(--s4q-accent-text);
  background: color-mix(in srgb, var(--mdc-theme-primary, #aa0000) 7%, transparent);
}

.segment__item:focus-visible {
  outline: 2px solid var(--mdc-theme-primary, #aa0000);
  outline-offset: -2px;
}

.chip--on .chip__count {
  color: inherit;
}

/* Der Rahmen einer Liste: dieselbe Flaeche und derselbe Schatten wie die SMUI-Card, die die
   Ereignis- und Datenpunkttabellen umgibt, nur ohne die Komponente. */
.table {
  background: var(--mdc-theme-surface, #fff);
  border-radius: 4px;
  box-shadow:
    0 2px 1px -1px rgba(0, 0, 0, 0.2),
    0 1px 1px 0 rgba(0, 0, 0, 0.14),
    0 1px 3px 0 rgba(0, 0, 0, 0.12);
}

/* **Die Hauptaktion der Kopfzeile**: flach und eckig. Der Entwurf kennt keinen Radius ausser an
   Chips und Kreisen, und ein erhabener Knopf waere der einzige Schatten der Seite. Die Umriss-Form
   daneben ist dieselbe Aktion eine Stufe leiser.

   **Steht hier und nicht in der Geraeteliste**, wo sie angefangen hat: Inzwischen benutzen sie
   drei Seiten (Geraete, Alarmierung, Ereignisse), und eine Regel, die in einer Route als
   `:global` steht, gehoert der Route nicht mehr. Dieselbe Ueberlegung wie bei `.row--device`. */
.bar-action,
.bar-action-outline {
  --mdc-typography-button-letter-spacing: 0.7px;
  --mdc-typography-button-font-size: 12.5px;
  flex: none;
  height: 38px;
}

.bar-action {
  --mdc-filled-button-container-shape: 0;
}

.bar-action-outline {
  --mdc-outlined-button-container-shape: 0;
}

/* **Das Raster der Geraetezeile - hier, weil zwei Bauteile es teilen.** Die Zeile selbst ist
   `DeviceRow.svelte`, ihr Spaltenkopf steht in der Geraeteliste, und seit die Zeile eine eigene
   Komponente ist, erreicht eine gekapselte Regel nie beide. Genau daran ist es beim Herausloesen
   gescheitert: Der Kopf verlor seine Spalten und stand als 140 px hoher Stapel da.

   Name links, Fakten rechts, die Luft dazwischen. Gemeldet: Bei drei gleich breiten Slots ueber
   411 px standen die Werte weit auseinander und wirkten "wild". Jetzt haben sie feste, schmale
   Slots und sitzen rechtsbuendig, der Ueberschuss faellt an die Namensspalte (bis 420 px) und in
   die Mitte - beide Bloecke sind verankert, statt dass einer mit der Fensterbreite wandert. */
.row--device {
  grid-template-columns: 26px minmax(170px, 1fr) 200px 348px 30px;
  min-width: 880px;
}

/* **Auf der Geraeteseite gibt die Namensspalte ganz nach** (`minmax(0, 1fr)` statt
   `minmax(170px, 1fr)`), und damit braucht die Zeile 708 statt 880 px. Das ist kein Sonderfall aus
   Bequemlichkeit: Der Name steht dort als Titel der Seite darueber, in voller Laenge - er ist in
   dieser Zeile das Einzige, was sich wiederholt, und deshalb das Erste, was nachgeben darf.

   **Und die leere Menuespalte faellt weg**, die letzten 46 px mit ihrem Abstand: In der Liste
   steht dort das Zeilenmenue, auf der Geraeteseite nichts.

   Gerechnet, nicht geraten: Die Inhaltsspalte misst an einem 1400-px-Fenster 794 px. Mit den 880
   der Liste waere die Zeile dort in ihre schmale Form gefallen - und die laesst genau die
   Messwerte weg, um derentwillen sie auf dieser Seite steht. So bleiben dem Namen dort 132 px, er
   schneidet erst bei laengeren Namen ab, und der ganze steht im Titel der Zeile und in der
   Kopfzeile darueber. */
.row--device.row--plain {
  grid-template-columns: 26px minmax(0, 1fr) fit-content(200px) 348px;
  min-width: 662px;
}

/* Gestapelt (die Klasse setzt stackBelow an .table-scroll): Marke, Name ueber Typ, Zustand, Menue -
   die Messwerte entfallen. Die Zeile beantwortet "muss ich hinsehen", und das tun Marke und
   Zustand; die Werte stehen ein Tippen weiter auf der Detailseite. */
.table--stack .row--device {
  grid-template-columns: 26px minmax(0, 1fr) auto 30px;
  min-width: 0;
}

/* Am Telefon dasselbe ohne die Menuespalte - die Zeile auf der Geraeteseite hat nur drei Zellen. */
.table--stack .row--device.row--plain {
  grid-template-columns: 26px minmax(0, 1fr) auto;
}

/* Breites scrollt in seinem eigenen Container, die Seite selbst nie seitwaerts. */
.table-scroll {
  overflow-x: auto;
}

/* Ein Raster fuer Kopf und Zeilen, damit keine Spalte zwischen beiden wandern kann; die Spalten
   setzt die Seite, und die erste ist ueberall die 26 px breite Spalte der Marke (.mark). Die
   Zuruecksetzung von border, background und font ist fuer Zeilen, die ein <button> sind
   (Datenpunkte). */
.row {
  display: grid;
  align-items: center;
  gap: 16px;
  width: 100%;
  min-height: 52px;
  padding: 10px 20px;
  box-sizing: border-box;
  border: none;
  border-bottom: 1px solid var(--s4q-row-line);
  background: none;
  font: inherit;
  font-size: 13.5px;
  color: inherit;
  text-align: left;
}

.row--head {
  min-height: 0;
  padding: 12px 20px;
  border-bottom-color: var(--s4q-head-line);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  color: var(--s4q-text-secondary);
}

/* Einzeilige Zellen (Ereignisse) brauchen die 52 px nicht, die zweizeilige (Geraete, Datenpunkte)
   brauchen - die eine Abweichung mit Grund. */
.row--single {
  min-height: 44px;
}

/* Die Wertspalte, in allen drei Listen dieselbe: eine Zahl, die kein Wort ist und trotzdem
   beliebig lang werden kann. Gemeldet an `var_hs_uptime`, einem Unix-Zeitstempel
   ("1789233225.56687", 16 Zeichen): Der Wert lief aus seiner 110-px-Spalte heraus, weil `nowrap`
   allein nur das Umbrechen verbietet und nichts abschneidet.

   **`min-width: 0` ist die Haelfte, die man vergisst.** Ein Raster-Element hat `min-width: auto`,
   also seine Mindestinhaltsbreite - und die schlaegt die feste Spaltenbreite. Ohne die Zeile
   greift die Ellipse nie. Dieselbe Falle, die die Messwert-Kaestchen der Geraeteliste schon
   einmal gestellt haben.

   Gekuerzt und nicht kleiner gesetzt: Eine Ziffernfolge, die sich der Spalte anpasst, waere in
   der einen Zeile 13,5 px und in der naechsten 9. Der ganze Wert steht im `title`. */
.cell-value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.row:not(.row--head):hover {
  background: var(--s4q-row-hover);
}

.row:last-child {
  border-bottom: none;
}

/* Die Gruppenzeile in der Tabelle - das Band der Geraeteliste, der Tag der Ereignisse: eine Zeile
   im selben Raster-Container, keine eigene Tabelle je Gruppe. So bleibt ein Kopf fuer alles, die
   Spalten laufen ueber die Gruppen hinweg durch, und die Zeile ist beim Scrollen da, wo die
   Eintraege sind. Ohne Modifikator neutral (Tage); die Baender bringen ihre Farbe mit. */
.group {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 12px 20px 8px;
  border-bottom: 1px solid var(--s4q-row-line);
  background: var(--s4q-group-fill);
}

/* Die Gruppenzeile ist die erste Zeile ihrer Box - ohne die Rundung oben stiesse ihre Faerbung
   ueber die Ecken der Karte hinaus. Steht hier und nicht in der Geraeteliste, seit die beiden
   Geraeteseiten dieselbe Huelle um ihre eine Zeile legen. */
.table--group .group {
  border-radius: 4px 4px 0 0;
}

.group__title {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--s4q-text-secondary);
}

.group__meta {
  font-size: 11.5px;
  color: var(--s4q-text-secondary);
}

.group--fault {
  background: var(--s4q-fault-fill);
}

.group--fault .group__title {
  color: var(--s4q-fault);
}

.group--attention {
  background: var(--s4q-attention-fill);
}

.group--attention .group__title {
  color: var(--s4q-warning);
}

.group--quiet {
  background: var(--s4q-ok-fill);
}

.group--quiet .group__title {
  color: var(--s4q-success);
}

.group--idle {
  background: var(--s4q-idle-fill);
}

/* Die Marke: ein Kreis, dessen Fuellung und Glyphe der Ton der Zeile bestimmt (row--fault usw.).
   Form und nicht nur Farbe - Ausrufezeichen, Haekchen und leerer Ring sind drei verschiedene
   Umrisse, ohne das waere die Zeile fuer rot-gruen-blinde Leser zustandslos. Der leere Ring ist
   der Grundzustand "keine Aussage": offline, nicht zugeordnet, nicht geliefert, ohne Schwelle.
   Zuvor stand hier ein 3-px-Balken am linken Rand; der Kreis war der Wunsch des Anwenders. */
.mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  box-sizing: border-box;
  border-radius: 50%;
  border: 2px solid var(--s4q-idle);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
}

.row--neutral .mark {
  border-color: var(--s4q-border-subtle);
}

.row--fault .mark {
  background: var(--s4q-fault);
  border-color: var(--s4q-fault);
}

.row--attention .mark {
  background: var(--s4q-attention);
  border-color: var(--s4q-attention);
}

.row--ok .mark {
  background: var(--s4q-success);
  border-color: var(--s4q-success);
  font-size: 11px;
}

  /* Lives in the drawer only, so it takes the drawer's tone - `--mdc-theme-on-primary` was the
     top bar it used to sit in. */
  .account-menu.svelte-1mzjx0 {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
    color: var(--s4q-drawer-text-strong);
  }

  /* Deliberately the same numbers as `.org-trigger` in OrgSwitcher - change one, change the other.
     A shared class would have to live outside both components' scoped styles; the file already
     cross-references that way ("See AccountMenu") and two rules are cheaper than a third file. */
  .account-trigger.svelte-1mzjx0 {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-width: 0;
    background: none;
    border: none;
    color: inherit;
    font-family: inherit;
    text-align: left;
    /* Fixed, not content-driven, and that is the whole point: expanded the two-line label block is
       30,6 px and taller than the 26 px glyph that stands alone when collapsed. The block therefore
       grew by 4,6 px on every expand and pushed the navigation below it down - the vertical half of
       the wobble. The rest of the drawer has worked this way all along (`.drawer__head` 64 px,
       `.drawer__entry` 44 px); these two were the only ones sized by their content.
       42 px = the label block plus the 5 px it used to carry as padding, rounded up. */
    height: 42px;
    padding: 0 6px;
    border-radius: 4px;
    cursor: pointer;
  }

  .account-menu--collapsed.svelte-1mzjx0 .account-trigger:where(.svelte-1mzjx0) {
    /* Deliberately *not* centred and deliberately the same padding as expanded: centring is a
       second way of placing the glyph, and two mechanisms are what made it jump by 2 px on every
       collapse. The 15 px on the wrapper already put it in the middle of the 68 px rail. */
    gap: 0;
  }

  .account-trigger.svelte-1mzjx0:hover {
    background: var(--s4q-drawer-hover);
  }

  /* 26 px wide like the organization badge above, so both rows start on the same x. */
  .account-glyph.svelte-1mzjx0 {
    flex: none;
    width: 26px;
    font-size: 24px;
    text-align: center;
    color: var(--s4q-drawer-text);
  }

  .account-labels.svelte-1mzjx0 {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
  }

  .account-labels__name.svelte-1mzjx0,
  .account-labels__email.svelte-1mzjx0 {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .account-labels__name.svelte-1mzjx0 {
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.25;
  }

  .account-labels__email.svelte-1mzjx0 {
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-drawer-text);
  }

  .menu-user.svelte-1mzjx0 {
    display: flex;
    flex-direction: column;
    padding: 8px 16px 10px;
    list-style: none;
  }

  .menu-user__name.svelte-1mzjx0 {
    font-size: 14px;
    font-weight: 500;
  }

  .menu-user__email.svelte-1mzjx0 {
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  .menu-label.svelte-1mzjx0 {
    padding: 8px 16px 2px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--s4q-text-secondary);
    list-style: none;
  }

  .menu-check.svelte-1mzjx0 {
    margin-left: auto;
    padding-left: 16px;
    font-size: 18px;
    opacity: 0.7;
  }

  /* MDC reserves 32px between icon and label - room for two-line items this menu
     does not have. The rows are single-line, so they may sit closer together. */
  .account-menu.svelte-1mzjx0 .mdc-deprecated-list-item__graphic {
    margin-right: 16px;
  }

  /* MDC paints the selected row in the primary color. On the dark surface the
     brand red lands at about 2:1 contrast, which would make the active entry the
     hardest one to read. Its tinted background and the check mark say what is
     selected; the label stays plain text. */
  .account-menu.svelte-1mzjx0 .mdc-deprecated-list-item--selected {
    color: var(--mdc-theme-on-surface, #000);
  }

  .org-switcher.svelte-wcx78p {
    position: relative;
    display: flex;
    align-items: center;
    color: var(--s4q-drawer-text-strong);
  }

  .org-trigger.svelte-wcx78p {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-width: 0;
    background: none;
    border: none;
    color: inherit;
    font-family: inherit;
    text-align: left;
    /* Fixed, not content-driven, and that is the whole point: expanded the two-line label block is
       30,6 px and taller than the 26 px glyph that stands alone when collapsed. The block therefore
       grew by 4,6 px on every expand and pushed the navigation below it down - the vertical half of
       the wobble. The rest of the drawer has worked this way all along (`.drawer__head` 64 px,
       `.drawer__entry` 44 px); these two were the only ones sized by their content.
       42 px = the label block plus the 5 px it used to carry as padding, rounded up. */
    height: 42px;
    padding: 0 6px;
    border-radius: 4px;
    cursor: pointer;
  }

  .org-switcher--collapsed.svelte-wcx78p .org-trigger:where(.svelte-wcx78p) {
    /* Deliberately *not* centred and deliberately the same padding as expanded: centring is a
       second way of placing the glyph, and two mechanisms are what made it jump by 2 px on every
       collapse. The 15 px on the wrapper already put it in the middle of the 68 px rail. */
    gap: 0;
  }

  .org-trigger.svelte-wcx78p:hover {
    background: var(--s4q-drawer-hover);
  }

  /* Nothing to switch to: no hover, no pointer. It still says which organization one is in, which
     is the other half of what this element is for. */
  .org-trigger--static.svelte-wcx78p,
  .org-trigger--static.svelte-wcx78p:hover {
    background: none;
    cursor: default;
  }

  /* The initial, not the brand mark: the mark sits 64 px above in the drawer head, and two red
     squares one under the other would read as one thing repeated. */
  .org-badge.svelte-wcx78p {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 4px;
    background: var(--s4q-drawer-active);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
  }

  /* min-width lets the name ellipsis do its job instead of pushing the caret out of the row. */
  .org-trigger__labels.svelte-wcx78p {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
  }

  .org-trigger__name.svelte-wcx78p {
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.25;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .org-trigger__role.svelte-wcx78p {
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-drawer-text);
    white-space: nowrap;
  }

  .org-trigger__caret.svelte-wcx78p {
    flex: none;
    font-size: 20px;
    color: var(--s4q-drawer-text);
  }

  .menu-check.svelte-wcx78p {
    margin-left: auto;
    padding-left: 16px;
    font-size: 18px;
    opacity: 0.7;
  }

  /* See AccountMenu: MDC's 32px between icon and label is meant for two-line items. */
  .org-switcher.svelte-wcx78p .mdc-deprecated-list-item__graphic {
    margin-right: 16px;
  }

  /* See AccountMenu: the active row keeps the normal text color, the brand red
     is unreadable on the dark surface. Tint and check mark mark it instead. */
  .org-switcher.svelte-wcx78p .mdc-deprecated-list-item--selected {
    color: var(--mdc-theme-on-surface, #000);
  }

  .shell.svelte-whg6dh {
    display: grid;
    grid-template-columns: 236px minmax(0, 1fr);
    min-height: 100vh;
  }

  .shell--collapsed.svelte-whg6dh {
    grid-template-columns: 68px minmax(0, 1fr);
  }

  /* Dark in both themes, like the top bar was red in both. Its tones therefore do not depend on
     the theme and are the same in either token file.

     Sticky and exactly one screen tall, not stretched over the page. Measured on the device list:
     the drawer was 1132 px high and `static`, so its foot - the account - sat at the bottom of the
     *page* rather than the screen and was out of sight until one scrolled. Two things follow from
     fixing that: the account is always reachable, and it stops moving when the drawer collapses.
     It moved because collapsing widens the content, the banner rewraps, the page gets 8 px shorter
     and the foot went with it - the last of the jumps, and the only one that never had anything to
     do with the drawer itself. */
  .drawer.svelte-whg6dh {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--s4q-drawer);
    overflow: hidden;
    /* **Die Leiste ist eine Spalte aus Bedienelementen, kein Text.** Dieselbe Meldung wie bei den
       Chips, hier nachgemessen: Ein Zug mit der Maus markierte "developer_board Geräte
       error_outline Offene Störungen ..." - samt der Ligatur-Namen der Symbole, die nur die
       Schrift kennt und niemand lesen soll. Nichts hier gehört in die Zwischenablage; was man
       kopieren will (die eigene Adresse, der Name der Organisation), steht auf den Seiten, zu
       denen die Einträge führen. */
    user-select: none;
  }

  /* **Der Kopf trägt die Farbe der Leiste, nicht die der Titelzeile daneben.** Einmal war es
     umgekehrt - gleiche Höhe, gleiches Kleben oben, also ein durchgehendes helles Band mit dem
     Logo links und dem Seitentitel rechts. Am Bildschirm hielt das nicht: ohne senkrechte Linie
     ging es nahtlos ineinander über, mit einer (0,08 Deckung) war sie zu schwach, um eine Spalte
     zu trennen, und kräftiger gezogen zerteilte sie genau das Band, dessen Sinn die
     Durchgängigkeit war. Dazu der harte Absatz auf die dunkle Schiene darunter - ein Muster, das
     es so kaum gibt: Ein Band über die ganze Breite haben Oberflächen mit *heller* Leiste, eine
     Leiste über volle Höhe trägt ihren Kopf in ihrer eigenen Farbe. Gemischt ist es keins von
     beidem. */
  .drawer__head.svelte-whg6dh {
    display: flex;
    align-items: center;
    gap: 12px;
    height: 64px;
    padding: 0 20px;
    border-bottom: 1px solid var(--s4q-drawer-line);
    box-sizing: border-box;
  }

  .shell--collapsed.svelte-whg6dh .drawer__head:where(.svelte-whg6dh) {
    justify-content: center;
    padding: 0;
  }

  /* 20 px statt der 13 px des alten Schriftzugs: Der trug nur Text, dieses Logo trägt die Marke
     mit, und auf 13 px wäre die "4" ein Fleck. Bei 7,12 : 1 misst es damit 142 px und liegt in den
     166 px, die zwischen der Polsterung und dem Einklapp-Knopf frei sind. */
  .drawer__wordmark.svelte-whg6dh {
    display: block;
    height: 20px;
    width: auto;
  }

  .drawer__head-spacer.svelte-whg6dh {
    flex: 1 1 auto;
  }

  /* A plain button, not an SMUI IconButton: the drawer is dark in both themes, so every MDC colour
     token would have to be reset here anyway - the same reason `.org-trigger` is one. */
  .drawer__toggle.svelte-whg6dh {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 30px;
    height: 30px;
    margin-right: -6px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--s4q-drawer-text);
    cursor: pointer;
  }

  .shell--collapsed.svelte-whg6dh .drawer__toggle:where(.svelte-whg6dh) {
    margin-right: 0;
  }

  .drawer__toggle.svelte-whg6dh:hover {
    background: var(--s4q-drawer-hover);
    color: var(--s4q-drawer-text-strong);
  }

  .drawer__toggle.svelte-whg6dh .material-icons:where(.svelte-whg6dh) {
    font-size: 20px;
  }

  /* 15 px, not guessed: with the trigger's own 6 px the 26 px badge starts at 21 and its label at
     57 - the same x as the nav entries' icons and labels below (3 px edge + 22 px padding). And
     collapsed it leaves 21 px on either side of the 68 px rail, so nothing has to move. */
  .drawer__org.svelte-whg6dh {
    padding: 8px 15px;
    border-bottom: 1px solid var(--s4q-drawer-line);
  }

  .drawer__nav.svelte-whg6dh {
    display: flex;
    flex-direction: column;
    padding-top: 8px;
    flex: 1 1 auto;
  }

  /* 22 px, and the number is arithmetic, not taste: with the 3 px edge the 18 px icon starts at 25
     and ends at 43, which leaves 25 px on the other side of the 68 px rail - centred when
     collapsed *and* unmoved when expanded. See "Ein Mechanismus fuer beide Zustaende". */
  .drawer__entry.svelte-whg6dh {
    position: relative;
    display: flex;
    align-items: center;
    gap: 14px;
    height: 44px;
    padding: 0 22px;
    box-sizing: border-box;
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 400;
    color: var(--s4q-drawer-text);
    /* The active marker is a left edge, so every entry carries the same transparent one - without
       it the label of the active entry would sit 3 px further right than its neighbours. */
    border-left: 3px solid transparent;
  }

  .drawer__entry.svelte-whg6dh:hover {
    background: var(--s4q-drawer-hover);
  }

  /* Dimmed and without the hover, so it reads as inactive before anyone clicks. `cursor: default`
     rather than `not-allowed` - nothing is forbidden here, it just does not exist yet. */
  .drawer__entry--pending.svelte-whg6dh {
    opacity: 0.45;
    cursor: default;
  }

  .drawer__entry--pending.svelte-whg6dh:hover {
    background: none;
  }

  /* **Beide Plaetze, nicht nur die Navigation.** Die Regel hing an `.drawer__nav`, und mit dem
     Umzug der Hilfe an den Fuss stand sie als einziger Eintrag ohne Hervorhebung da - gemessen
     identisch mit einem inaktiven. Der Eintrag ist derselbe, also gilt fuer ihn dasselbe; wo er
     haengt, ist eine Frage der Ordnung und keine der Form. */
  .drawer__nav.svelte-whg6dh a.is-active,
  .drawer__extra.svelte-whg6dh a.is-active {
    color: var(--s4q-drawer-text-strong);
    font-weight: 500;
    background: var(--s4q-drawer-active);
    border-left-color: var(--mdc-theme-primary, #aa0000);
  }

  .drawer__icon.svelte-whg6dh {
    font-size: 18px;
    width: 18px;
    text-align: center;
    flex: none;
  }

  .drawer__label.svelte-whg6dh {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .drawer__badge.svelte-whg6dh {
    flex: none;
    font-size: 11.5px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--s4q-drawer-badge);
  }

  /* Collapsed the number sits on the icon's top right corner and is filled rather than coloured
     text: at 68 px it overlaps the glyph, and `#ff8a80` as thin text on a dark icon would read as
     part of the icon. Filled it is unmistakably a count. Capped at "99+" - three digits would grow
     wider than the rail, and past a hundred the exact figure changes nothing about what to do. */
  .drawer__badge--pin.svelte-whg6dh {
    position: absolute;
    top: 6px;
    right: 11px;
    min-width: 9px;
    padding: 1px 4px;
    border-radius: 999px;
    background: var(--s4q-drawer-badge);
    color: var(--s4q-drawer);
    font-size: 10px;
    font-weight: 600;
    line-height: 1.3;
    text-align: center;
  }

  /* Same padding as `.drawer__org`, so glyph and label line up with everything above them. */
  /* Derselbe untere Abstand, den die Navigation oben hat: Der Eintrag soll nicht an der Linie des
     Fusses kleben. Keine eigene Linie darueber - zwei Striche in 50 px zerschneiden die Spalte,
     und getrennt werden soll hier nur das Portal vom Angemeldeten. */
  .drawer__extra.svelte-whg6dh {
    display: flex;
    flex-direction: column;
    padding-bottom: 8px;
  }

  .drawer__foot.svelte-whg6dh {
    display: flex;
    align-items: center;
    padding: 8px 15px;
    border-top: 1px solid var(--s4q-drawer-line);
  }

  .shell__main.svelte-whg6dh {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  .app-content.svelte-whg6dh {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Full width above the content, not a dismissible toast: the state persists until we lift it. */
  .org-suspended.svelte-whg6dh {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 16px;
    background: var(--s4q-fill-subtle);
    border-bottom: 1px solid var(--s4q-border-subtle);
    color: var(--s4q-warning);
    font-size: 14px;
    text-align: center;
  }

  .org-suspended.svelte-whg6dh .material-icons:where(.svelte-whg6dh) {
    font-size: 20px;
  }

  /* Compact: one column, and the drawer is taken out of the grid entirely. Fixed at the left edge,
     full height, its open width, and slid off-screen until asked for. No media query here - the
     class comes from the store's matchMedia, so what the CSS does and what the state says are one
     thing. A stylesheet-only breakpoint once forced 68 px onto a drawer whose state said "open",
     and it rendered its labels into the gap. */
  .shell--compact.svelte-whg6dh {
    grid-template-columns: minmax(0, 1fr);
  }

  .shell--compact.svelte-whg6dh .drawer:where(.svelte-whg6dh) {
    position: fixed;
    top: 0;
    left: 0;
    width: 236px;
    z-index: 5;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
  }

  .shell--overlay.svelte-whg6dh .drawer:where(.svelte-whg6dh) {
    transform: none;
    box-shadow: 0 0 24px rgba(0, 0, 0, 0.35);
  }

  /* Under the drawer, over everything else - the page bar is sticky at z-index 2 and would
     otherwise show through. */
  .scrim.svelte-whg6dh {
    position: fixed;
    inset: 0;
    /* The gesture stops here rather than reaching the page underneath. */
    overscroll-behavior: contain;
    z-index: 4;
    padding: 0;
    border: none;
    background: rgba(0, 0, 0, 0.4);
    cursor: default;
  }

  .toast--error .mdc-snackbar__surface {
    background-color: var(--s4q-danger);
  }

  .login-page.svelte-110obhz {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
  }

  .login-card {
    width: 100%;
    max-width: 380px;
  }

  /* The 2 px belonged to the subheading this once had - a line that reads as part of its heading.
     The wordmark is an object of its own and gets a real gap; still smaller than the 20 px below
     it, so heading and mark stay one group and the form is the next. */
  h1.svelte-110obhz {
    font-size: 22px;
    margin: 0 0 12px;
  }

  /* Only the height is set - the file carries its own proportions. Bigger than the 16 px in the
     top bar on purpose: there the mark shares a 48 px strip with the navigation, here it is what
     the card leads with. 24 px means 207 of the 348 px available inside the card, and letters
     about a sixth taller than the heading's capitals. */
  .login-wordmark.svelte-110obhz {
    display: block;
    height: 24px;
    width: auto;
    margin: 0 0 20px;
  }

  .login-form.svelte-110obhz {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .login-error.svelte-110obhz {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 0;
  }

  .login-forgot.svelte-110obhz {
    display: inline-block;
    margin-top: 20px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  .password-toggle {
    cursor: pointer;
    user-select: none;
  }


  .session-notice.svelte-15at428 {
    margin: 0 0 20px;
    color: var(--s4q-text-secondary);
    font-size: 13px;
  }

  .session-notice__actions.svelte-15at428 {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }

  .session-notice__error.svelte-15at428 {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }

  .invite-page.svelte-jk041 {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
  }

  .invite-card {
    width: 100%;
    max-width: 420px;
  }

  h1.svelte-jk041 {
    font-size: 22px;
    margin: 0 0 4px;
  }

  .invite-hint.svelte-jk041 {
    margin: 0 0 20px;
    color: var(--s4q-text-secondary);
    font-size: 13px;
  }

  .invite-form.svelte-jk041 {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .invite-error.svelte-jk041 {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 0;
  }

  .password-toggle {
    cursor: pointer;
    user-select: none;
  }

  .reset-page.svelte-1r5zizq {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
  }

  .reset-card {
    width: 100%;
    max-width: 420px;
  }

  h1.svelte-1r5zizq {
    font-size: 22px;
    margin: 0 0 4px;
  }

  .reset-hint.svelte-1r5zizq {
    margin: 0 0 20px;
    color: var(--s4q-text-secondary);
    font-size: 13px;
  }

  .reset-form.svelte-1r5zizq {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .reset-error.svelte-1r5zizq {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 0;
  }

  .reset-back.svelte-1r5zizq {
    display: inline-block;
    margin-top: 20px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  .password-toggle {
    cursor: pointer;
    user-select: none;
  }

  .dialog-subtitle.svelte-zudecy {
    display: block;
    font-size: 13px;
    font-weight: 400;
    color: var(--s4q-text-secondary);
    margin-top: 2px;
  }

  .credential.svelte-u51ufo + .credential:where(.svelte-u51ufo) {
    margin-top: 12px;
  }

  .credential__label.svelte-u51ufo {
    font-size: 12px;
    font-weight: 500;
    margin-bottom: 2px;
  }

  .credential__row.svelte-u51ufo {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--s4q-fill-subtle);
    border-radius: 8px;
    padding: 6px 6px 6px 14px;
  }

  .credential__value.svelte-u51ufo {
    flex: 1 1 auto;
    font-family: ui-monospace, Consolas, monospace;
    font-size: 14px;
    user-select: all;
    word-break: break-all;
  }

  .tags.svelte-1kw1j3q {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  /* The label of a field that is not a text field - same size and tone as an MDC floating label
     at rest, so it lines up with the fields above and below. */
  .tags__label.svelte-1kw1j3q {
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  .tags__list.svelte-1kw1j3q {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }

  .tags__hint.svelte-1kw1j3q {
    margin: 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .create-fields.svelte-11zk5im {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding-top: 4px;
  }

  .create-field {
    width: 100%;
  }

  .hint.svelte-11zk5im {
    font-size: 13px;
    color: var(--s4q-text-secondary);
    margin: 0 0 16px;
  }

  .hint--warn.svelte-11zk5im {
    margin: 16px 0 0;
    color: var(--s4q-warning);
  }

  .error.svelte-11zk5im {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }
  /* The recording sits apart from the master data above it: the fields say what a device *is*,
     this says what the portal does with it - the same reasoning that set the offline field apart
     in the alarm group dialog. */
  .recording.svelte-11zk5im {
    border-top: 1px solid var(--s4q-border, rgba(0, 0, 0, 0.12));
    padding-top: 12px;
    margin-top: 4px;
  }

  /* MDC positions the menu absolutely against the nearest *positioned* ancestor. Without this it
     resolves against the page and the menu lands in the top right corner, several rows away from
     the button that opened it - measured, not guessed. */
  .actions.svelte-msl1h5 {
    position: relative;
    display: flex;
    justify-content: center;
  }

  /* The surface's right edge is the button's right edge, always: this button never stands
     anywhere but at the right end of a row, so the menu always opens leftwards into the row.
     Pinned with !important over the inline `left` MDC writes, because MDC's own decision came
     out wrong (see above) and there is exactly one right answer here. `right` in viewport
     terms, which is what a fixed surface wants. */
  .actions.svelte-msl1h5 .mdc-menu-surface--open {
    left: auto !important;
    right: var(--menu-right) !important;
  }

  /* Explicit box: MDC 14 does not read --mdc-icon-button-size, and the 48 px default was what
     held every device row at 69 px while the other lists sat at 52 - measured, not the padding. */
  .mdc-icon-button.row-menu {
    width: 30px;
    height: 30px;
    padding: 6px;
    font-size: 18px;
    color: var(--s4q-text-secondary);
  }

  /* Über dem gestreckten Zeilenlink - sonst erreicht den Auslöser kein Zeigerereignis. Dieselbe
     Stufe, die das Zeilenmenü braucht. */
  .hovercard.svelte-p4k238 {
    position: relative;
    z-index: 1;
    display: inline-flex;
  }

  /* Der Auslöser sieht aus wie der Text, an dessen Stelle er steht: Er ist eine Anmerkung, kein
     Bedienelement. Der Fokusring bleibt - mit der Tastatur ist er eines. */
  .hovercard__trigger.svelte-p4k238 {
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
  }

  .hovercard__trigger.svelte-p4k238:focus-visible {
    outline: 2px solid var(--mdc-theme-primary, #aa0000);
    outline-offset: 2px;
    border-radius: 3px;
  }

  /* Die Karte trägt die Fläche der Listen, nur kleiner: Sie ist eine Auskunft und kein Dialog.
     `pointer-events: none`, damit sie nicht ihr eigenes `pointerleave` auslöst, wenn der Zeiger
     sie streift. */
  .hovercard__panel.svelte-p4k238 {
    position: fixed;
    z-index: 30;
    pointer-events: none;
    max-width: 320px;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid var(--s4q-border-subtle);
    background: var(--mdc-theme-surface, #fff);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.16);
    font-size: 12.5px;
    color: var(--mdc-theme-on-surface, #1b1b1b);
    text-align: left;
    white-space: normal;
  }

  /* **Die schlichte Fassung** - auf der Seite des Geraets selbst. Kein Zeiger, kein Hover, keine
     gestreckte Klickflaeche: Dort ist die Zeile eine Auskunft und kein Weg. */
  .row--plain.svelte-el8foi {
    cursor: default;
  }

  .row--plain.svelte-el8foi:hover {
    background: none;
  }

  /* Die Spalten stehen in `app.css` als `.row--device` - der Spaltenkopf der Liste braucht
     dasselbe Raster, und der steht in einer anderen Datei. */
  .row.svelte-el8foi {
    position: relative;
  }

  /* Die ganze Zeile ist der Link. Nicht als <a> um die Zeile herum - sie enthaelt einen Knopf, und
     ein <a> um einen Knopf ist ungueltiges HTML -, sondern als Flaeche des einen Links darin
     ("stretched link"). Damit bleibt genau ein Element je Zeile in der Tabulatorreihenfolge, und
     die Tastatur bedient dieselbe Ziel-URL wie die Maus. Das Zeilenmenue liegt darueber. */
  .name.svelte-el8foi::after {
    content: '';
    position: absolute;
    inset: 0;
  }

  .row.svelte-el8foi .actions {
    position: relative;
    z-index: 1;
  }

  /* The z-index above makes every row's menu box a stacking context, and later rows paint over
     earlier ones - so the buttons of the rows below showed through an open menu (reported). MDC's
     own z-index on the surface cannot escape the context it lives in; the context itself has to
     rise while its menu is open. */
  .row.svelte-el8foi .actions:has(.mdc-menu-surface--open) {
    z-index: 3;
  }

  /* Gestapelt entfallen die Messwerte - das Raster dazu steht bei `.row--device` in `app.css`.
     Kein Umbau der Zellen noetig, die Zeile war schon zweizeilig. */
  .table--stack .value.svelte-el8foi {
    display: none;
  }

  /* Die Karte hinter dem "+n": zwei Spalten, Name links, Zahl rechtsbuendig - dieselbe
     Handschrift wie die Wert-Slots daneben. */
  .hover__title.svelte-el8foi {
    margin: 0 0 6px;
    font-size: 11px;
    color: var(--s4q-text-hint);
  }

  .hover__list.svelte-el8foi {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 3px 14px;
    margin: 0;
  }

  .hover__list.svelte-el8foi dt:where(.svelte-el8foi) {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--s4q-text-secondary);
  }

  .hover__list.svelte-el8foi dd:where(.svelte-el8foi) {
    margin: 0;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
  }

  /* Wie auf der Ereignisseite - der Chip traegt das x in derselben Groesse. */
  .chip__close.svelte-el8foi {
    font-size: 15px;
  }

  .cell-name.svelte-el8foi {
    min-width: 0;
  }

  /* 13,5 px wie die Namensspalte der Datenpunkte und der Ereignisse - danach traegt keine der drei
     Listen mehr eine eigene Groesse. */
  .name.svelte-el8foi {
    display: block;
    font-size: 13.5px;
    line-height: 1.3;
    font-weight: 500;
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .name.svelte-el8foi:hover {
    color: var(--mdc-theme-primary, #aa0000);
  }

  .cell-name__type.svelte-el8foi {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
    white-space: nowrap;
  }

  /* The type keeps its width, the tags give theirs up: `flex: none` against `min-width: 0` is
     what decides which of the two cuts when the column gets narrow. */
  .cell-name__kind.svelte-el8foi {
    flex: none;
  }

  .cell-name__tags.svelte-el8foi {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* The dot is the separator this portal uses between facts of one line; it hangs off the tags,
     so a device without any has no dangling mark. */
  .cell-name__tags.svelte-el8foi::before {
    content: '· ';
  }

  .cell-name__warn.svelte-el8foi {
    display: flex;
    align-items: center;
    gap: 3px;
    color: var(--s4q-warning);
  }

  .cell-name__warn.svelte-el8foi .material-icons:where(.svelte-el8foi) {
    font-size: 13px;
  }

  /* Derselbe Strich in Zustand und Messwerten - beide sagen dasselbe, also sehen sie gleich aus. */
  .none.svelte-el8foi {
    font-size: 13px;
    color: var(--s4q-idle);
  }

  .state.svelte-el8foi,
  .cell-name.svelte-el8foi {
    min-width: 0;
  }

  /* Dieselbe Bauform wie ein Messwert-Slot: leise Beschriftung ueber der Aussage, rechtsbuendig,
     und direkt neben den Werten - so bilden Zustand und Messwerte einen Block rechts, waehrend
     der Name den Ueberschuss der Zeile bekommt. */
  /* Der Abstand zu den Werten: Zustand und Messwerte sind zwei verschiedene Aussagen und standen
     im allgemeinen Spaltenabstand von 16 px nebeneinander - gemeldet als zu nah. Die Polsterung
     schiebt die rechtsbuendige Aussage nach links, ohne das Raster anzufassen. */
  .state.svelte-el8foi {
    text-align: right;
    padding-right: 14px;
  }

  .state__word.svelte-el8foi,
  .state__figure.svelte-el8foi {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .state__word.svelte-el8foi {
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
  }

  .state__figure.svelte-el8foi {
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
    color: var(--s4q-fault);
  }

  /* Breit traegt der Titel der Zeile die Angabe, und die Zustandszelle bleibt eine Zahl hoch -
     "seit 06.09., 21:47" hat sie dort einmal 40 px gekostet, die der Geraetename zahlte. Schmal
     gibt es keinen Titel, also steht sie da. */
  .state__since.svelte-el8foi {
    display: none;
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
    white-space: nowrap;
  }

  .table--stack .state__since.svelte-el8foi {
    display: block;
  }

  /* Drei feste, schmale Slots und ein Platz fuer das "+n", rechtsbuendig: fest, damit die Werte
     einer Zeile unter denen der naechsten stehen; schmal und rechts, damit sie als ein Block
     zusammenstehen statt ueber die halbe Zeile verteilt. 92 px tragen "43,6 °C" und
     "1234.5 kWh"; laengere Punktnamen schneidet die Beschriftung ab und nennt sie im title.

     "justify-content: end" wirkt hier nur auf das Raster als Ganzes, und das ist bei festen
     Spalten immer gleich breit - womit die Zelle sich fuellt, entscheidet allein die Reihenfolge
     im Markup. Die leeren Slots stehen deshalb vorn, siehe dort. */

  /* Die Typ-Kachel steht in der Namensspalte und ueber beide Zeilen - sie gehoert zur Identitaet
     des Geraets, nicht in eine eigene Spalte. 20 px sind kleiner als der zweizeilige Block
     daneben, die Zeilenhoehe von 52 px bleibt also unberuehrt. */
  .cell-name.svelte-el8foi {
    display: grid;
    grid-template-columns: 20px minmax(0, 1fr);
    align-items: center;
    gap: 0 10px;
  }

  /* **Entfaerbt, und das ist keine Kosmetik.** Die Kacheln tragen Markenrot, und in einer Zeile
     mit roter Stoerungsmarke standen zwei Rottoene nebeneinander - in der Offline-Zeile zog die
     rote Kachel den Blick sogar auf ein Geraet, an dem nichts ansteht. Dieselbe Regel, aus der die
     Marken nicht im Markenrot stehen: Farbe sagt in dieser Liste den Zustand, sonst nichts. Als
     Grauton bleibt die Kachel, was sie sein soll - Textur und ein Wiedererkennungszeichen. */
  .cell-name__type-icon.svelte-el8foi {
    grid-row: 1 / span 2;
    width: 20px;
    height: 20px;
    filter: grayscale(1);
    opacity: 0.55;
  }

  .cell-name__line.svelte-el8foi {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
  }

  /* Die Nadel hinter dem Namen: klein, im Hinweiston, und sie schrumpft nie weg - der Name traegt
     die Ellipse. */
  .cell-name__pin.svelte-el8foi {
    flex: none;
    font-size: 14px;
    color: var(--s4q-text-hint);
  }

  /* Die Einheit leiser als die Zahl: Die Zahl ist die Nachricht, die Einheit die Fussnote. */
  .value__unit.svelte-el8foi {
    margin-left: 3px;
    font-weight: 400;
    color: var(--s4q-text-secondary);
  }

  .value.svelte-el8foi {
    display: grid;
    grid-template-columns: repeat(3, 92px) 30px;
    justify-content: end;
    align-items: center;
    gap: 8px 14px;
    min-width: 0;
  }

  /* Rechtsbuendig auch im Slot: Zahlen gehoeren an eine gemeinsame Kante, und der Slot ist so
     schmal, dass links davon nichts steht, worauf sich die Beschriftung beziehen koennte. */
  .value__body.svelte-el8foi {
    min-width: 0;
    text-align: right;
  }

  .value__title.svelte-el8foi {
    display: block;
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .value__number.svelte-el8foi {
    display: block;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .value__number--fault.svelte-el8foi {
    color: var(--s4q-fault);
  }

  /* Derselbe Warnton wie auf der Detailseite. Die Zeilenhoehe kommt von .value__number, das Symbol
     sitzt also genau dort, wo sonst die Zahl steht. */
  .value__pending.svelte-el8foi .material-icons:where(.svelte-el8foi) {
    font-size: 15px;
    line-height: 1.3;
    color: var(--s4q-warning);
  }

  .value__more.svelte-el8foi {
    justify-self: end;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--s4q-text-hint);
  }

  /* Sticky like the drawer beside it: the title, the search and the "n new" button are what one
     reaches for from anywhere in a long list, so they stay where the hand expects them. The
     z-index is for this rule alone - a sticky bar is painted in DOM order, and the rows come
     after it. */
  .bar.svelte-6jr0bc {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 18px;
    height: 64px;
    padding: 0 24px;
    box-sizing: border-box;
    background: var(--mdc-theme-surface, #fff);
    border-bottom: 1px solid var(--s4q-border-subtle);
  }

  /* The bar is where you are, so the page title lives here and not a second time in the content.
     Sized like a section heading, not like the old page h1 - the drawer already says which area
     this is, and a 32 px word above a table only pushes the table down.

     Since the toggle left, the title is the first thing in the bar and therefore starts on the
     bar's own 24 px padding - measured, that is exactly where the content below begins.

     **18 px und halbfett seit dieser Runde**, vorher 16/500. Gemeldet, dass der Titel neben den
     32 px hohen Chips und Segmenten klein wirkte - und das tat er: Er stand in derselben Groesse
     da wie die Abschnittsueberschrift eine Zeile tiefer, obwohl er die Seite benennt und sie nur
     einen ihrer Abschnitte. Die Stufen sind jetzt 18 / 16 / 13,5. */
  /* Der Titelblock: zwei Zeilen, senkrecht mittig in den 64 px der Leiste. Gerechnet, damit die
     Leiste ihre Hoehe behaelt - gefordert, und zwar ausdruecklich: 21,6 px Titel plus 1 plus
     16,3 px Untertitel sind 39, die Leiste ist 64. Mehr geht hier nicht hinein; eine dritte Zeile
     waere eine Aenderung an *allen* Seiten und an der Seitenleiste daneben.
     `min-width: 0`, sonst behaelt das Flex-Element seine Inhaltsbreite, was die Leiste auch sagt. */
  .bar__titles.svelte-6jr0bc {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1px;
    min-width: 0;
    flex: 0 1 auto;
  }

  .bar__title.svelte-6jr0bc {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    /* A device name as title can be long; it shortens rather than pushing the action off the bar. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .bar__counter.svelte-6jr0bc {
    font-size: 12.5px;
    line-height: 1.3;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Die Knoepfe neben dem Titel: untereinander eng (8 px), zum Titel der volle Abstand der Leiste.
     Sie standen dichter dran (-8 px), weil sie zum Titel gehoeren - gemeldet, dass es zu eng ist,
     und der Einwand trifft: Zusammengehoerig sind sie mit dem Titel, ein Wort und ein Kasten
     brauchen aber mehr Luft als zwei Kaesten. `min-width: 0`, damit ein langer Geraetename den
     Knoepfen den Platz nimmt statt sie ueber die Aktion rechts zu schieben. */
  .bar__meta.svelte-6jr0bc {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
  }

  /* Links und rechts: dieselben Abstaende wie in der Leiste selbst, damit der Umbau in drei
     Bloecke am Bild nichts aendert. Rechts haelt `justify-content` die Aktion am Rand - dafuer war
     frueher der Platzhalter da. */
  .bar__lead.svelte-6jr0bc,
  .bar__trail.svelte-6jr0bc {
    display: flex;
    align-items: center;
    gap: 18px;
    min-width: 0;
  }

  .bar__lead.svelte-6jr0bc {
    flex: 0 1 auto;
  }

  .bar__trail.svelte-6jr0bc {
    flex: 1 1 auto;
    justify-content: flex-end;
  }

  /* **Die Knoepfe in der Mitte der Leiste.** Links und rechts wachsen aus derselben
     Grundbreite 0 und teilen sich den freien Platz je zur Haelfte - der mittlere Block sitzt damit
     exakt in der Mitte, **unabhaengig davon, was links und rechts steht**.

     Genau das war die Meldung: Die Geraetedetailseite hat rechts zwei Aktionen, die Zustandsseite
     keine - der Block sprang beim Wechsel zwischen den beiden um 50 px. Die Vorstufe zentrierte
     zwischen Titel und Aktionen und hatte den Sprung deshalb noch.

     Nur wo es einen mittleren Block gibt: Der linke waechst dafuer aus Grundbreite 0 und
     schneidet den Titel damit spaetestens bei halber Leiste ab. Fuer einen Geraetenamen ist das
     reichlich (49 Zeichen messen 500 px, die halbe Leiste 550), fuer eine Seite ohne `meta` waere
     es Unfug - deshalb haengt die Regel am Vorhandensein des Snippets. */
  .bar--center.svelte-6jr0bc .bar__lead:where(.svelte-6jr0bc),
  .bar--center.svelte-6jr0bc .bar__trail:where(.svelte-6jr0bc) {
    flex: 1 1 0;
  }

  .bar__icon {
    --mdc-icon-button-size: 34px;
    --mdc-icon-size: 19px;
    color: var(--s4q-text-secondary);
    flex: none;
  }

  .bar__search.svelte-6jr0bc {
    display: flex;
    align-items: center;
    width: 0;
    opacity: 0;
    overflow: hidden;
    border-bottom: 1px solid transparent;
    transition:
      width 0.18s ease,
      opacity 0.18s ease;
  }

  .bar__search--open.svelte-6jr0bc {
    width: 290px;
    opacity: 1;
    border-bottom-color: var(--s4q-text-hint);
  }

  .bar__search.svelte-6jr0bc input:where(.svelte-6jr0bc) {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: none;
    padding: 6px 0;
    font: inherit;
    font-size: 13.5px;
    color: inherit;
  }

  .bar__search.svelte-6jr0bc input:where(.svelte-6jr0bc)::placeholder {
    color: var(--s4q-text-hint);
  }

  /* The browser's own clear button would sit next to ours. */
  .bar__search.svelte-6jr0bc input:where(.svelte-6jr0bc)::-webkit-search-cancel-button {
    display: none;
  }

  .bar__close.svelte-6jr0bc {
    flex: none;
    display: flex;
    border: none;
    background: none;
    padding: 2px;
    cursor: pointer;
    color: var(--s4q-text-secondary);
  }

  .bar__close.svelte-6jr0bc .material-icons:where(.svelte-6jr0bc) {
    font-size: 17px;
  }

  .bar__action.svelte-6jr0bc {
    display: flex;
    align-items: center;
    flex: none;
  }

  /* The menu button: a plain button like the drawer's toggle, 34 px like the search beside it. */
  .bar__menu.svelte-6jr0bc {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 34px;
    height: 34px;
    margin-left: -6px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--s4q-text-secondary);
    cursor: pointer;
  }

  .bar__menu.svelte-6jr0bc .material-icons:where(.svelte-6jr0bc) {
    font-size: 22px;
  }

  /* Fault red, not brand red: it stands in for the drawer's badges, and those mean "something is
     standing", not "here is an accent". */
  .bar__dot.svelte-6jr0bc {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--s4q-fault);
    box-shadow: 0 0 0 2px var(--mdc-theme-surface, #fff);
  }

  /* Compact: tighter, and the counter goes - the chips below carry the same number ("Alle 5"),
     and at 375 px the bar has room for the title, the search and the one action, not for a
     sentence beside the title. When the search is open it takes the whole bar: title and action
     step aside, the field's own x is the way back. */
  .bar--compact.svelte-6jr0bc {
    gap: 10px;
  }

  /* **Am Telefon bricht die Leiste um**, und nur dort: Die Knoepfe sind auf den Geraeteseiten zwei
     Segment-Kaesten (232 px gemessen); mit Titel und den beiden Aktionen rechts ergibt das 434 px
     in einer 390 px breiten Leiste - der Stift lag ueber dem Segment. Titel und Aktionen oben, die
     Knoepfe darunter in voller Breite, nachgemessen 80 px statt 64.

     Breit bleibt es bei einer Zeile und 64 px. Zwischenstand dieser Runde war, die Knoepfe auch
     dort unter den Titel zu setzen - gemeint war aber der *Text*, nicht die Knoepfe, und die Hoehe
     der Leiste soll sich ausdruecklich nicht aendern. Waere sie es je, dann ueberall und samt der
     Seitenleiste daneben. */
  .bar--compact.svelte-6jr0bc {
    height: auto;
    min-height: 64px;
    flex-wrap: wrap;
    /* Waagerecht die 10 px der schmalen Leiste, senkrecht 4: Die beiden Zeilen gehoeren zusammen. */
    row-gap: 4px;
    padding: 8px 12px;
  }

  /* `order` und nicht die Reihenfolge im Dokument: Im Fluss stehen die Knoepfe vor der Aktion
     rechts - umbraeche nur ihr Kasten, landete die Aktion in einer dritten Zeile. Und der Titel
     bekommt `flex-basis: 0`, sonst waere schon er allein eine Zeile: Beim Umbruch wird zuerst
     verteilt und dann geschrumpft, ein langer Geraetename raeumt die Zeile also leer, bevor seine
     Ellipse greift. Gemessen: vier Zeilen und 190 px hohe Leiste, jetzt zwei und 98. */
  /* Am Telefon nimmt der linke Block den ganzen freien Platz und der rechte nur, was er braucht:
     Geteilt zur Haelfte stand der Name als "Zaehlersc..." da, waehrend daneben Luft war. Auch auf
     den Geraeteseiten - die Mitte ist dort ohnehin eine eigene Zeile. */
  .bar--compact.svelte-6jr0bc .bar__lead:where(.svelte-6jr0bc),
  .bar--compact.bar--center.svelte-6jr0bc .bar__lead:where(.svelte-6jr0bc) {
    flex: 1 1 0;
  }

  .bar--compact.svelte-6jr0bc .bar__trail:where(.svelte-6jr0bc),
  .bar--compact.bar--center.svelte-6jr0bc .bar__trail:where(.svelte-6jr0bc) {
    flex: 0 0 auto;
  }

  .bar--compact.svelte-6jr0bc .bar__lead:where(.svelte-6jr0bc),
  .bar--compact.svelte-6jr0bc .bar__trail:where(.svelte-6jr0bc) {
    gap: 10px;
  }

  .bar--compact.svelte-6jr0bc .bar__meta:where(.svelte-6jr0bc) {
    order: 1;
    flex-basis: 100%;
  }

  .bar--compact.svelte-6jr0bc .bar__counter:where(.svelte-6jr0bc) {
    display: none;
  }

  .bar--compact.svelte-6jr0bc .bar__search--open:where(.svelte-6jr0bc) {
    flex: 1 1 auto;
    width: auto;
  }

  .bar--searching.svelte-6jr0bc .bar__lead:where(.svelte-6jr0bc),
  .bar--searching.svelte-6jr0bc .bar__meta:where(.svelte-6jr0bc),
  .bar--searching.svelte-6jr0bc .bar__action:where(.svelte-6jr0bc) {
    display: none;
  }

  .bar--searching.svelte-6jr0bc .bar__trail:where(.svelte-6jr0bc) {
    flex: 1 1 auto;
  }

  /* **Die Luft zwischen den Gruppen** - 16 px, und nur zwischen ihnen. Gemeldet, dass sie nach
     dem Herausloesen der Zeile fehlte: Die Regel stand im selben CSS-Block wie die Zeile und ist
     mit ihr gewandert, obwohl sie den Boxen gehoert und nicht der Zeile. Ohne sie stossen die
     Karten aneinander und die Trennung, um derentwillen es getrennte Boxen gibt, ist weg. */
  .table--group.svelte-q0902g + .table--group:where(.svelte-q0902g) {
    margin-top: 16px;
  }

  /* Der Spaltenkopf, den es nur in der ungruppierten Liste gibt. */
  .head.svelte-q0902g {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    font: inherit;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
  }

  .head--end.svelte-q0902g {
    justify-content: flex-end;
    text-align: right;
  }

  .head--value.svelte-q0902g {
    padding-right: 4px;
  }

  .head.svelte-q0902g:hover {
    color: var(--mdc-theme-on-surface, #1b1b1b);
  }

  .head__dir.svelte-q0902g {
    font-size: 10px;
  }

  .loading.svelte-q0902g {
    display: flex;
    justify-content: center;
    padding: 48px 0;
  }

  /* Every element `flex: none`: without it the chips wrap *inside* themselves on a narrow window,
     and a chip with its count on a second line is unreadable. */
  /* Filter row, chips, frame and rows come from app.css - one definition for the three lists. */
  .filters__hint.svelte-q0902g {
    font-size: 12.5px;
    color: var(--s4q-text-secondary);
  }

  /* Die 180 px der Messwertspalte sind gemessen, nicht geschätzt: Abzüglich Abstand und "+n"
     bleiben 156 px für die Titelzeile, und die trägt den Punktnamen. Der ist in echten Anlagen
     lang - "Rücklauftemperatur HK1" misst 139 px, "Wirkleistung gesamt" 113. Der breiteste
     realistische *Wert* ("1234567.89 kWh", 112 px) passte schon vorher; eng war die Zeile darüber.
     Bezahlt wird es aus der Namensspalte, die den Überschuss bekommt und ihn hat. */
  /* Der Spielraum liegt jetzt bei den Werten, nicht beim Namen: Gemessen bekam die Namensspalte
     bei 1280 px 386 px und die Messwerte 180, waehrend der laengste Name 130 brauchte. Der Name
     bekommt eine Spanne statt des ganzen Ueberschusses, die Werte den Rest. */
  .empty-state.svelte-q0902g {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 12px;
    padding: 72px 40px 78px;
    background: var(--mdc-theme-surface, #fff);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  }

  .empty-state__ring.svelte-q0902g {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border: 2px solid var(--s4q-border-subtle);
    border-radius: 50%;
    color: var(--s4q-idle);
  }

  .empty-state.svelte-q0902g h2:where(.svelte-q0902g) {
    margin: 0;
    font-size: 17px;
    font-weight: 500;
  }

  .empty-state.svelte-q0902g p:where(.svelte-q0902g) {
    margin: 0;
    max-width: 470px;
    font-size: 13px;
    line-height: 1.7;
    color: var(--s4q-text-secondary);
    text-wrap: pretty;
  }

  /* An accent edge instead of a coloured surface: this is information, not an alarm. */
  .notice.svelte-q0902g {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    max-width: 820px;
    margin-bottom: 18px;
    padding: 14px 18px;
    background: var(--mdc-theme-surface, #fff);
    border-left: 3px solid var(--mdc-theme-primary, #aa0000);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  }

  .notice.svelte-q0902g .material-icons:where(.svelte-q0902g) {
    font-size: 18px;
    color: var(--mdc-theme-primary, #aa0000);
  }

  .notice.svelte-q0902g p:where(.svelte-q0902g) {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.65;
  }
.uplot, .uplot *, .uplot *::before, .uplot *::after {box-sizing: border-box;}.uplot {font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";line-height: 1.5;width: min-content;}.u-title {text-align: center;font-size: 18px;font-weight: bold;}.u-wrap {position: relative;user-select: none;}.u-over, .u-under {position: absolute;}.u-under {overflow: hidden;}.uplot canvas {display: block;position: relative;width: 100%;height: 100%;}.u-axis {position: absolute;}.u-legend {font-size: 14px;margin: auto;text-align: center;}.u-inline {display: block;}.u-inline * {display: inline-block;}.u-inline tr {margin-right: 16px;}.u-legend th {font-weight: 600;}.u-legend th > * {vertical-align: middle;display: inline-block;}.u-legend .u-marker {width: 1em;height: 1em;margin-right: 4px;background-clip: padding-box !important;}.u-inline.u-live th::after {content: ":";vertical-align: middle;}.u-inline:not(.u-live) .u-value {display: none;}.u-series > * {padding: 4px;}.u-series th {cursor: pointer;}.u-legend .u-off > * {opacity: 0.3;}.u-select {background: rgba(0,0,0,0.07);position: absolute;pointer-events: none;}.u-cursor-x, .u-cursor-y {position: absolute;left: 0;top: 0;pointer-events: none;will-change: transform;}.u-hz .u-cursor-x, .u-vt .u-cursor-y {height: 100%;border-right: 1px dashed #607D8B;}.u-hz .u-cursor-y, .u-vt .u-cursor-x {width: 100%;border-bottom: 1px dashed #607D8B;}.u-cursor-pt {position: absolute;top: 0;left: 0;border-radius: 50%;border: 0 solid;pointer-events: none;will-change: transform;/*this has to be !important since we set inline "background" shorthand */background-clip: padding-box !important;}.u-axis.u-off, .u-select.u-off, .u-cursor-x.u-off, .u-cursor-y.u-off, .u-cursor-pt.u-off {display: none;}
  .chart.svelte-1oj12n0 {
    width: 100%;
    min-height: 280px;
  }

  /* A band is a strip, not a plot area - it must not reserve the height of one. */
  .chart--band.svelte-1oj12n0 {
    min-height: 86px;
  }

  .chart__hint.svelte-1oj12n0 {
    margin: 6px 0 0;
    font-size: 11px;
    line-height: 1.35;
    color: var(--s4q-text-hint);
  }

  /* uPlot ships its own stylesheet; this is where it does not know our theme - and where its
     legend gets the two edges the rest of the column has.

     **Links, was die Linie ist; rechts, was sie unter dem Zeiger sagt.** Zentriert stand sie als
     einziges Stueck der Spalte weder an der linken Flucht der Ueberschriften und der y-Achse noch
     an der rechten Kante. Gemeldet, zusammen mit dem zweiten Punkt: Beschriftung und Wert sassen
     nicht auf einer Hoehe - `th` und `td` sind Tabellenzellen, die Marke darin ist ein `div`, und
     die Grundlinien lagen ein Pixel auseinander.

     `display: contents` an der Zeile loest die vier Zellen aus ihren beiden Zeilen heraus, sodass
     sie Flex-Elemente *einer* Reihe werden; `order` stellt sie um (Reihe, dann Zeitpunkt, dann
     Wert) und `align-items: baseline` setzt alle vier auf dieselbe Grundlinie. Ohne das haette
     man die Tabelle nachbauen muessen, die uPlot selbst schreibt. */
  .chart.svelte-1oj12n0 .u-legend {
    display: block;
    width: 100%;
    margin: 4px 0 0;
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  .chart.svelte-1oj12n0 .u-legend tbody {
    display: flex;
    align-items: baseline;
    gap: 4px 10px;
    flex-wrap: wrap;
  }

  .chart.svelte-1oj12n0 .u-legend tr {
    display: contents;
  }

  /* Kein `gap` hier: uPlot haengt den Doppelpunkt als `::after` an die Zelle, und ein Abstand
     zwischen den Kindern schoebe ihn von seiner Beschriftung weg ("Zeitpunkt :"). Die Luft
     zwischen Marke und Wort traegt deshalb die Marke selbst. */
  .chart.svelte-1oj12n0 .u-legend th {
    display: inline-flex;
    align-items: baseline;
    font-weight: 400;
  }

  .chart.svelte-1oj12n0 .u-legend .u-marker {
    margin-right: 6px;
  }

  /* Die Reihe nach ganz links, ihr Wert nach ganz rechts; dazwischen der Zeitpunkt, der von der
     Luecke nach rechts geschoben wird. */
  .chart.svelte-1oj12n0 .u-legend tr:last-child th {
    order: 1;
  }

  .chart.svelte-1oj12n0 .u-legend tr:first-child th {
    order: 2;
    margin-left: auto;
  }

  .chart.svelte-1oj12n0 .u-legend tr:first-child td {
    order: 3;
  }

  .chart.svelte-1oj12n0 .u-legend tr:last-child td {
    order: 4;
    font-variant-numeric: tabular-nums;
  }

  /* Die Marke traegt ihre Farbe als Inline-Stil von uPlot; hier nur die Groesse und der Sitz auf
     der Grundlinie der Schrift daneben. */
  .chart.svelte-1oj12n0 .u-legend .u-marker {
    align-self: center;
  }

  /* uPlot haengt an jede Beschriftung einen Doppelpunkt. Rechts ist er richtig - dort folgt der
     Wert unmittelbar -, links haengt er ins Leere, weil sein Wert an der anderen Kante steht. */
  .chart.svelte-1oj12n0 .u-legend tr:last-child th::after {
    content: '';
  }

  .fields.svelte-jneiqm {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding-top: 4px;
  }

  .field {
    width: 100%;
  }

  .error.svelte-jneiqm {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }
  /* The recording sits apart from the master data above it: the fields say what a device *is*,
     this says what the portal does with it - the same reasoning that set the offline field apart
     in the alarm group dialog. */
  .recording.svelte-jneiqm {
    border-top: 1px solid var(--s4q-border, rgba(0, 0, 0, 0.12));
    padding-top: 12px;
    margin-top: 4px;
  }

  .hint.svelte-14a6nei {
    font-size: 13px;
    color: var(--s4q-text-secondary);
    margin: 0 0 8px;
  }

  .hint--warn.svelte-14a6nei {
    margin: 16px 0 0;
    color: var(--s4q-warning);
  }

  .error.svelte-14a6nei {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }

  .hint.svelte-1t1uyrj {
    font-size: 13px;
    color: var(--s4q-text-secondary);
    margin: 0 0 8px;
  }

  .error.svelte-1t1uyrj {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }

  .device-actions.svelte-1g1susb {
    position: relative;
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
  }

  /* Main column and a narrow side column, as the design lays it out. Below 1100 px the side
     column would squeeze the table to nothing, so it drops underneath. */
  .layout.svelte-1g1susb {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 22px;
    align-items: start;
  }

  @media (max-width: 1100px) {
    .layout.svelte-1g1susb {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  .main.svelte-1g1susb {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 22px;
  }

  .side.svelte-1g1susb {
    display: flex;
    flex-direction: column;
    gap: 22px;
  }

  /* ---- data points --------------------------------------------------------------------------- */

  /* **Der Rhythmus der Inhaltsspalte, auf beiden Geraeteseiten derselbe** - gerechnet und
     nachgemessen, nachdem dreimal gemeldet wurde, dass die Seiten sich unterscheiden:

       Titelzeile ...64 | 22 | Zeile 86...176 | 22 | Ueberschrift 198...230 | 16 | Inhalt 246...

     **22 ueber der Ueberschrift, 16 darunter**, und das ist kein Geschmack: Eine Ueberschrift
     gehoert zu dem, was ihr folgt, nicht zu dem, was ueber ihr steht - der engere Abstand bindet
     sie an die Tabelle, der weitere trennt sie von der Zeile. Vorgeschlagen vom Anwender. Die 22
     sind zugleich der Abstand zwischen Titelzeile und Zeile (das Innenmass der Seite) und der
     zwischen den Karten der Seitenspalte; die Spalte hat damit genau zwei Abstaende, einen
     trennenden und einen bindenden.

     Das Ueberschriftenband ist auf beiden Seiten 32 px hoch: hier, weil die Chips so hoch sind,
     drueben erzwungen (`min-height`), damit die Ueberschrift nicht springt, je nachdem ob ein
     Zeitraum-Schalter danebensteht. Ohne das verschoebe sich jeder Abstand, den man an ihr misst. */
  .dp-head.svelte-1g1susb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 -6px 2px;
  }

  /* 16 px seit dieser Runde, vorher 14. Gemeldet, dass die Ueberschrift neben den 32 px hohen
     Chips klein wirkte - dieselbe Beobachtung wie beim Seitentitel, und dieselbe Antwort: Die
     Stufen sind 18 (Seite) / 16 (Abschnitt) / 13,5 (Inhalt). */
  .dp-title.svelte-1g1susb {
    font-size: 16px;
    font-weight: 500;
    flex: none;
  }

  .dp-filters.svelte-1g1susb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    margin-left: auto;
  }

  /* On a phone the title has the first line to itself and the controls the next two - right
     against the edge they looked like they belonged to something further right. */
  .shell--compact .dp-filters.svelte-1g1susb {
    justify-content: flex-start;
    margin-left: 0;
  }

  /* Chips, rows and the scroll container come from app.css - one definition for the three
     lists; only the columns are this page's. */
  .row.svelte-1g1susb {
    grid-template-columns: 26px minmax(0, 1fr) 190px 110px 26px;
    min-width: 600px;
  }

  button.row--point.svelte-1g1susb {
    cursor: pointer;
  }

  /* Schmal: Name ueber Schluessel wie bisher, darunter Art und Bedingung in einer Zeile ("Messwert
     - groesser als 70 °C"), rechts der Wert, ganz rechts der Chevron. Die Art nicht zu zeigen
     waere billiger gewesen - der Editor zeigt sie ohnehin -, aber "wird nicht geliefert" steht in
     dieser Zelle, und das darf nicht verschwinden. Die Marke ist bei einem nicht zugeordneten
     Punkt ein leeres span ohne Klasse; :first-child holt beide auf ihren Platz. */
  .table--stack .row.svelte-1g1susb:not(.row--head) {
    grid-template-columns: 26px minmax(0, 1fr) auto 26px;
    min-width: 0;
    grid-template-areas:
      'mark point value chevron'
      'mark kind value chevron';
  }

  .table--stack .row--head.svelte-1g1susb {
    grid-template-columns: 26px minmax(0, 1fr) auto 26px;
    min-width: 0;
  }

  .table--stack .row--head.svelte-1g1susb .cell-kind:where(.svelte-1g1susb) {
    display: none;
  }

  .table--stack .row--point.svelte-1g1susb > :where(.svelte-1g1susb):first-child {
    grid-area: mark;
  }

  .table--stack .cell-point.svelte-1g1susb {
    grid-area: point;
  }

  .table--stack .row--point.svelte-1g1susb .cell-kind:where(.svelte-1g1susb) {
    grid-area: kind;
    display: flex;
    gap: 6px;
    font-size: 11px;
    line-height: 1.25;
  }

  /* Der Punkt zwischen Art und Bedingung nur, wo es eine Art gibt - und die gibt es genau dann,
     wenn die Zeile eine Marke traegt (ein nicht zugeordneter Punkt hat beides nicht). :empty
     taugte nicht: Das Namens-span traegt bei leerer Art noch den Zeilenumbruch aus dem Markup. */
  .table--stack .row--point.svelte-1g1susb > .mark:where(.svelte-1g1susb) ~ .cell-kind:where(.svelte-1g1susb) .cell-kind__rule:where(.svelte-1g1susb)::before {
    content: '·';
    margin-right: 6px;
    color: var(--s4q-text-hint);
  }

  .table--stack .row--point.svelte-1g1susb .cell-value:where(.svelte-1g1susb) {
    grid-area: value;
    text-align: right;
  }

  .table--stack .cell-chevron.svelte-1g1susb {
    grid-area: chevron;
  }

  button.row--point.svelte-1g1susb:hover {
    background: var(--s4q-row-hover);
  }

  /* Nach der Hover-Regel und eine Klasse spezifischer, damit das Ueberfahren die offene Zeile
     nicht wieder aufhellt. */
  .row--point.row--open.svelte-1g1susb {
    background: var(--s4q-row-open);
  }

  /* Only a point that stopped arriving is greyed. The design greys the unassigned too, as a work
     list after a reprogramming; dropped on the user's word - unassigned only means "not part of
     events and alarms", the point is delivered so its value can be looked at, and a value one
     looks at is not a lesser value. */
  .row--missing.svelte-1g1susb .cell-point__name:where(.svelte-1g1susb) {
    color: var(--s4q-text-hint);
  }

  .cell-point.svelte-1g1susb,
  .cell-kind.svelte-1g1susb {
    min-width: 0;
  }

  .cell-point__name.svelte-1g1susb,
  .cell-kind__name.svelte-1g1susb,
  .cell-point__key.svelte-1g1susb,
  .cell-kind__rule.svelte-1g1susb {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-point__name.svelte-1g1susb {
    line-height: 1.3;
  }

  .cell-point__key.svelte-1g1susb,
  .cell-kind__rule.svelte-1g1susb {
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
  }

  .cell-kind__name.svelte-1g1susb {
    font-size: 12.5px;
    line-height: 1.3;
    color: var(--s4q-text-secondary);
  }

  .cell-kind__rule--missing.svelte-1g1susb {
    color: var(--s4q-warning);
  }

  /* line-height wie in den beiden Zellen daneben: ohne sie rechnet der Browser "normal" und die
     Zeile mit "seit" wurde 54 px hoch gegen 52 in den ruhigen - nachgemessen 33,8 gegen 31,3 px
     Zellhoehe. */
  /* Schrift, Ziffern und die Ellipse kommen aus app.css - hier bleibt, was diese Tabelle
     ausmacht: die rechte Kante und die ausdrueckliche Zeilenhoehe (siehe unten). */
  .cell-value.svelte-1g1susb {
    line-height: 1.3;
    text-align: right;
  }

  /* Derselbe Warnton wie "wird nicht geliefert" in der Spalte daneben: beides ist "hier steht
     keine Zahl, und das hat einen Grund". Normales Gewicht, damit der Hinweis nicht wie ein
     Messwert aussieht - die Zelle setzt 500 fuer Zahlen -, und 12 px, weil die Spalte 110 px
     breit ist: "kein Wert" samt Sanduhr misst darin 82 px, in der Schriftgroesse der Zahlen
     waeren es 90. Nachgemessen, nachdem die erste Fassung "noch kein Wert" hiess und mit 119 px
     als "noch kein ..." abgeschnitten dastand. Der ganze Satz steht im Titel. */
  .cell-value__pending.svelte-1g1susb {
    font-size: 12px;
    font-weight: 400;
    color: var(--s4q-warning);
  }

  .cell-value__pending.svelte-1g1susb .material-icons:where(.svelte-1g1susb) {
    font-size: 14px;
    vertical-align: -3px;
    margin-right: 2px;
  }

  .cell-value__since.svelte-1g1susb {
    display: block;
    font-size: 11px;
    font-weight: 400;
    line-height: 1.25;
    color: var(--s4q-text-hint);
  }

  .cell-value--active.svelte-1g1susb {
    color: var(--s4q-fault);
  }

  .cell-chevron.svelte-1g1susb {
    font-size: 18px;
    color: var(--s4q-text-hint);
    text-align: center;
  }

  /* ---- the row editor ------------------------------------------------------------------------ */

  .editor__hint.svelte-1g1susb {
    margin: 2px 0 0;
    font-size: 11px;
    line-height: 1.35;
    color: var(--s4q-text-hint);
  }

  /* The small caps heading of a section, in both columns of the opened row: ZEITRAUM over the
     range, AUSWERTUNG over the kinds, BEDINGUNG, ANZEIGE. They sit on one line across the two
     columns, and so do the two segments under them. */
  .expand__label.svelte-1g1susb {
    display: block;
    font-size: 10.5px;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
    margin-bottom: 7px;
  }

  /* The line under ZEITRAUM: the segment on the left, and room on the right for the jump to a
     date that will join it. */
  .expand__controls.svelte-1g1susb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
    /* A little less than the 22 px of the right column: what follows is not the next section but
       the content of this one. */
    margin-bottom: 14px;
  }

  /* Close under the kinds: the sentence belongs to them and reads the choice back. The 22 px to
     the next block come from `.editor__section`, so the column keeps one rhythm. */
  .editor.svelte-1g1susb .segment:where(.svelte-1g1susb) {
    margin-bottom: 8px;
  }

  .editor__form.svelte-1g1susb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 14px;
  }

  /* Breiten nach dem, was hineingehoert: eine Schwelle ist "60,5", eine Einheit "kWh", ein Name
     "Ruecklauftemperatur HK1". Zusammen mit ihren Beschriftungen passen je zwei in die Spalte. */
  .editor__number.svelte-1g1susb {
    width: 78px;
  }

  .editor__unit.svelte-1g1susb {
    width: 78px;
  }

  .editor__name.svelte-1g1susb {
    /* Gemessen, damit Name und Einheit in eine Zeile passen: 38 (Beschriftung) + 8 + 150 + 14 +
       46 + 8 + 78 = 342 in den 350 px der Spalte. Ein langer Name scrollt im Feld, wie in jedem
       Eingabefeld. */
    width: 150px;
  }

  /* Die Einheit hinter der Schwelle, wie das Suffix des MDC-Feldes vorher - nur ohne das Feld. */
  .editor__suffix.svelte-1g1susb {
    font-size: 12.5px;
    color: var(--s4q-text-secondary);
    margin-left: -2px;
  }

  /* Das Haekchen unter den beiden Feldern, mit dem Abstand eines eigenen Blocks statt dem einer
     Formularzeile - es ist die dritte Frage dieses Abschnitts und nicht das dritte Feld. */
  .editor__check.svelte-1g1susb {
    margin-top: 14px;
  }

  .editor__sentence.svelte-1g1susb {
    margin: 0;
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  /* Die Knoepfe sind der Fuss der Tafel und keine weitere Zeile des letzten Abschnitts - ohne den
     Abstand klebten sie am Hinweistext darueber. */
  .editor__actions.svelte-1g1susb {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 18px;
  }

  .editor__toggle.svelte-1g1susb {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 32px;
    font-size: 12.5px;
    cursor: pointer;
  }

  /* `accent-color` faerbt den nativen Haken im Markenrot - eine Zeile statt eines nachgebauten
     Kastens, und der Browser behaelt seinen Fokusring. */
  .editor__toggle.svelte-1g1susb input:where(.svelte-1g1susb) {
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--mdc-theme-primary, #aa0000);
    cursor: pointer;
  }

  /* 22 px zwischen den Bloecken statt 16 - genug, dass das Auge jede Ueberschrift mit dem
     gruppiert, was unter ihr steht. */
  .editor__section.svelte-1g1susb {
    margin-top: 22px;
  }

  /* Die Felder der Bedingung gehoeren zum Haken darueber und tragen deshalb keine eigene
     Ueberschrift mehr - nur den Abstand einer Formularzeile. Siehe den Kommentar im Markup. */
  .editor__condition.svelte-1g1susb {
    margin-top: 12px;
  }

  /* **Die Linie zwischen zwei Bloecken, und sie ist symmetrisch**: 16 px darueber, 16 darunter.
     Mit den 22 px von `.editor__section` waeren es 22 gegen 16 gewesen, und eine Linie, die
     naeher an dem liegt, was ihr folgt, bindet sie an den falschen Block. */
  .editor__section--apart.svelte-1g1susb {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--s4q-border-subtle);
  }

  /* Der Haken ist hier das erste Element seines Blocks und nicht die dritte Zeile eines anderen -
     die 14 px von `.editor__check` waeren der Abstand, den er dort hatte. */
  .editor__section.svelte-1g1susb > .editor__toggle:where(.svelte-1g1susb) {
    margin-top: 2px;
  }

  .editor__section.svelte-1g1susb .editor__hint:where(.svelte-1g1susb) {
    /* Under the label, not under the box: the checkbox brings its own 8 px of touch area. */
    margin-left: 2px;
  }

  /* ---- side cards ---------------------------------------------------------------------------- */

  .card-title.svelte-1g1susb {
    display: block;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
    margin-bottom: 12px;
  }

  /* The same 12 px under the head as under a bare card title, so the first line of every card
     in the column starts at the same height (measured 45 px from the card's top). */
  .card-head.svelte-1g1susb {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 12px;
  }

  .card-head.svelte-1g1susb .card-title:where(.svelte-1g1susb) {
    margin-bottom: 0;
  }

  /* The one word (or figure) beside a card title: "in Ordnung", "Offline", "2 · seit 21:43". */
  .card-word.svelte-1g1susb {
    font-size: 11.5px;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
  }

  .card-word--ok.svelte-1g1susb {
    color: var(--s4q-success);
  }

  /* Die Fernwartungs-Karte: eine Zeile Erklaerung, darunter der Knopf, darunter der Fehler.
     Keine eigenen Farben - der Fehler nimmt dieselbe wie jede andere Fehlerzeile der Seite.

     **Und ausdruecklich nicht `.section-empty`.** Die bringt `padding: 16px 20px` mit, weil sie
     fuer Meldungen gedacht ist, die einen *ganzen Abschnitt* ersetzen - im Diagrammbereich etwa.
     In einer schmalen Karte neben dem Steckbrief sass der Text damit sichtbar weiter innen als
     dort, waehrend die Ueberschrift richtig stand. Gemeldet und nachgemessen: 20 px mehr links.
     Diese Zeile nimmt stattdessen das Mass der Karten daneben - 13 px, `margin: 0 0 12px`, wie
     deren `dd`. */
  /* Die Meldung ueber das letzte Ende: im Warnton, denn sie sagt, dass etwas zugegangen ist,
     ohne dass der Leser es veranlasst hat - aber keine Stoerung, das Geraet laeuft ja. */
  .remote__ended.svelte-1g1susb {
    margin: 0 0 10px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--s4q-warning);
  }

  /* Die Worte des Geraets, wo es welche hatte - leiser als der Satz davor und in eigener Zeile:
     Es ist eine Diagnose und keine Fortsetzung. */
  .remote__detail.svelte-1g1susb {
    display: block;
    color: var(--s4q-text-hint);
  }

  .remote__line.svelte-1g1susb {
    margin: 0 0 12px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  .remote__actions.svelte-1g1susb {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }

  .remote__error.svelte-1g1susb {
    margin: 12px 0 0;
    font-size: 13px;
    color: var(--s4q-danger);
  }

  .card-word--busy.svelte-1g1susb {
    color: var(--s4q-warning);
  }

  /* Explicit box, not --mdc-icon-button-size: MDC 14 does not read that variable, and the 48 px
     button made this card's head 36 px against the 17 px title line of the profile card - the
     two heads did not start at the same height (measured 25.5 px vs 16 px). Negative margins
     take the 30 px box back to the title's 17 px, so both cards share one rhythm. */
  .mdc-icon-button.card-refresh {
    width: 30px;
    height: 30px;
    padding: 6px;
    font-size: 18px;
    color: var(--s4q-text-secondary);
    margin: -6px -8px -7px 0;
  }

  /* Label above value, stacked - the side column is 300 px, a two-column list would break the
     values into two lines anyway. */
  .facts.svelte-1g1susb {
    margin: 0;
    font-size: 13px;
  }

  .facts.svelte-1g1susb dt:where(.svelte-1g1susb) {
    font-size: 11px;
    line-height: 1.3;
    color: var(--s4q-text-hint);
  }

  .facts.svelte-1g1susb dd:where(.svelte-1g1susb) {
    margin: 0 0 12px;
    line-height: 1.45;
    overflow-wrap: anywhere;
  }

  .facts.svelte-1g1susb dd:where(.svelte-1g1susb):last-child {
    margin-bottom: 0;
  }

  /* "keine" is an answer, not a value - the same tone the labels above it carry. */
  .facts__none.svelte-1g1susb {
    color: var(--s4q-text-hint);
  }

  .facts__link.svelte-1g1susb {
    color: var(--s4q-accent-text);
    text-decoration: none;
  }

  .facts__link.svelte-1g1susb:hover {
    text-decoration: underline;
  }

  .facts__id.svelte-1g1susb {
    font-size: 12px;
  }

  .stale.svelte-1g1susb {
    font-size: 12px;
    color: var(--s4q-warning);
    margin: 0 0 8px;
  }

  /* Die Zeile als Kopf der Inhaltsspalte: dieselbe Karte wie eine Tabelle, eine Zeile hoch.

     Keinen eigenen Abstand nach unten: Die 22 px des Spaltenrhythmus (`.main`) sind genau die,
     die unter der Zeile stehen sollen - siehe die Rechnung ueber `.dp-head`. */
  .strip.svelte-1g1susb {
    margin-bottom: 0;
  }

  /* Das Segment der Sichten sitzt in der Kopfzeile und traegt dort Anker statt Knoepfe - die
     gemeinsame Regel in `app.css` greift ueber die Klasse, nur der Unterstrich muss weg. */
  .views.svelte-1g1susb a.segment__item {
    text-decoration: none;
  }

  .views.svelte-1g1susb a.segment__item:hover {
    background: var(--s4q-row-hover);
    color: var(--mdc-theme-on-surface, #1b1b1b);
  }

  /* Das gesetzte Feld ist die offene Sicht und kein Bedienelement: kein Zeiger, kein Hover. */
  .views.svelte-1g1susb span.segment__item {
    cursor: default;
  }


  .card-foot.svelte-1g1susb {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--s4q-row-line);
    font-size: 11.5px;
  }

  .card-foot__hint.svelte-1g1susb {
    color: var(--s4q-text-hint);
  }

  .loading.svelte-1g1susb {
    display: flex;
    justify-content: center;
    padding: 48px;
  }

  .empty.svelte-1g1susb {
    color: var(--s4q-text-secondary);
  }

  .section-empty.svelte-1g1susb {
    font-size: 13px;
    margin: 0;
    padding: 16px 20px;
  }

  /* Inside a card's Content the padding is already there; the table's 20 px would indent the
     sentence against the title above it. */
  .section-empty--card.svelte-1g1susb {
    padding: 0;
  }
  /* The opened row: what was on the left, what applies on the right. The same split the device
     page itself uses, one level down - and the reason they sit together is that the chart is the
     argument for the threshold beside it. */
  .expand.svelte-1g1susb {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 370px;
    gap: 20px;
    /* Not `start`: the columns would be as tall as their content, and the rule between them
       would end where the shorter of the two does. Stretched it runs through, whichever side is
       longer. Nothing else changes - the columns have no surface of their own, and their content
       flows from the top either way. */
    align-items: stretch;
    padding: 14px 20px 16px;
    border-bottom: 1px solid var(--s4q-row-line);
    background: var(--s4q-expand-fill);
  }

  /* One column below that, chart first: a 370 px editor beside a 200 px chart is two cramped
     halves instead of one readable one. 730 px is what the two need - 370 for the editor (the
     width the five kinds need to stand on one line, measured 342), 20 gap, 40 padding and at
     least 300 left for the chart. */
  .expand--single.svelte-1g1susb {
    grid-template-columns: minmax(0, 1fr);
  }

  /* A rule between the two, so they read as two panels rather than as one long column of
     headings, and over the whole height of the expander (see `align-items` above). Only where
     they stand side by side - stacked, the line would cut the chart off from the fields below it,
     and the section headings already do the dividing there. */
  .expand.svelte-1g1susb:not(.expand--single) .editor:where(.svelte-1g1susb) {
    border-left: 1px solid var(--s4q-border-subtle);
    padding-left: 20px;
  }

  .expand__history.svelte-1g1susb {
    min-width: 0;
  }

  /* Both columns open with a head of the same height, which is what puts "Verlauf" and
     "Datenpunkt einstellen" on one line. The left one also carries the range switch; it wraps
     underneath the title where the column is too narrow for both, and the titles stay put. */
  .expand__head.svelte-1g1susb {
    display: flex;
    align-items: center;
    align-content: flex-start;
    flex-wrap: wrap;
    gap: 8px 12px;
    min-height: 30px;
    margin-bottom: 10px;
  }

  .expand__title.svelte-1g1susb {
    font-size: 12px;
    font-weight: 500;
    flex: none;
  }

  /* Rechts im Kopf und leiser als die Ueberschrift - dieselbe Rollenverteilung wie in der
     Vorschaukarte, wo `card-word` die Zahl neben `card-title` traegt. */
  .expand__total.svelte-1g1susb {
    margin-left: auto;
    font-size: 11.5px;
    color: var(--s4q-text-hint);
  }

  /* Die Zeile als Kopf der Inhaltsspalte: dieselbe Karte wie eine Tabelle, eine Zeile hoch. */
  .strip.svelte-w4wlvv {
    margin-bottom: 22px;
  }

  /* Ohne Bericht gibt es die Seitenspalte nicht - und dann darf ihre Spur nicht leer stehen
     bleiben, sonst endet die Karte darunter 316 px vor dem Rand. */
  .layout.layout--single.svelte-w4wlvv {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Das Segment der Sichten sitzt in der Kopfzeile und traegt dort Anker statt Knoepfe - die
     gemeinsame Regel in `app.css` greift ueber die Klasse, nur der Unterstrich muss weg. */
  .views.svelte-w4wlvv a.segment__item {
    text-decoration: none;
  }

  .views.svelte-w4wlvv a.segment__item:hover {
    background: var(--s4q-row-hover);
    color: var(--mdc-theme-on-surface, #1b1b1b);
  }

  /* Das gesetzte Feld ist die offene Sicht und kein Bedienelement: kein Zeiger, kein Hover. */
  .views.svelte-w4wlvv span.segment__item {
    cursor: default;
  }


  .loading.svelte-w4wlvv {
    display: flex;
    justify-content: center;
    padding: 32px 0;
  }

  .empty.svelte-w4wlvv {
    color: var(--s4q-text-secondary);
  }

  /* Dieselbe Ueberschrift wie "Datenpunkte" auf der Detailseite (`.dp-title`): 16 px/500. Sie
     waren 16 gegen 14 - gemeldet, und eine Seite erfindet keine eigene Typografie. */
  .section-heading.svelte-w4wlvv {
    font-size: 16px;
    font-weight: 500;
  }

  /* Ueberschrift links, Zeitraum rechts - eine Zeile, in der beide auf einer Grundlinie stehen.
     Schmal bricht sie um, statt den Zeitraum aus dem Bild zu schieben.

     **32 px hoch, auch ohne den Zeitraum-Schalter.** Sonst steht die Ueberschrift 5 px weiter oben,
     sobald fuer diese Organisation keine Aufzeichnung gebucht ist - und sie steht dann auch anders
     als "Datenpunkte" drueben, das in der 32 px hohen Chip-Zeile sitzt. Der Rest des Rhythmus steht
     dort beschrieben; er ist auf beiden Seiten derselbe. */
  .section-head.svelte-w4wlvv {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    min-height: 32px;
    gap: 10px 16px;
    margin: 0 0 16px 2px;
  }

  /* Drei Karten untereinander; die Diagramme sind breit, nicht hoch, und nebeneinander waere jedes
     zu schmal fuer eine lesbare Zeitachse. */
  .resources.svelte-w4wlvv {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }

  /* Die Kopfzeile einer Ressource: Beschriftung, Wert, Grenze - und rechts die Einzelheiten, die
     der Bericht sonst noch hergibt (Last 1/5/15, belegt von gesamt). */
  .res__head.svelte-w4wlvv {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin-bottom: 12px;
  }

  .card-title.svelte-w4wlvv {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
  }

  .res__value.svelte-w4wlvv {
    font-size: 19px;
    font-weight: 500;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
  }

  /* Ueber der Grenze faerbt sich die Zahl - im Warnton und nicht im Stoerungsrot: Eine Kiste unter
     Last laeuft, das ist eine Warnung und keine Stoerung. Dieselbe Entscheidung wie beim Band
     "System auffaellig". */
  .res__value--over.svelte-w4wlvv {
    color: var(--s4q-warning);
  }

  .res__limit.svelte-w4wlvv,
  .res__detail.svelte-w4wlvv {
    font-size: 12px;
    color: var(--s4q-text-hint);
  }

  .spacer.svelte-w4wlvv {
    flex: 1;
  }

  /* Dieselbe Aufteilung wie die Geraetedetailseite: Inhalt links, 300 px Steckbrief rechts, 22 px
     dazwischen. Unter 1100 px eine Spalte - die Seitenspalte drueckte die Diagramme sonst auf
     nichts zusammen. (Die 22 standen hier als 16, bis der Rhythmus der Spalte nachgerechnet
     wurde; "dieselbe Bauform" heisst auch dieselben Masse.) */
  .layout.svelte-w4wlvv {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 22px;
    align-items: start;
  }

  @media (max-width: 1100px) {
    .layout.svelte-w4wlvv {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  .main.svelte-w4wlvv,
  .side.svelte-w4wlvv {
    min-width: 0;
  }

  /* Die beiden Karten der Seitenspalte untereinander, mit denselben 22 px wie drueben - siehe den
     Rhythmus in `DeviceDetail.svelte`. Hier standen 16, obwohl der Kommentar "wie ueberall" sagte;
     aufgefallen, als der Anwender den Abstand der Karten als Mass fuer den der Ueberschrift nahm.
     **`minmax(0, 1fr)` und nicht `auto`**: Eine Rasterspalte ist so breit wie ihr Mindestinhalt,
     und ein Hostname wie `s4q-heizzentrale-nebengebaeude-ost-03.kunde.local` ist ein einziges
     Wort - die Karte wuchs ueber die 300 px hinaus und die Seite scrollte seitlich. Gemeldet, und
     es ist dieselbe Falle wie bei den Messwert-Slots der Geraeteliste: `min-width: auto` schlaegt
     jede feste Breite. Erst damit greift die Ellipse an `.facts__value`. */
  .side.svelte-w4wlvv {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 22px;
  }

  .side.svelte-w4wlvv .card-title:where(.svelte-w4wlvv) {
    display: block;
    margin-bottom: 12px;
  }

  /* Lange Werte (Dateiname, Projekt-ID, Hostname) schneiden ab und tragen ihren ganzen Text im
     Titel - in 300 px bricht sonst jede Projektierung um. Der Kopier-Knopf steht daneben und
     nimmt der Zeile keine Hoehe (negative Raender, wie in der Kopfzeile der Karten). */
  .facts__value.svelte-w4wlvv {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .facts__copy.svelte-w4wlvv {
    display: flex;
    align-items: center;
    gap: 2px;
    min-width: 0;
  }

  .facts__copy.svelte-w4wlvv .facts__value:where(.svelte-w4wlvv) {
    min-width: 0;
  }

  .mdc-icon-button.facts__copy-button {
    flex: none;
    width: 24px;
    height: 24px;
    padding: 4px;
    font-size: 16px;
    color: var(--s4q-text-secondary);
    margin: -6px 0;
  }

  /* **Beschriftung ueber dem Wert**, wie in den Karten der Detailseite - gemeldet, dass es hier
     anders aussah. Zweispaltig war richtig, solange die Karte die ganze Seite breit war; in 300 px
     braeche jeder zweite Wert um. */
  .facts.svelte-w4wlvv {
    margin: 0;
    font-size: 13px;
  }

  .facts.svelte-w4wlvv dt:where(.svelte-w4wlvv) {
    font-size: 11px;
    line-height: 1.3;
    color: var(--s4q-text-hint);
  }

  .facts.svelte-w4wlvv dd:where(.svelte-w4wlvv) {
    margin: 0 0 12px;
    line-height: 1.45;
    overflow-wrap: anywhere;
  }

  .facts.svelte-w4wlvv dd:where(.svelte-w4wlvv):last-child {
    margin-bottom: 0;
  }

  /* A remark about the section below, so it has to stay under the headings: at the 13 px the facts
     and the h3s share it read as a heading of its own. */
  .stale.svelte-w4wlvv {
    font-size: 12px;
    color: var(--s4q-warning);
    margin: 0 0 8px;
  }

  /* Inside the card this sentence stands in place of a section - heading and facts at once - so it
     takes their size and their spacing, not the page's 16 px. */
  .section-empty.svelte-w4wlvv {
    font-size: 13px;
    margin: 0;
  }

  /* Der Satz unter der Seite bezieht sich auf die Zahlen, nicht auf die Diagramme - die holen sich
     nicht alle drei Sekunden neu, siehe den Effekt. */

  /* Filter row, chips, rows and tones come from app.css - one definition for the three lists. */
  .chip__close.svelte-1llbit0 {
    font-size: 15px;
  }

  /* No icons: the bar on the left (row--fault/ok/neutral, app.css) says what kind of entry this
     is, and the sentence in the last column says the rest. A fault and a breached limit share the
     colour on purpose - which of them is more urgent is decided by the data point, not by the
     kind of event. */
  /* 900, nicht die 772, die die festen Spalten brauchen: Der Rest ist die Satz-Spalte, und ein
     Satz mit 18 px Platz ist keine Breitform. Gemessen bei 801 px Tabelle: die Spur war 18 px, die
     Standzeit ("seit 29 s", nowrap) ragte darueber hinaus und machte die Tabelle 17 px breiter als
     ihren Container - seitliches Scrollen in der Breitform, genau das, was nicht sein soll. Mit
     900 hat der Satz mindestens 128 px, darunter stapelt die Zeile (stackBelow, gleiche Zahl). */
  .row.svelte-1llbit0 {
    grid-template-columns: 26px 104px 196px 176px 104px minmax(0, 1fr) 30px;
    min-width: 900px;
  }

  /* Schmal: ein Stapel statt sieben Spalten. Oben das Geraet und rechts die Uhrzeit, darunter der
     Satz mit dem Wert daneben, zuletzt der Datenpunkt; die Marke haengt an der ersten Zeile, das
     Menue ebenso. Die Zellen bleiben, was sie sind - nur ihr Platz ist ein anderer, ueber
     grid-area. Bei einem Verbindungsereignis sind Punkt und Wert leer, und die dritte Zeile
     faellt auf null zusammen. Der Kopf entfaellt: Der Stapel sagt selbst, was er ist. */
  .table--stack .row.svelte-1llbit0:not(.row--head) {
    grid-template-columns: 26px minmax(0, 1fr) auto 30px;
    min-width: 0;
    grid-template-areas:
      'mark device time menu'
      'mark event value menu'
      'mark point point menu';
  }

  .table--stack .row--head.svelte-1llbit0 {
    display: none;
  }

  .table--stack .mark.svelte-1llbit0 {
    grid-area: mark;
  }

  .table--stack .cell-time.svelte-1llbit0 {
    grid-area: time;
  }

  .table--stack .cell-device.svelte-1llbit0 {
    grid-area: device;
  }

  .table--stack .cell-event.svelte-1llbit0 {
    grid-area: event;
  }

  /* Schmal darf der Satz umbrechen: Er ist die Nachricht der Zeile, und mit 180 px neben dem Wert
     war "Wert ausserhalb der Bedingung, seit 29 s" gemessen abgeschnitten. Auf dem Desktop bleibt
     die Ellipse - dort hat er eine ganze Spalte. */
  .table--stack .cell-event__text.svelte-1llbit0 {
    white-space: normal;
  }

  .table--stack .cell-value.svelte-1llbit0 {
    grid-area: value;
    text-align: right;
  }

  .table--stack .cell-point.svelte-1llbit0 {
    grid-area: point;
  }

  .table--stack .actions.svelte-1llbit0 {
    grid-area: menu;
    margin-top: -4px;
  }

  .cell-time.svelte-1llbit0 {
    color: var(--s4q-text-secondary);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  .cell-point.svelte-1llbit0,
  .cell-event__text.svelte-1llbit0 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-device.svelte-1llbit0 {
    min-width: 0;
  }

  /* Not in the brand red: at one entry per row the same name repeats down the whole page, and a
     column of red words fights with the marks that carry the actual message. It shows on hover. */
  .cell-device__name.svelte-1llbit0 {
    display: block;
    min-width: 0;
    font-size: 13px;
    font-weight: 500;
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-device__name.svelte-1llbit0:hover {
    color: var(--s4q-accent-text);
  }

  .cell-device__name--gone.svelte-1llbit0 {
    font-weight: 400;
    color: var(--s4q-text-hint);
    cursor: default;
  }

  .cell-point.svelte-1llbit0 {
    font-size: 12.5px;
    color: var(--s4q-text-secondary);
  }

  /* MDC positions the menu against the nearest *positioned* ancestor - without this it resolves
     against the page. Same rule, same measurement as in DeviceRowMenu. */
  .actions.svelte-1llbit0 {
    position: relative;
    display: flex;
    justify-content: center;
  }

  /* Right edge pinned to the button's - the menu opens leftwards into the row. Same rule and
     same reason as in DeviceRowMenu. */
  .actions.svelte-1llbit0 .mdc-menu-surface--open {
    left: auto !important;
    right: var(--menu-right) !important;
  }

  /* A plain button, not an SMUI IconButton: a thousand rows carry a thousand of these, and the
     ripple, the focus ring and their listeners are what would make this list expensive. The menu
     itself exists only for the row that is open. */
  .actions__button.svelte-1llbit0 {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--s4q-text-hint);
    cursor: pointer;
  }

  .row.svelte-1llbit0:hover .actions__button:where(.svelte-1llbit0) {
    color: var(--s4q-text-secondary);
  }

  .actions__button.svelte-1llbit0:hover {
    background: var(--s4q-fill-subtle);
    color: var(--s4q-accent-text);
  }

  .actions__button.svelte-1llbit0 .material-icons:where(.svelte-1llbit0) {
    font-size: 18px;
  }

  .row--fault.svelte-1llbit0 .cell-value:where(.svelte-1llbit0) {
    color: var(--s4q-fault);
  }

  .cell-event.svelte-1llbit0 {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    /* Die Standzeit ist nowrap und kann als Flex-Kind nicht schrumpfen; ohne dies raegte sie ueber
       die Spur hinaus und machte die Tabelle breiter als ihren Container (siehe .row). */
    overflow: hidden;
  }

  .row--neutral.svelte-1llbit0 .cell-event__text:where(.svelte-1llbit0) {
    color: var(--s4q-text-secondary);
  }

  .cell-event__dur.svelte-1llbit0 {
    flex: none;
    font-size: 12px;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
  }

  .loading.svelte-1llbit0 {
    display: flex;
    justify-content: center;
    padding: 32px 0;
  }

  .empty.svelte-1llbit0 {
    margin: 0;
    padding: 20px 16px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  .volatile.svelte-1llbit0 {
    margin: 2px 0 0;
    font-size: 11.5px;
    color: var(--s4q-text-hint);
  }

  .row.svelte-1f7mih6 {
    grid-template-columns: 26px minmax(140px, 1fr) minmax(180px, 1.4fr) 120px 110px;
    min-width: 720px;
  }

  /* Schmal: Geraet und Wert oben, darunter der Datenpunkt (Name ueber Art und Schluessel, wie
     bisher) und rechts daneben "seit". Der Kopf entfaellt wie bei den Ereignissen. */
  .table--stack .row.svelte-1f7mih6:not(.row--head) {
    grid-template-columns: 26px minmax(0, 1fr) auto;
    min-width: 0;
    grid-template-areas:
      'mark device value'
      'mark point since';
  }

  .table--stack .row--head.svelte-1f7mih6 {
    display: none;
  }

  .table--stack .mark.svelte-1f7mih6 {
    grid-area: mark;
  }

  .table--stack .cell-device.svelte-1f7mih6 {
    grid-area: device;
  }

  .table--stack .cell-point.svelte-1f7mih6 {
    grid-area: point;
  }

  .table--stack .cell-value.svelte-1f7mih6 {
    grid-area: value;
  }

  .table--stack .cell-since.svelte-1f7mih6 {
    grid-area: since;
    text-align: right;
  }

  .cell-device.svelte-1f7mih6,
  .cell-point.svelte-1f7mih6 {
    min-width: 0;
  }

  .cell-device__name.svelte-1f7mih6 {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
    color: inherit;
    text-decoration: none;
  }

  .cell-device__name.svelte-1f7mih6:hover {
    color: var(--s4q-accent-text);
  }

  .cell-point__name.svelte-1f7mih6,
  .cell-point__kind.svelte-1f7mih6 {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-point__name.svelte-1f7mih6 {
    line-height: 1.3;
  }

  .cell-point__kind.svelte-1f7mih6 {
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
  }

  /* Schrift, Ziffern und die Ellipse kommen aus app.css. */
  .cell-value.svelte-1f7mih6 {
    text-align: right;
    color: var(--s4q-fault);
  }

  .row--head.svelte-1f7mih6 .cell-value:where(.svelte-1f7mih6) {
    color: inherit;
  }

  .cell-since.svelte-1f7mih6 {
    font-variant-numeric: tabular-nums;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
  }

  /* The empty case is the good one and says so - a green mark, not an empty table with a head. */
  .quiet.svelte-1f7mih6 {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 72px 40px 78px;
    background: var(--mdc-theme-surface, #fff);
    border-radius: 4px;
    box-shadow:
      0 2px 1px -1px rgba(0, 0, 0, 0.2),
      0 1px 1px 0 rgba(0, 0, 0, 0.14),
      0 1px 3px 0 rgba(0, 0, 0, 0.12);
  }

  .quiet__mark.svelte-1f7mih6 {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--s4q-success);
    color: #fff;
    font-size: 20px;
    font-weight: 700;
    line-height: 1;
  }

  .quiet__title.svelte-1f7mih6 {
    margin: 0;
    font-size: 15px;
    font-weight: 500;
  }

  .quiet__body.svelte-1f7mih6 {
    margin: 0;
    max-width: 460px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--s4q-text-secondary);
  }

  .note.svelte-1f7mih6 {
    margin: 14px 0 0;
    max-width: 620px;
    font-size: 11.5px;
    line-height: 1.6;
    color: var(--s4q-text-hint);
  }

  .loading.svelte-1f7mih6 {
    display: flex;
    justify-content: center;
    padding: 48px 0;
  }

  .empty.svelte-1f7mih6 {
    margin: 0;
    padding: 16px 20px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  .fields.svelte-1sl9ljw {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding-top: 4px;
  }

  .field {
    width: 100%;
  }

  /* Tight under the field it belongs to, not a paragraph of its own. */
  .hint.svelte-1sl9ljw {
    margin: -8px 0 2px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .hint--next.svelte-1sl9ljw {
    margin: 4px 0 0;
  }

  /* A thin rule and its own space: what follows is not another rhythm. */
  .apart.svelte-1sl9ljw {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 4px;
    padding-top: 18px;
    border-top: 1px solid var(--s4q-row-line);
  }

  .devices.svelte-1sl9ljw {
    display: flex;
    flex-direction: column;
    max-height: 220px;
    overflow-y: auto;
    margin-top: -6px;
    padding: 2px 0;
    border: 1px solid var(--s4q-border-subtle);
    border-radius: 4px;
  }

  .device.svelte-1sl9ljw {
    font-size: 13.5px;
  }

  .device__type.svelte-1sl9ljw {
    margin-left: 6px;
    font-size: 11px;
    color: var(--s4q-text-hint);
  }

  .error.svelte-1sl9ljw {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 8px 0 0;
  }

  .row.svelte-hr9cmf {
    position: relative;
    grid-template-columns: 26px minmax(200px, 1fr) minmax(160px, 1fr) 110px 120px 30px;
    min-width: 760px;
  }

  /* The name is the one link of the row and its ::after is the row ("stretched link", see the
     device list for the reasoning). Nothing sits above it: the row has no menu. */
  .name.svelte-hr9cmf {
    color: inherit;
    text-decoration: none;
  }

  .name.svelte-hr9cmf::after {
    content: '';
    position: absolute;
    inset: 0;
  }

  /* Stacked (stackBelow at .table-scroll): the group over its rhythm, then recipients and
     devices, "last sent" and the menu at the right - the same shape as the other lists. */
  .table--stack .row.svelte-hr9cmf:not(.row--head) {
    grid-template-columns: 26px minmax(0, 1fr) auto 30px;
    min-width: 0;
    grid-template-areas:
      'mark group sent menu'
      'mark recipients devices menu';
  }

  .table--stack .row--head.svelte-hr9cmf {
    display: none;
  }

  .table--stack .mark.svelte-hr9cmf {
    grid-area: mark;
  }

  .table--stack .cell-group.svelte-hr9cmf {
    grid-area: group;
  }

  .table--stack .cell-recipients.svelte-hr9cmf {
    grid-area: recipients;
  }

  .table--stack .cell-devices.svelte-hr9cmf {
    grid-area: devices;
    text-align: right;
  }

  .table--stack .cell-sent.svelte-hr9cmf {
    grid-area: sent;
    text-align: right;
  }

  .table--stack .cell-chevron.svelte-hr9cmf {
    grid-area: menu;
  }

  .cell-group.svelte-hr9cmf,
  .cell-recipients.svelte-hr9cmf {
    min-width: 0;
  }

  .cell-group__name.svelte-hr9cmf {
    display: block;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-group__off.svelte-hr9cmf {
    margin-left: 6px;
    font-size: 11px;
    font-weight: 400;
    color: var(--s4q-text-hint);
  }

  .cell-group__rhythm.svelte-hr9cmf {
    display: block;
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-recipients.svelte-hr9cmf {
    font-size: 12.5px;
    color: var(--s4q-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .cell-devices.svelte-hr9cmf,
  .cell-sent.svelte-hr9cmf {
    font-size: 12.5px;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  .cell-chevron.svelte-hr9cmf {
    font-size: 18px;
    color: var(--s4q-text-hint);
    text-align: center;
  }

  .quiet.svelte-hr9cmf {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 72px 40px 78px;
    background: var(--mdc-theme-surface, #fff);
    border-radius: 4px;
    box-shadow:
      0 2px 1px -1px rgba(0, 0, 0, 0.2),
      0 1px 1px 0 rgba(0, 0, 0, 0.14),
      0 1px 3px 0 rgba(0, 0, 0, 0.12);
  }

  .quiet__title.svelte-hr9cmf {
    margin: 0;
    font-size: 15px;
    font-weight: 500;
  }

  .quiet__body.svelte-hr9cmf {
    margin: 0;
    max-width: 520px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--s4q-text-secondary);
  }

  .note.svelte-hr9cmf {
    margin: 14px 0 0;
    max-width: 620px;
    font-size: 11.5px;
    line-height: 1.6;
    color: var(--s4q-text-hint);
  }

  .loading.svelte-hr9cmf {
    display: flex;
    justify-content: center;
    padding: 48px 0;
  }

  .fields.svelte-alp0q8 {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding-top: 4px;
  }

  .field {
    width: 100%;
  }

  /* Tight under the field it belongs to, not a paragraph of its own. */
  .hint.svelte-alp0q8 {
    margin: -10px 0 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .hint--error.svelte-alp0q8 {
    color: var(--mdc-theme-error, #b3261e);
  }

  .hint--warn.svelte-alp0q8 {
    margin-top: 2px;
    color: var(--s4q-warning);
  }

  .hint--warn.svelte-alp0q8 a:where(.svelte-alp0q8) {
    color: inherit;
  }

  /* Der Code steht so da, wie er getippt wird - dicktengleich, damit 0 und O sich nicht
     verwechseln lassen, und markierbar, weil manche ihn kopieren statt abzutippen. */
  .code.svelte-alp0q8 {
    margin: -6px 0 0;
    padding: 8px 12px;
    border: 1px solid var(--s4q-border-subtle);
    border-radius: 4px;
    background: var(--s4q-fill-subtle);
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 14px;
    letter-spacing: 0.5px;
    user-select: all;
  }

  /* Der verbundene Chat: die eine Zeile, die sagt, dass der Ablauf durch ist. */
  .paired.svelte-alp0q8 {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 13px;
    color: var(--s4q-success);
  }

  .paired.svelte-alp0q8 .material-icons:where(.svelte-alp0q8) {
    font-size: 18px;
  }

  .hint--row.svelte-alp0q8 {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 12px;
  }

  .link.svelte-alp0q8 {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 12px;
    color: var(--mdc-theme-primary);
    cursor: pointer;
  }

  .link.svelte-alp0q8:hover {
    text-decoration: underline;
  }

  .error.svelte-alp0q8 {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 8px 0 0;
  }

  .group-actions.svelte-1pas28p {
    position: relative;
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
  }

  /* Main column and a narrow side column, as on the device page. Below 1100 px the side column
     drops underneath. */
  .layout.svelte-1pas28p {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 22px;
    align-items: start;
  }

  @media (max-width: 1100px) {
    .layout.svelte-1pas28p {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  .main.svelte-1pas28p {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 22px;
  }

  .side.svelte-1pas28p {
    display: flex;
    flex-direction: column;
    gap: 22px;
  }

  .card-title.svelte-1pas28p {
    display: block;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
    margin-bottom: 12px;
  }

  .card-head.svelte-1pas28p {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 12px;
  }

  .card-head.svelte-1pas28p .card-title:where(.svelte-1pas28p) {
    margin-bottom: 0;
  }

  .spacer.svelte-1pas28p {
    flex: 1 1 auto;
  }

  /* The text button in a card head, pulled back to the title's line height: the 36 px button
     would otherwise push every row under it 10 px down against the side cards. */
  .card-head.svelte-1pas28p .card-action {
    margin: -10px -8px -10px 0;
  }

  .card-word.svelte-1pas28p {
    font-size: 11.5px;
    color: var(--s4q-text-secondary);
    white-space: nowrap;
  }

  .card-word--ok.svelte-1pas28p {
    color: var(--s4q-success);
  }

  .card-word--fault.svelte-1pas28p {
    color: var(--s4q-danger);
  }

  /* ---- recipients ---------------------------------------------------------------------------- */

  .recipients.svelte-1pas28p {
    display: flex;
    flex-direction: column;
    margin: 0 -16px -8px;
  }

  /* A row of the shared list handwriting, without the table around it: icon, two lines, menu. */
  .recipient.svelte-1pas28p {
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr) 30px;
    align-items: center;
    gap: 12px;
    min-height: 52px;
    padding: 6px 16px;
    border-top: 1px solid var(--s4q-row-line);
  }

  .recipient__icon.svelte-1pas28p {
    font-size: 20px;
    color: var(--s4q-text-hint);
  }

  .recipient__text.svelte-1pas28p {
    min-width: 0;
  }

  .recipient__label.svelte-1pas28p {
    display: block;
    font-size: 13.5px;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .recipient__detail.svelte-1pas28p {
    display: block;
    font-size: 11px;
    line-height: 1.25;
    color: var(--s4q-text-hint);
  }

  /* Configured but silent: a warning, not a fault - the recipient is right, the organization
     just has no gateway account yet. */
  .recipient__detail--warn.svelte-1pas28p {
    color: var(--s4q-warning);
  }

  .section-empty.svelte-1pas28p {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--s4q-text-secondary);
  }

  .actions.svelte-1pas28p {
    position: relative;
    display: flex;
    justify-content: center;
  }

  .actions.svelte-1pas28p .mdc-menu-surface--open {
    left: auto !important;
    right: var(--menu-right) !important;
  }

  .actions__button.svelte-1pas28p {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--s4q-text-hint);
    cursor: pointer;
  }

  .recipient.svelte-1pas28p:hover .actions__button:where(.svelte-1pas28p) {
    color: var(--s4q-text-secondary);
  }

  .actions__button.svelte-1pas28p:hover {
    background: var(--s4q-fill-subtle);
    color: var(--s4q-accent-text);
  }

  .actions__button.svelte-1pas28p .material-icons:where(.svelte-1pas28p) {
    font-size: 18px;
  }

  /* ---- facts --------------------------------------------------------------------------------- */

  .facts.svelte-1pas28p {
    margin: 0;
    font-size: 13px;
  }

  .facts.svelte-1pas28p dt:where(.svelte-1pas28p) {
    font-size: 11px;
    line-height: 1.3;
    color: var(--s4q-text-hint);
  }

  .facts.svelte-1pas28p dd:where(.svelte-1pas28p) {
    margin: 0 0 12px;
    line-height: 1.45;
    overflow-wrap: anywhere;
  }

  .facts.svelte-1pas28p dd:where(.svelte-1pas28p):last-child {
    margin-bottom: 0;
  }

  .facts__link.svelte-1pas28p {
    color: var(--s4q-accent-text);
    text-decoration: none;
  }

  .facts__link.svelte-1pas28p:hover {
    text-decoration: underline;
  }

  .stale.svelte-1pas28p {
    font-size: 12px;
    color: var(--s4q-text-secondary);
    margin: 0 0 10px;
  }

  .card-foot.svelte-1pas28p {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--s4q-row-line);
    font-size: 11.5px;
  }

  .card-foot__hint.svelte-1pas28p {
    color: var(--s4q-text-hint);
    line-height: 1.4;
  }

  .card-link.svelte-1pas28p {
    margin-left: auto;
    color: var(--s4q-accent-text);
    text-decoration: none;
    white-space: nowrap;
  }

  .card-link.svelte-1pas28p:hover {
    text-decoration: underline;
  }

  /* ---- devices ------------------------------------------------------------------------------- */

  .devices-rule.svelte-1pas28p {
    margin: 0 0 10px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .devices.svelte-1pas28p {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 13px;
  }

  .devices.svelte-1pas28p li:where(.svelte-1pas28p) {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    line-height: 1.45;
    margin-bottom: 6px;
  }

  .devices.svelte-1pas28p li:where(.svelte-1pas28p):last-child {
    margin-bottom: 0;
  }

  .devices__type.svelte-1pas28p {
    font-size: 11px;
    color: var(--s4q-text-hint);
    white-space: nowrap;
  }

  .confirm.svelte-1pas28p {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
  }

  .empty.svelte-1pas28p {
    color: var(--s4q-text-secondary);
  }

  .loading.svelte-1pas28p {
    display: flex;
    justify-content: center;
    padding: 48px 0;
  }

  .hint.svelte-1ihkqyo {
    font-size: 13px;
    color: var(--s4q-text-secondary);
    margin: 0 0 16px;
  }

  .fields.svelte-1ihkqyo {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding-top: 4px;
  }

  .field {
    width: 100%;
  }

  .error.svelte-1ihkqyo {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }

  .fields.svelte-15u79l2 {
    padding-top: 4px;
  }

  .field {
    width: 100%;
  }

  .hint.svelte-15u79l2 {
    font-size: 12px;
    color: var(--s4q-text-secondary);
    margin: 12px 0 0;
  }

  .error.svelte-15u79l2 {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 12px 0 0;
  }

  .intro.svelte-qib6kt {
    margin: 0 0 16px;
    font-size: 13px;
    line-height: 1.55;
    color: var(--s4q-text-secondary);
  }

  .fields.svelte-qib6kt {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .field {
    width: 100%;
  }

  /* Tight under the field it belongs to, not a paragraph of its own. */
  .hint.svelte-qib6kt {
    margin: -10px 0 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .hint--error.svelte-qib6kt {
    color: var(--mdc-theme-error, #b3261e);
  }

  .hint--remove.svelte-qib6kt {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 8px;
    margin-top: 2px;
  }

  .link.svelte-qib6kt {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 12px;
    color: var(--mdc-theme-primary);
    cursor: pointer;
  }

  .link.svelte-qib6kt:hover:not(:disabled) {
    text-decoration: underline;
  }

  .error.svelte-qib6kt {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 8px 0 0;
  }

  .field {
    width: 100%;
  }

  .hint.svelte-1v7h890 {
    margin: 8px 0 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--s4q-text-secondary);
  }

  .error.svelte-1v7h890 {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 10px 0 0;
  }

  .page.svelte-adbamk .mdc-card + .mdc-card {
    margin-top: 16px;
  }

  /* **Ein Kopf für jede Karte, und er ist überall gleich hoch.** Gemeldet als „je nachdem, ob
     ein Knopf da ist und welcher, ist die Box anders" - und nachgemessen stimmte genau das: Der
     MDC-IconButton ist 48 px hoch, der gefüllte Knopf 36, ohne Knopf war der Kopf so hoch wie das
     `h2`. Drei Karten, drei Höhen.

     Jetzt trägt der Kopf die feste Höhe seines größten Elements (30 px), und beide Arten von
     Aktion sind auf dieses Maß gebracht (`.card-action`). Die Beschriftung ist dieselbe wie auf
     der Gerätedetailseite: kleine Versalien statt eines `h2` - zwei Kartenformen in einem Portal
     waren eine zu viel. */
  .card-head.svelte-adbamk {
    display: flex;
    align-items: center;
    gap: 9px;
    min-height: 30px;
    margin-bottom: 12px;
  }

  .card-title.svelte-adbamk {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
  }

  .spacer.svelte-adbamk {
    flex: 1;
  }

  /* Ausdrücklicher Kasten, nicht `--mdc-icon-button-size`: MDC 14 liest die Variable nicht - das
     ist derselbe Fallstrick, an dem die Zeilen der Geräteliste einmal 69 px hoch waren. */
  .mdc-icon-button.card-action {
    width: 30px;
    height: 30px;
    padding: 6px;
    font-size: 18px;
    color: var(--s4q-text-secondary);
  }

  /* Und der gefüllte Knopf auf dasselbe Maß: Er ist die Hauptaktion der Karte und bleibt
     beschriftet (die Regel „Icon oder Beschriftung"), nur eben so hoch wie der Kopf. */
  .mdc-button.card-action {
    height: 30px;
    font-size: 12.5px;
  }

  .facts.svelte-adbamk {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 6px 20px;
    margin: 12px 0 0;
    font-size: 14px;
  }

  .facts.svelte-adbamk dt:where(.svelte-adbamk) {
    color: var(--s4q-text-secondary);
  }

  .facts.svelte-adbamk dd:where(.svelte-adbamk) {
    margin: 0;
  }

  .muted.svelte-adbamk {
    color: var(--s4q-text-secondary);
  }

  /* Die Adresse und ihr Kopier-Knopf auf einer Linie, ohne die Zeile höher zu machen. */
  .host.svelte-adbamk {
    display: inline-flex;
    align-items: center;
    gap: 2px;
  }

  .host.svelte-adbamk .mdc-icon-button.card-action {
    margin: -7px 0;
  }

  /* Der Satz zur Buchung steht hinter „nicht gebucht", nicht darunter: Er erklärt genau dieses
     eine Wort, und eine eigene Zeile machte daraus einen Hinweis für die ganze Karte. */
  .hint.svelte-adbamk {
    color: var(--s4q-text-hint);
    font-size: 12.5px;
  }

  .warn.svelte-adbamk {
    color: var(--s4q-warning);
  }

  .members.svelte-adbamk {
    margin-top: 4px;
  }

  /* **Ein Raster, keine Flex-Reihe** - dieselbe Antwort, die die drei Listen auf dieselbe Frage
     geben. Vorher floss die Zeile, und die Marke „Einladung offen" schob in genau den Zeilen, die
     sie trägt, alles Folgende nach rechts; gemeldet als „der Rest wirkt nicht wie eine Tabelle".
     Mit festen Spalten steht die Rolle in jeder Zeile an derselben Stelle, mit und ohne
     Einladung. */
  .member.svelte-adbamk {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 150px 120px 68px;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--s4q-border-subtle);
  }

  /* Ein Tag hat weder Rolle noch Status. */
  .member--plain.svelte-adbamk {
    grid-template-columns: minmax(0, 1fr) 68px;
  }

  .member.svelte-adbamk:last-child {
    border-bottom: none;
  }

  .member__identity.svelte-adbamk {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .member__name.svelte-adbamk,
  .member__email.svelte-adbamk {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .member__name.svelte-adbamk {
    font-size: 14px;
    font-weight: 500;
  }

  .member__self.svelte-adbamk {
    font-weight: 400;
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  .member__email.svelte-adbamk {
    font-size: 12px;
    color: var(--s4q-text-secondary);
  }

  .member__status.svelte-adbamk {
    display: flex;
    justify-content: flex-start;
    min-width: 0;
  }

  .member__pending.svelte-adbamk {
    font-size: 12px;
    padding: 2px 10px;
    border-radius: 999px;
    background: var(--s4q-fill-subtle);
    color: var(--s4q-warning);
    white-space: nowrap;
  }

  /* Das Feld kommt aus `app.css` (`.filter-select__field`) und ist damit 32 px hoch wie überall
     sonst; hier steht nur, wie breit es in dieser Spalte sein darf. */
  .member__role.svelte-adbamk {
    width: 100%;
  }

  .member__role.svelte-adbamk:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }

  .member__actions.svelte-adbamk {
    display: flex;
    align-items: center;
    justify-content: flex-end;
  }

  /* Schmal (die Klasse der Hülle, siehe AppShell): Identität + Rolle + Status + Aktionen sind
     rund 470 px, die Karte bietet etwa 320. Aus vier Spalten werden zwei Reihen - oben Name und
     Adresse neben dem Status, darunter die Rolle neben den Aktionen. Diese Zeile war einmal das
     eine Element, das eine 375-px-Ansicht seitlich scrollen ließ. */
  .shell--compact .member.svelte-adbamk {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  /* Ausdrücklich gesetzt und nicht der automatischen Platzierung überlassen: Die füllt in der
     Reihenfolge des Markups, und dann stünde der Status unter dem Namen statt neben ihm. Oben
     Identität und Status, darunter Rolle und Aktionen. */
  .shell--compact .member__identity.svelte-adbamk {
    grid-area: 1 / 1;
  }

  .shell--compact .member__status.svelte-adbamk {
    grid-area: 1 / 2;
    justify-content: flex-end;
  }

  .shell--compact .member__role.svelte-adbamk {
    grid-area: 2 / 1;
  }

  .shell--compact .member__actions.svelte-adbamk {
    grid-area: 2 / 2;
  }

  .shell--compact .member--plain.svelte-adbamk {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .shell--compact .member--plain.svelte-adbamk .member__actions:where(.svelte-adbamk) {
    grid-area: 1 / 2;
  }

  .loading.svelte-adbamk {
    display: flex;
    justify-content: center;
    padding: 48px;
  }

  .page.svelte-rj7pkj .mdc-card + .mdc-card {
    margin-top: 16px;
  }

  h2.svelte-rj7pkj {
    font-size: 16px;
    margin: 0 0 4px;
  }

  .hint.svelte-rj7pkj {
    font-size: 13px;
    color: var(--s4q-text-secondary);
    margin: 0 0 16px;
  }

  .fields.svelte-rj7pkj {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 420px;
  }

  .fields.svelte-rj7pkj .hint:where(.svelte-rj7pkj) {
    margin: -8px 0 0;
  }

  .actions.svelte-rj7pkj {
    margin-top: 20px;
  }

  .error.svelte-rj7pkj {
    color: var(--mdc-theme-error, #b3261e);
    font-size: 13px;
    margin: 0;
  }

  .password-toggle {
    cursor: pointer;
    user-select: none;
  }

  /* The only place in the portal where a number is the message, so it may be large - the bar next
     to it already says in words what happened. */
  .code.svelte-1h4jfc7 {
    margin: 0;
    font-size: 40px;
    font-weight: 500;
    color: var(--s4q-text-hint);
  }

  .bands.svelte-ydgvcj {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 26px;
  }

  /* Die Klasse setzt `stackBelow` zur Laufzeit, sie steht also in keinem Markup - ohne
     `:global` haelt der Compiler die Regel fuer tot und wirft sie weg. Die Scope-Klasse bleibt
     ueber `.bands` daran. */
  .bands.svelte-ydgvcj.bands--stack {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Dieselbe Bauform wie eine Zeile: Fläche, Radius und Schatten der Karte, damit die vier Zahlen
     nicht wie ein fünftes Bedienelement aussehen. */
  .band.svelte-ydgvcj {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--mdc-theme-surface, #fff);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    text-decoration: none;
    color: inherit;
  }

  .band.svelte-ydgvcj:hover {
    background: var(--s4q-row-hover);
  }

  .band__count.svelte-ydgvcj {
    font-size: 19px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
  }

  .band__count--fault.svelte-ydgvcj {
    color: var(--s4q-fault);
  }

  .band__count--attention.svelte-ydgvcj {
    color: var(--s4q-attention);
  }

  .band__label.svelte-ydgvcj {
    font-size: 11px;
    color: var(--s4q-text-secondary);
  }

  /* Der Abstand gehoert an die Ueberschrift und nicht zwischen Tabelle und Ueberschrift: Zwischen
     beiden kann eine Fussnote stehen, und dann trennte die Regel nichts. */
  .block__title.svelte-ydgvcj {
    margin: 26px 0 10px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
  }

  .row.svelte-ydgvcj {
    grid-template-columns: 26px minmax(0, 1fr) auto;
  }

  .cell-device.svelte-ydgvcj {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }

  .cell-device__name.svelte-ydgvcj {
    font-weight: 500;
    color: inherit;
    text-decoration: none;
  }

  .cell-device__name.svelte-ydgvcj:hover {
    text-decoration: underline;
  }

  .cell-device__meta.svelte-ydgvcj,
  .cell-since.svelte-ydgvcj {
    font-size: 11px;
    color: var(--s4q-text-secondary);
  }

  .cell-gap__text.svelte-ydgvcj {
    min-width: 0;
  }

  .cell-link.svelte-ydgvcj {
    color: var(--s4q-accent-text);
    text-decoration: none;
    white-space: nowrap;
  }

  .cell-link.svelte-ydgvcj:hover {
    text-decoration: underline;
  }

  .block__title.svelte-ydgvcj:first-of-type {
    margin-top: 0;
  }

  .note.svelte-ydgvcj {
    margin: 8px 0 0;
    font-size: 11px;
    color: var(--s4q-text-hint);
  }

  .clear.svelte-ydgvcj {
    margin: 0;
    font-size: 13.5px;
    color: var(--s4q-text-secondary);
  }

  .loading.svelte-ydgvcj {
    display: flex;
    justify-content: center;
    padding: 48px 0;
  }

  /* Der leere Fall ist die gute Nachricht und sieht so aus - dieselbe Form wie auf der Seite der
     offenen Störungen. */
  .quiet.svelte-ydgvcj {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 56px 24px;
    text-align: center;
  }

  .quiet__mark.svelte-ydgvcj {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--s4q-success);
    color: #fff;
    font-size: 15px;
    line-height: 1;
  }

  .quiet__title.svelte-ydgvcj {
    margin: 0;
    font-size: 15px;
    font-weight: 500;
  }

  .quiet__body.svelte-ydgvcj {
    margin: 0;
    max-width: 520px;
    font-size: 13px;
    color: var(--s4q-text-secondary);
  }

  /* Dieselben 30 px wie auf der Organisationsseite und den Karten der Geraeteseiten - eine
     Kopfhoehe fuer alle Karten des Portals. */
  .card-head.svelte-fjjr6b {
    display: flex;
    align-items: center;
    min-height: 30px;
    margin-bottom: 12px;
  }

  .card-title.svelte-fjjr6b {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--s4q-text-secondary);
  }

  /* 13,5 px wie der Inhalt der Listen, und eine grosszuegigere Zeilenhoehe: Das hier wird gelesen
     und nicht ueberflogen. Der Deckel haelt die Zeile lesbar - ueber eine ganze Kartenbreite
     findet das Auge den Zeilenanfang nicht wieder. Der letzte Absatz traegt keinen unteren
     Abstand: Der Schluss gehoert dem Innenmass der Karte. */
  .prose.svelte-fjjr6b {
    margin: 0 0 10px;
    max-width: 72ch;
    font-size: 13.5px;
    line-height: 1.65;
  }

  .prose.svelte-fjjr6b:last-child {
    margin-bottom: 0;
  }

  /* Das Band ist an der laengsten Beschriftung gemessen, nicht geschaetzt: „Offene Stoerungen"
     misst 116 px. 10 rem lassen dem Datum der Release-Spalte daneben Platz, ohne dass es
     umbricht. */
  .entries.svelte-fjjr6b {
    display: grid;
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: 14px 16px;
    margin: 0;
    align-items: baseline;
  }

  .entries.svelte-fjjr6b dt:where(.svelte-fjjr6b) {
    font-size: 13.5px;
    font-weight: 500;
  }

  .entries.svelte-fjjr6b dd:where(.svelte-fjjr6b) {
    margin: 0;
    max-width: 72ch;
    font-size: 13.5px;
    line-height: 1.65;
    color: var(--s4q-text-secondary);
  }

  /* Ein Release ist ein Block und keine Zeile - der Abstand zwischen zweien muss deshalb groesser
     sein als der zwischen Begriff und Begriff. */
  .entries--release.svelte-fjjr6b {
    row-gap: 22px;
  }

  /* Datum unter die Version: Sie ist der Bezeichner und traegt das Gewicht, das Datum ist die
     Einordnung. Nebeneinander waere das Band doppelt so breit, nur fuer eine Nebensache. */
  .release__date.svelte-fjjr6b {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    font-weight: 400;
    color: var(--s4q-text-secondary);
  }

  .release__title.svelte-fjjr6b {
    margin: 0 0 8px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--mdc-theme-text-primary-on-background);
  }

  .release__list.svelte-fjjr6b {
    margin: 0;
    padding-left: 18px;
  }

  .release__list.svelte-fjjr6b li:where(.svelte-fjjr6b) + li:where(.svelte-fjjr6b) {
    margin-top: 6px;
  }

  /* Schmal faellt das Band weg und der Begriff steht ueber seinem Text: 10 rem plus ein Satz gehen
     sich auf einem Telefon nicht aus, und dann ist der Stapel die richtige Form - dieselbe
     Entscheidung wie bei den Zeilen der Listen, und gesetzt wird sie von derselben Aktion. */
  .entries.svelte-fjjr6b.entries--stack {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
  }

  /* `:global` an der Klasse und nicht am Ganzen: Sie kommt von der Aktion und steht im Markup nur
     als Zeichenkette, und einen *Nachfahren* darunter erkennt Svelte daran nicht mehr - die beiden
     Regeln galten als unbenutzt und waeren beim Bauen weggefallen. Der `dd`-Teil bleibt damit
     gekapselt. */
  .entries.svelte-fjjr6b.entries--stack dd:where(.svelte-fjjr6b) {
    margin-bottom: 14px;
  }

  .entries.svelte-fjjr6b.entries--stack dd:where(.svelte-fjjr6b):last-child {
    margin-bottom: 0;
  }

  .boot-loading.svelte-1n46o8q {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
  }
