/* =========================================================
   Ticget — Gestaltungssystem
   =========================================================

   Ein Tokensatz fuer alle Bereiche. Tickets, Ueberwachung und
   Einstellungen greifen auf dieselben Werte zu; es gibt keine
   Sonderwege je Bereich mehr.

   Grundsaetze:
   - Volle Bildschirmbreite. Nur Fliesstext wird begrenzt, damit
     Zeilen lesbar bleiben.
   - Schriftgroessen beginnen bei 12px. Der Vorgaenger setzte
     Beschriftungen auf 8px und 9px.
   - Dunkel ist der Auslieferungszustand.
   - Bedienelemente sind mindestens 40px hoch — auch mit dem Finger
     zu treffen.
   ========================================================= */

/* ---------- Schriften ----------

   IBM Plex, mitgeliefert und einkompiliert. Kein Google-Fonts-Verweis,
   kein CDN: die Anwendung sieht in einem abgeschotteten Netz genauso
   aus wie mit Internetzugang, und es geht keine Information darueber
   hinaus, wer die Seite wann aufruft.

   Ausgewaehlt, weil sie fuer technische Werkzeuge entworfen wurde:
   klare Ziffern, gute Unterscheidung von 1/l/I und 0/O, vollstaendige
   deutsche Abdeckung. Die Mono traegt Ticketnummern und Messwerte.

   Lizenz: SIL Open Font License 1.1, siehe vendor/LICENSE-ibm-plex.txt

   Sollten die Dateien je ausgetauscht werden, muss der Dateiname sich
   mitaendern — die Adressen hier tragen keine Inhaltskennung. */

@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("vendor/plex-sans-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("vendor/plex-sans-500.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("vendor/plex-sans-600.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("vendor/plex-sans-700.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("vendor/plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("vendor/plex-mono-500.woff2") format("woff2");
}

/* ---------- Tokens: dunkel (Standard) ---------- */

:root,
:root[data-theme="dark"] {
  --ground:       #020617;
  --surface:      #0b1220;
  --surface-2:    #131c2e;
  --surface-3:    #1e293b;
  --surface-4:    #334155;

  --ink:          #f1f5f9;
  --ink-2:        #94a3b8;
  --ink-3:        #64748b;

  --line:         #1e293b;
  --line-strong:  #334155;

  --accent:        #38bdf8;
  --accent-ink:    #041523;
  --accent-soft:   #0c2b40;
  --accent-line:   #0ea5e9;
  /* Das Sky, das Text tragen darf — als Flaeche unter heller Schrift
     ebenso wie als Schriftfarbe selbst.

     In der dunklen Ansicht ist das derselbe Wert wie --accent. #38bdf8
     steht auf --surface bei 8,7:1 und traegt --accent-ink bei 8,6:1; es
     gibt nichts zu korrigieren. Der Token wird hier trotzdem gesetzt,
     damit die Regeln, die ihn benutzen, in beiden Ansichten dieselben
     bleiben. Warum es ihn ueberhaupt gibt, steht bei der hellen. */
  --accent-strong: #38bdf8;
  /* Die eigene Chatblase traegt Sky als Flaeche. Sie ist dunkler als
     --accent, weil darauf Fliesstext steht und nicht nur ein Wort. */
  --bubble-me:     #0369a1;
  --bubble-me-ink: #f0f9ff;

  --ok:           #34d399;
  --ok-soft:      #062e22;
  --warn:         #fbbf24;
  --warn-soft:    #2e2206;
  --crit:         #f87171;
  --crit-soft:    #340f0f;

  /* Verweise, Navigation und alles, was ueber dem Inhalt liegt.

     Sie standen frueher nicht als eigene Token da: ein Verweis nahm
     einfach --accent-strong, die Kopfleiste mischte sich --surface mit
     etwas Durchsicht zurecht. Damit liessen sie sich nicht einstellen,
     und genau das war die Beschwerde. Die Werte sind dieselben wie
     zuvor — neu ist nur, dass jeder von ihnen einen Namen hat.

     Die achtstellige Form traegt die Deckkraft: `eb` sind die 92
     Prozent, mit denen die Kopfleiste den Inhalt durchscheinen laesst. */
  --link:             #38bdf8;
  --nav-bg:           #0b1220eb;
  --nav-ink:          #94a3b8;
  --nav-ink-soft:     #64748b;
  --nav-active:       #38bdf8;
  --nav-active-line:  #0ea5e9;
  --menu-bg:          #0b1220;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5);
  --shadow:    0 2px 4px rgb(0 0 0 / 0.4), 0 12px 32px rgb(0 0 0 / 0.28);
  --scrim:     #020617b8;
}

/* ---------- Tokens: hell (nur auf ausdrueckliche Wahl) ---------- */

:root[data-theme="light"] {
  --ground:       #f1f5f9;
  --surface:      #ffffff;
  --surface-2:    #f8fafc;
  --surface-3:    #eef2f7;
  --surface-4:    #dde5ee;

  --ink:          #0f172a;
  --ink-2:        #475569;
  --ink-3:        #7c8ba1;

  --line:         #e2e8f0;
  --line-strong:  #cbd5e1;

  --accent:        #0ea5e9;
  --accent-ink:    #ffffff;
  --accent-soft:   #e0f2fe;
  --accent-line:   #0284c7;
  /* Hier liegt der Grund fuer den Token: --accent ist in der hellen
     Ansicht #0ea5e9 und traegt weisse Schrift nur bei 2,8:1 — unter der
     WCAG-AA-Grenze von 4,5:1. Dieses dunklere Sky schafft 5,9:1 unter
     weisser Schrift, 5,9:1 als Schrift auf --surface und 5,2:1 als
     Schrift auf --accent-soft.

     Wer das hier auf --accent zurueckdreht, macht in der hellen Ansicht
     jeden Verweis, jeden Hauptknopf und jede Info-Plakette unlesbar,
     ohne dass ein Test anschlaegt. Das hellere --accent bleibt fuer
     Raender, Unterstriche und Punkte — dort steht kein Text darauf. */
  --accent-strong: #0369a1;

  --bubble-me:     #0284c7;
  --bubble-me-ink: #ffffff;

  --ok:           #047857;
  --ok-soft:      #d1fae5;
  --warn:         #b45309;
  --warn-soft:    #fef3c7;
  --crit:         #be123c;
  --crit-soft:    #ffe4e6;

  --link:             #0369a1;
  --nav-bg:           #ffffffeb;
  --nav-ink:          #475569;
  --nav-ink-soft:     #7c8ba1;
  --nav-active:       #0369a1;
  --nav-active-line:  #0284c7;
  --menu-bg:          #ffffff;

  --shadow-sm: 0 1px 2px rgb(15 23 42 / 0.06);
  --shadow:    0 2px 4px rgb(15 23 42 / 0.05), 0 12px 32px rgb(15 23 42 / 0.07);
  --scrim:     #0f172a66;
}

/* ---------- Skalen ---------- */

:root {
  /* Die Systemschriften stehen als Rueckfall dahinter — falls eine
     Schriftdatei einmal nicht laedt, bleibt die Seite lesbar. */
  --font-ui: "IBM Plex Sans", ui-sans-serif, system-ui, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Code", "SF Mono",
               Menlo, Consolas, "Liberation Mono", monospace;

  /* Plex laeuft etwas schmaler als die Systemschriften. */
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semi:   600;
  --fw-bold:   700;

  /* 12px ist die Untergrenze. Darunter gibt es nichts. */
  --fs-label: 0.75rem;
  --fs-sm:    0.8125rem;
  --fs-base:  0.9375rem;
  --fs-lg:    1.0625rem;
  --fs-xl:    1.375rem;
  --fs-2xl:   1.75rem;
  --fs-3xl:   2.25rem;

  --radius-sm: 0.5rem;
  --radius:    0.75rem;
  --radius-lg: 1rem;
  /* Nur fuer Fenster und die Anmeldekarte. Karten, Kacheln und Zeilen
     behalten ausdruecklich --radius-lg — der grosse Radius des
     Python-Vorgaengers wird dort nicht uebernommen (Anweisung des
     Auftraggebers vom 18. August 2026). */
  --radius-xl: 1.75rem;

  --s1: 0.25rem;  --s2: 0.5rem;   --s3: 0.75rem;
  --s4: 1rem;     --s5: 1.5rem;   --s6: 2rem;    --s8: 3rem;

  /* Volle Breite mit Luft am Rand. Nur Fliesstext wird enger.

     Der Rand ist genauso breit wie der Abstand zwischen den Spalten
     (`--s5`) und nicht breiter: bis 2.5rem stand die Leiste weiter vom
     Bildschirmrand weg als vom Verlauf daneben, und die Seite sah aus,
     als haette sie sich verrutscht. Auf dem Handy faellt er auf 1rem —
     dort ist jeder Millimeter Breite Inhalt. */
  --gutter: clamp(1rem, 4vw, var(--s5));
  --prose:  68ch;

  /* Die Kopfleiste ist hoch genug, dass der aktive Bereich einen
     Unterstrich tragen kann, ohne gedraengt zu wirken. */
  --topbar-h: 4.5rem;
  --subnav-h: 2.75rem;

  /* Hoehe der Regenbogenleiste. Steht als Token da, weil die Kopfleiste
     genau darunter kleben muss. */
  --rainbow-h: 4px;

  /* Die gesamte Hoehe dessen, was oben klebt. Alles, was sich darunter
     festheftet — die Seitenspalte etwa —, rechnet damit und nicht mit
     den Einzelteilen. Die Einstellungen tragen eine dritte Zeile; die
     Vorlage setzt dort `body.has-tree`, und nur diese eine Zeile weiter
     unten muss sich dann aendern. */
  --head-h: calc(var(--rainbow-h) + var(--topbar-h) + var(--subnav-h));

  /* Mindestgroesse fuer alles Anklickbare. */
  --touch: 2.5rem;
}

/* ---------- Grundlagen ---------- */

*, *::before, *::after { box-sizing: border-box; }

/* Das Attribut `hidden` muss staerker sein als jede Klasse, die
   `display` setzt. Ohne diese Regel schlaegt z.B. `.filepick`
   (display: inline-flex) das Attribut, und ein Element, das der
   Server als verborgen ausliefert, steht sichtbar auf der Seite.
   Das ist keine Schoenheitsfrage: am Antwortfeld haengt daran, ob ein
   als intern gemeinter Vermerk offen herausgeht. */
[hidden] { display: none !important; }

html { color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

h1 { font-size: var(--fs-2xl); font-weight: 800; }
h2 { font-size: var(--fs-xl);  font-weight: 750; }
h3 { font-size: var(--fs-lg);  font-weight: 700; }
h4 { font-size: var(--fs-base); font-weight: 700; }

p { margin: 0; }
p.prose, .prose p { max-width: var(--prose); }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

code, .mono {
  font-family: var(--font-mono);
  font-size: 0.9em;
  font-variant-numeric: tabular-nums;
}

img { max-width: 100%; height: auto; }

/* Nur fuer Screenreader. */
.sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Sprungmarke — erscheint beim Tabben. */
.skip {
  position: absolute; left: var(--s4); top: -3rem; z-index: 200;
  background: var(--accent-strong); color: var(--accent-ink);
  padding: var(--s2) var(--s4); border-radius: var(--radius-sm);
  font-weight: 700; font-size: var(--fs-sm);
  transition: top 0.15s;
}
.skip:focus { top: var(--s2); text-decoration: none; }

/* ---------- Regenbogenleiste ----------

   Das Erkennungszeichen des Vorgaengers. Fest verankert und ueber allem,
   damit sie auch bei geoeffnetem Fenster stehen bleibt — sie ist die
   einzige Stelle der Oberflaeche, die nie verschwindet. */

.rainbow {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: var(--rainbow-h);
  z-index: 10000;
  background: linear-gradient(90deg,
    #ef4444 0%, #f59e0b 25%, #10b981 50%, #3b82f6 75%, #8b5cf6 100%);
}

/* ---------- Kopfleiste: volle Breite, klebt oben ---------- */

/* Klebt unter der Regenbogenleiste, nicht darunter hinweg. */
.shell-head {
  position: sticky; top: var(--rainbow-h); z-index: 100;
  background: var(--nav-bg);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

.topbar {
  height: var(--topbar-h);
  padding-inline: var(--gutter);
  display: flex; align-items: center; gap: var(--s5);
}

.brand {
  display: inline-flex; align-items: center; gap: var(--s2);
  font-size: var(--fs-lg); font-weight: 800;
  letter-spacing: -0.03em; color: var(--ink); flex: none;
}
.brand:hover { text-decoration: none; }
/* Das mitgelieferte Logo folgt der Akzentfarbe und damit der gewaehlten
   Ansicht. Ein hochgeladenes Logo ist ein Rasterbild und folgt
   currentColor nicht — object-fit und width:auto halten es trotzdem in
   der Hoehe der Kopfleiste, ohne es zu verzerren. */
.brand__mark {
  height: 1.5rem; width: auto; flex: none;
  color: var(--accent);
  overflow: visible;
  object-fit: contain;
}

/* Bereiche — auf schmalen Schirmen wischbar statt umbrechend. */
.nav-top {
  display: flex; align-items: center; gap: var(--s1);
  flex: 1 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.nav-top::-webkit-scrollbar { display: none; }

/* Auf voller Leistenhoehe, damit der Unterstrich des aktiven Bereichs
   auf der Kante der Kopfleiste sitzt und nicht in der Luft haengt. */
.nav-top__link {
  position: relative;
  display: inline-flex; align-items: center;
  height: var(--topbar-h); padding-inline: var(--s4);
  font-size: var(--fs-sm); font-weight: 650;
  color: var(--nav-ink); white-space: nowrap;
  transition: color 0.15s, background 0.15s;
}
.nav-top__link:hover {
  color: var(--ink); background: var(--surface-2); text-decoration: none;
}
.nav-top__link.is-active { color: var(--nav-active); }
/* Ein Balken statt einer Flaeche: die Kopfleiste bleibt ruhig, und der
   aktive Bereich ist trotzdem auf einen Blick zu finden. */
.nav-top__link.is-active::after {
  content: ""; position: absolute;
  inset-inline: var(--s2); inset-block-end: 0; height: 3px;
  border-radius: 999px 999px 0 0;
  background: var(--nav-active-line);
}

.head-tools { display: flex; align-items: center; gap: var(--s2); flex: none; }

/* Der Umschalter ist ein Formular, kein Skriptknopf — die Anwendung
   soll ohne JavaScript funktionieren. `display: contents` nimmt das
   Formular aus dem Layout heraus, damit die Kopfleiste aussieht wie
   zuvor, als der Knopf noch allein stand. */
.theme-form { display: contents; }

.icon-btn {
  width: var(--touch); height: var(--touch);
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink-2);
  cursor: pointer; transition: color 0.15s, background 0.15s;
}
.icon-btn:hover { color: var(--ink); background: var(--surface-3); }
.icon-btn svg { width: 1.125rem; height: 1.125rem; }

/* Benutzermenue */
.usermenu { position: relative; flex: none; }
.usermenu__trigger {
  display: inline-flex; align-items: center; gap: var(--s2);
  height: var(--touch); padding: 0 var(--s2) 0 var(--s1);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: none; color: inherit; font: inherit; cursor: pointer;
}
.usermenu__trigger:hover { background: var(--surface-2); }

.avatar {
  width: 2rem; height: 2rem; flex: none;
  border-radius: 50%; overflow: hidden;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent-strong);
  font-size: var(--fs-label); font-weight: 800; letter-spacing: 0.02em;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }

.usermenu__name {
  display: none; font-size: var(--fs-sm); font-weight: 650; line-height: 1.2;
}
/* Die Gruppe steht als Plakette unter dem Namen — sie beantwortet die
   Frage "mit welchen Rechten sehe ich das hier gerade". */
.usermenu__group {
  display: inline-block; margin-top: 0.15rem;
  padding: 0.1em 0.45em; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--accent-strong);
  font-size: var(--fs-label); font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
}

.usermenu__panel {
  position: absolute; right: 0; top: calc(100% + var(--s2));
  min-width: 13rem; padding: var(--s2);
  background: var(--menu-bg); border: 1px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow);
  display: none; z-index: 110;
}
.usermenu[open] .usermenu__panel { display: block; }

/* Die Wahl der Ansicht steht als eigenes Formular im Menue, damit die
   drei Zustaende ohne JavaScript bedienbar sind. `display: contents`
   nimmt es aus dem Layout heraus, sonst ruecke die Eintraege darunter
   um eine Ebene ein. */
.menu-choice { display: contents; }

.menu-item {
  display: flex; align-items: center; gap: var(--s3);
  height: var(--touch); padding-inline: var(--s3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-2);
}
.menu-item:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.menu-item--danger { color: var(--crit); }
.menu-item--danger:hover { background: var(--crit-soft); color: var(--crit); }

/* `.menu-item` steht sowohl als `<a>` wie auch als `<button>` im
   Benutzermenue — die Umschalter der Ansicht sind Formularknoepfe.
   Ohne diese Regeln fielen sie aus der Reihe: ein Knopf ist von Haus
   aus schmal, mittig und traegt einen Rahmen. */
button.menu-item {
  width: 100%; text-align: left;
  background: none; border: 0; font: inherit; cursor: pointer;
}
.menu-item.is-active { color: var(--nav-active); font-weight: 700; }

/* Eine Haarlinie zwischen der Wahl der Ansicht und dem, was danach
   kommt. Oben stehen drei Schalter, die nur die Darstellung umstellen;
   darunter Verweise, die die Seite verlassen. Ohne die Linie sind das
   sechs gleich aussehende Zeilen, und man klickt „Abmelden", wo man
   „Immer dunkel" meinte. `.menu-choice` traegt `display: contents` —
   im Baum steht es trotzdem, und der Geschwisterwaehler findet es. */
.menu-choice + .menu-item {
  margin-top: var(--s2); padding-top: var(--s2);
  border-top: 1px solid var(--line);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

/* ---------- Unterleiste ---------- */

.nav-sub {
  height: var(--subnav-h);
  padding-inline: var(--gutter);
  display: flex; align-items: stretch; gap: var(--s1);
  border-top: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.nav-sub::-webkit-scrollbar { display: none; }

.nav-sub__link {
  position: relative;
  display: inline-flex; align-items: center; padding-inline: var(--s3);
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--nav-ink-soft); white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.nav-sub__link:hover { color: var(--ink); text-decoration: none; }
.nav-sub__link.is-active { color: var(--nav-active); border-bottom-color: var(--nav-active-line); }

/* Die dritte Zeile ist dieselbe Leiste, eine Ebene tiefer. Sie
   unterscheidet sich nur um die Linie darueber — die kommt von
   `.nav-sub` schon mit — und um die Einrueckung, an der man sieht, dass
   sie zum Punkt darueber gehoert und nicht neben ihm steht. Ein eigenes
   Aussehen hatte sie vorher; das las sich wie eine dritte Art von
   Leiste statt wie eine dritte Ebene. */
.nav-sub--tief { padding-inline-start: calc(var(--gutter) + var(--s5)); }

/* ---------- Aufklappmenue fuer schmale Schirme ----------

   Auf breiten Schirmen bleibt die Navigation, wie sie ist: drei offene
   Zeilen. Erst unterhalb von 48rem tauschen die Zeilen und dieses
   Menue die Plaetze — siehe ganz unten, „Schmale Schirme". Bis dahin
   steht der Knopf nicht im Weg, weil er dort gar nicht erst erscheint. */

.navmenu { display: none; position: relative; flex: none; }

.navmenu__trigger {
  width: var(--touch); height: var(--touch);
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink-2);
  cursor: pointer; list-style: none;
}
/* Das Dreieck, das der Browser einem <summary> voranstellt, wuerde den
   Knopf ausbeulen. Zwei Schreibweisen, weil Safari die zweite braucht. */
.navmenu__trigger::-webkit-details-marker { display: none; }
.navmenu__trigger::marker { content: ""; }
.navmenu__trigger:hover { color: var(--ink); background: var(--surface-3); }
.navmenu__trigger svg { width: 1.25rem; height: 1.25rem; }
.navmenu[open] .navmenu__trigger {
  color: var(--accent-strong); border-color: var(--accent-line);
}

/* Die Fahne haengt an der Kopfleiste und nicht am Knopf: sie geht ueber
   die volle Breite, so wie man es vom Telefon kennt. Deshalb `fixed` —
   `absolute` wuerde sie an dem schmalen Knopf ausrichten. */
.navmenu__panel {
  display: none;
  position: fixed; inset-inline: 0;
  top: calc(var(--rainbow-h) + var(--topbar-h));
  max-height: calc(100dvh - var(--rainbow-h) - var(--topbar-h));
  overflow-y: auto;
  padding: var(--s3) var(--gutter) var(--s5);
  background: var(--menu-bg);
  border-block: 1px solid var(--line-strong);
  box-shadow: var(--shadow);
  z-index: 105;
}
.navmenu[open] .navmenu__panel { display: block; }

.navtree { display: flex; flex-direction: column; }
.navtree__node { min-width: 0; }

/* Erste Ebene: die Bereiche. So gross wie die Kopfzeile daneben, damit
   sie sich mit dem Daumen treffen lassen. */
.navtree__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3);
  min-height: var(--touch); padding: var(--s2) var(--s3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-base); font-weight: 700;
  color: var(--ink); cursor: pointer; list-style: none;
}
.navtree__top::-webkit-details-marker { display: none; }
.navtree__top::marker { content: ""; }
.navtree__top:hover { background: var(--surface-2); text-decoration: none; }
.navtree__top.is-active { color: var(--nav-active); }

/* Zweite und dritte Ebene. Der eingerueckte Strich links ersetzt die
   Einrueckung allein: ohne ihn sieht man auf einem Telefon nicht, wo
   ein Zweig aufhoert und der naechste beginnt. */
.navtree__kids {
  margin-inline-start: var(--s4);
  padding-inline-start: var(--s3);
  border-inline-start: 1px solid var(--line);
}

.navtree__leaf {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3);
  min-height: var(--touch); padding: var(--s2) var(--s3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--ink-2); cursor: pointer; list-style: none;
}
.navtree__leaf::-webkit-details-marker { display: none; }
.navtree__leaf::marker { content: ""; }
.navtree__leaf:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.navtree__leaf--deep { font-weight: 550; color: var(--ink-3); }

/* Markiert wird mit Flaeche und Strich, nicht nur mit Farbe — im
   aufgeklappten Menue steht sonst irgendwo in der Liste ein andersfarbiges
   Wort, das man erst suchen muss. */
a.navtree__top.is-active,
a.navtree__leaf.is-active {
  background: var(--accent-soft); color: var(--nav-active); font-weight: 700;
  box-shadow: inset 2px 0 0 var(--nav-active-line);
}
summary.navtree__leaf.is-active { color: var(--nav-active); }

.navtree__caret {
  width: 1rem; height: 1rem; flex: none;
  color: var(--ink-3); transition: transform 0.15s;
}
.navtree__node[open] > summary .navtree__caret { transform: rotate(180deg); }

/* ---------- Inhalt: volle Breite ---------- */

main { flex: 1 1 auto; padding: var(--s6) var(--gutter) var(--s8); }

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s5);
}
.page-head__text { display: flex; flex-direction: column; gap: var(--s2); }
.page-head__actions { display: flex; gap: var(--s2); flex-wrap: wrap; }

.label {
  font-size: var(--fs-label); font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-3);
}
.muted { color: var(--ink-2); font-size: var(--fs-sm); }

/* Die laufende Fassung des Hauses. Sie steht unten links im Fussbereich
   jeder Seite und ebenso unten links auf der Anmeldung — an der Hotline
   ist sie die erste Frage, und wer sie nur nach dem Anmelden faende,
   koennte sie ausgerechnet dann nicht nennen, wenn er nicht hineinkommt.

   In Mono, weil sie eine Kennung ist und keine Aussage: Ziffern und
   Punkte stehen dort auf gleicher Breite. */
.version {
  font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--ink-3); white-space: nowrap;
}

.stack   { display: flex; flex-direction: column; gap: var(--s5); }
.stack-3 { display: flex; flex-direction: column; gap: var(--s3); }
.stack-2 { display: flex; flex-direction: column; gap: var(--s2); }
/* Inhalte behalten ihre eigene Breite, statt auf die des Stapels
   gezogen zu werden — fuer Stapel mit Plaketten und Knoepfen darin. */
.stack--start { align-items: flex-start; min-width: 0; }
.row-x   { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* Raster, die sich selbst anpassen — keine Breakpoints noetig. */
.grid       { display: grid; gap: var(--s4);
              grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr)); }
.grid-wide  { display: grid; gap: var(--s4);
              grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr)); }
.grid-stats { display: grid; gap: var(--s3);
              grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr)); }

/* Arbeitsflaeche mit Seitenspalte, die unten anschliesst. */
.split { display: grid; gap: var(--s5); grid-template-columns: 1fr; }
/* Die breitere Fassung fuer die Startseite. Dort steht in der
   Seitenspalte kein Formular, sondern ein Ring mit seiner Legende —
   und die Legende traegt je Zeile einen Statusnamen, der aus der
   Datenbank kommt und beliebig lang sein darf. Auf 21rem blieb davon
   zu wenig uebrig. */
.split--wide { --aside: 30rem; }

@media (min-width: 60rem) {
  .split {
    grid-template-columns: minmax(0, 1fr) var(--aside, 21rem);
    align-items: start;
  }
  /* Kein eigener Bildlauf — beide Spalten haften oben.

     Zuerst stand hier ein Kasten mit eigener Hoehe und eigener
     Bildlaufleiste; dann eine Spalte, die mit ihrem unteren Ende am
     Fensterrand haengenblieb. Beides war daneben: das eine stellte eine
     zweite Bildlaufleiste mitten auf die Seite, das andere hielt die
     Spalte am falschen Ende fest.

     Jetzt haftet jede Spalte mit ihrem oberen Rand unter der Kopfzeile
     und laeuft beim Weiterscrollen mit — und zwar beide. Welche die
     kuerzere ist, entscheidet sich von Seite zu Seite: bei einem langen
     Verlauf steht die Seitenspalte, bei einem kurzen steht der Verlauf
     und die Kaesten bestimmen die Hoehe. `sticky` braucht dafuer keine
     Fallunterscheidung — die kuerzere Spalte ist die, die haftet.

     `align-self: start`, damit die Spalte in ihrer Rasterzelle nur so
     hoch ist wie ihr Inhalt: eine gestreckte Zelle haette nichts, worin
     sie sich bewegen koennte.

     Die Anheftungsleiste bleibt aussen vor: sie hat ihre eigenen Regeln
     weiter unten, und zwischen 60 und 80 rem liegt sie ueber die volle
     Breite oben — angeheftet wuerde sie dort den Verlauf verdecken. */
  .split > *:not(.split__rail) {
    position: sticky;
    top: calc(var(--head-h) + var(--s4));
    align-self: start;
  }
}

/* Drei Zeilen statt zwei. */
body.has-tree { --head-h: calc(var(--rainbow-h) + var(--topbar-h) + 2 * var(--subnav-h)); }

/* ---------- Bausteine ---------- */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--s5);
  box-shadow: var(--shadow-sm);
}
.card--flush { padding: 0; overflow: hidden; }
.card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
}
.card__body { padding: var(--s5); }

.stat { display: flex; flex-direction: column; gap: var(--s1); }
.stat__value {
  font-size: var(--fs-2xl); font-weight: 800; line-height: 1;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}

.pill {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-size: var(--fs-label); font-weight: 700; letter-spacing: 0.03em;
  padding: 0.3em 0.7em; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-3); color: var(--ink-2);
}
.pill--ok   { background: var(--ok-soft);     color: var(--ok); }
.pill--warn { background: var(--warn-soft);   color: var(--warn); }
.pill--crit { background: var(--crit-soft);   color: var(--crit); }
.pill--info { background: var(--accent-soft); color: var(--accent-strong); }

/* Farbpunkt fuer Status und Prioritaet — Form zusaetzlich zur Farbe. */
.dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; flex: none;
       background: currentColor; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--touch); padding-inline: var(--s4);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink);
  font: inherit; font-size: var(--fs-sm); font-weight: 650;
  cursor: pointer; white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, transform 0.05s;
}
.btn:hover { background: var(--surface-3); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }

/* Der Hauptknopf ist gesperrt gesetzt und in Grossbuchstaben — es gibt
   je Ansicht hoechstens einen, und er soll auch ohne Farbe auffallen.
   Der getoente Schlagschatten hebt ihn von der Flaeche ab.

   Den Radius setzt er ausdruecklich nicht: jeder Knopf der Oberflaeche
   traegt denselben, sonst erkennt man an der Ecke nicht mehr wieder,
   dass es dieselbe Bauart ist (Anweisung des Auftraggebers).

   Und keinen Schlagschatten: hier stand ein getoenter Schein nach
   aussen, der den Knopf ueber die Flaeche hob. Die Farbe traegt den
   Knopf allein — der Schein machte aus einem Bedienelement ein
   leuchtendes Etwas (Anweisung des Auftraggebers). */
.btn--primary {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--accent-ink);
  font-weight: var(--fw-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* Beim Ueberfahren weg von der Knopfschrift statt auf gut Glueck heller:
   --ink ist in beiden Ansichten das Gegenteil von --accent-ink, also wird
   der Knopf hell auf hell dunkler und dunkel auf dunkel heller. Der
   Kontrast steigt so in beiden Ansichten, statt in einer zu kippen. */
.btn--primary:hover {
  background: color-mix(in srgb, var(--accent-strong) 86%, var(--ink));
  border-color: color-mix(in srgb, var(--accent-strong) 86%, var(--ink));
}
.btn--primary:active { transform: scale(0.98); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn--ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn--danger { color: var(--crit); border-color: var(--crit-soft); background: transparent; }
.btn--danger:hover { background: var(--crit-soft); }

/* Kennzahlkachel der Startseite. Drei Zeilen: Rubrik, Zahl mit der
   Veraenderung des Tages daneben, darunter das, was an der Zahl zu tun
   ist.

   Hier standen vier Farbvarianten (sky, indigo, emerald, amber) und je
   ein Symbolquadrat. Vier gleich laute Kacheln in vier Farben heissen:
   Farbe ist Dekoration. In dieser Oberflaeche bedeutet Farbe Zustand —
   deshalb ist jetzt genau **eine** Kachel hervorgehoben, die eigene, und
   die Symbolquadrate sind weg. Ein Symbol, das nur wiederholt, was die
   Beschriftung daneben sagt, kostet Platz und traegt nichts. */
.tile {
  display: flex; flex-direction: column; gap: var(--s2);
  padding: var(--s5);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  color: inherit;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
a.tile:hover {
  border-color: var(--line-strong); box-shadow: var(--shadow);
  transform: translateY(-2px); text-decoration: none;
}

/* Die eigene Kachel. Rand und Kante in Akzentfarbe — dieselbe Geste wie
   der Sky-Streifen an der linken Kante der Anmeldekarte: hier ist die
   Stelle, an der du selbst dran bist. */
.tile--eigen {
  border-color: var(--accent);
  box-shadow: inset 3px 0 0 var(--accent), var(--shadow-sm);
}
a.tile--eigen:hover {
  border-color: var(--accent);
  box-shadow: inset 3px 0 0 var(--accent), var(--shadow);
}

/* Zahl und Veraenderung stehen auf einer Grundlinie: die Zahl ist die
   Aussage, die Veraenderung der Nachsatz dazu. Untereinander laesen sie
   sich wie zwei Kennzahlen. */
.tile__zahl { display: flex; align-items: baseline; gap: var(--s3); }
.tile__value {
  font-size: var(--fs-3xl); font-weight: 800; line-height: 1;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}
.tile__delta {
  font-size: var(--fs-sm); font-weight: 650;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Die dritte Zeile. Ihre Farbe kommt aus der Vorlage, weil sie den
   Zustand meint: liegengeblieben ist --warn, frisch aktualisiert ist
   Sky, und nichts zu tun ist gedaempft. */
.tile__hinweis { font-size: var(--fs-label); font-weight: 650; }

/* Ueberschrift einer Gruppe, mit Sky-Balken davor. Der Balken ersetzt die
   Trennlinie: er sagt "hier faengt etwas Neues an", ohne eine Kante quer
   ueber die Seite zu ziehen. */
.section-head {
  display: flex; align-items: center; gap: var(--s3);
  margin-bottom: var(--s3);
}
.section-head::before {
  content: ""; flex: none;
  width: 4px; align-self: stretch; min-height: 1.25rem;
  border-radius: 999px; background: var(--accent);
}

/* Karte, die Zeilen aufnimmt. Der Spaltenschnitt steht hier und nicht in
   der Zeile, weil Spaltenkopf und Zeilen ihn teilen muessen — laufen sie
   auseinander, steht jede Beschriftung ueber der falschen Spalte. Eine
   anders geschnittene Liste setzt --spalten am Behaelter neu. */
.rows {
  /* Der Schnitt der Ticketliste. Feste Breiten dort, wo der Inhalt
     seine Breite kennt — Nummer, Zeitpunkt, Prioritaet —, damit die
     Spalten nicht mit jedem Bildschirm neu wandern. Alles andere in
     Anteilen: Betreff und Namen sollen den uebrigen Platz teilen. */
  --spalten:
    minmax(0, 26fr) 5rem minmax(0, 9fr) minmax(0, 11fr)
    minmax(0, 11fr) 9rem minmax(0, 9fr) 6rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.rows__head {
  display: grid; grid-template-columns: var(--spalten);
  align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s5);
  background: var(--surface-2); border-bottom: 1px solid var(--line);
  font-size: var(--fs-label); font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-3);
}

/* Eine ganze Zeile als Verweis. Raster statt Tabelle, damit das
   umschliessende <a> zulaessig bleibt — in einer Tabellenzeile darf
   kein Verweis um die Zellen stehen. */
.row-link {
  display: grid;
  grid-template-columns: var(--spalten);
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s5);
  color: inherit;
  border-bottom: 1px solid var(--line);
  /* Die Statuskante. Ihre Farbe kommt als `--kante` aus der Vorlage;
     ohne Angabe ist sie unsichtbar, und Listen ohne Zustand je Zeile —
     das Inventar, die Geraeteliste — bleiben unveraendert.

     Ein innerer Schlagschatten und keine erste Rasterspalte: die Kante
     laeuft so ueber die volle Zeilenhoehe, ohne dass jede Zelle ihre
     Polsterung selbst tragen muss. Aus zwei Metern Abstand ist damit
     zu sehen, wo etwas eskaliert ist. */
  box-shadow: inset 3px 0 0 var(--kante, transparent);
}
.row-link:hover { background: var(--surface-2); text-decoration: none; }
.row-link:last-child { border-bottom: none; }

/* Rechtsbuendige Zelle — in der Ticketliste sind das Datum, Update
   und Prioritaet. Zeitpunkte und kurze Werte am rechten Rand stehen
   untereinander auf einer Kante; linksbuendig franst die Spalte mit
   jeder unterschiedlich langen Angabe aus.

   Die Klasse sitzt an Zelle **und** Spaltenkopf. `justify-items` am
   Raster ginge nicht: es traefe alle acht Spalten. */
.rows__head > .cell-end,
.row-link > .cell-end { text-align: right; }

/* Der Aenderungszeitpunkt in Sky, wenn seit dem Anlegen etwas geschah;
   ohne Aenderung steht dort ein gedaempfter Strich. So sieht man ohne
   Lesen, in welchen Zeilen Bewegung ist. `--accent-strong`, weil hier
   Schrift auf einer Flaeche steht und nicht ein Rand gezogen wird. */
.cell-changed { color: var(--accent-strong); }

@media (max-width: 63.999rem) {
  /* Auf schmalen Schirmen gestapelt; der Spaltenkopf verschwindet. */
  .row-link { grid-template-columns: 1fr 1fr; }
  .rows__head { display: none; }
  /* Gestapelt gibt es keine durchgehende rechte Kante mehr, an der sich
     etwas ausrichten liesse — die Zellen wechseln die Spalte. */
  .row-link > .cell-end { text-align: left; }
}

/* Filterchip. Der Unterschied zum Knopf ist kein aesthetischer: es gibt
   je Ansicht **einen** Hauptknopf, und in der Ticketliste ist das
   „+ Ticket erstellen". Vorher trug der aktive Statusfilter
   `.btn--primary` — vier Hauptknoepfe ueber der Liste, und der echte
   ging darin unter.

   Der Chip traegt seine Zahl gleich mit. Sie ist der Grund, ihn
   anzuklicken oder es zu lassen: wer „Wartet auf Kunde 0" liest, spart
   sich den Klick. */
.chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--touch); padding-inline: var(--s3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink-2);
  font-size: var(--fs-sm); font-weight: 650;
  white-space: nowrap; cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.chip:hover { color: var(--ink); background: var(--surface-3); text-decoration: none; }

/* `--accent-strong` und nicht `--accent`: hier steht Schrift auf einer
   Flaeche. In der hellen Ansicht traegt `--accent` weisse Schrift nur
   bei 2,8:1 — der aktive Filter waere dort unlesbar, ohne dass ein Test
   anschlaegt. */
.chip--active {
  background: var(--accent-strong); border-color: var(--accent-strong);
  color: var(--accent-ink);
}
.chip--active:hover {
  color: var(--accent-ink);
  background: color-mix(in srgb, var(--accent-strong) 86%, var(--ink));
  border-color: color-mix(in srgb, var(--accent-strong) 86%, var(--ink));
}

/* Der eine Chip, der einen Zustand meint und nicht einen Ausschnitt:
   „Ohne Bewegung > 3 Tage" benennt ein Versaeumnis. Flaeche und Rand
   mischt er sich aus `currentColor`, wie die Plakette — so gibt es
   dafuer keinen zweiten Tokensatz. */
.chip--warn {
  color: var(--warn);
  background: color-mix(in srgb, currentColor 14%, transparent);
  border-color: color-mix(in srgb, currentColor 30%, transparent);
}
.chip--warn:hover {
  color: var(--warn);
  background: color-mix(in srgb, currentColor 22%, transparent);
}
.chip--warn.chip--active {
  background: color-mix(in srgb, currentColor 30%, transparent);
  border-color: currentColor;
}

/* Die Zahl im Chip in Mono: untereinander stehende Chips bekommen so
   gleich breite Ziffern, und die Leiste zappelt beim Filterwechsel
   nicht. */
.chip__zahl { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.chip:not(.chip--active) .chip__zahl { color: var(--ink); }

/* Das Kreuz am abwaehlbaren Chip. Ein eigener Grund, damit es als
   Bedienelement lesbar ist und nicht als Buchstabe im Text — der ganze
   Chip ist der Verweis, das Kreuz sagt nur, wohin er fuehrt. */
.chip__weg {
  display: grid; place-items: center;
  width: 1.125rem; height: 1.125rem; flex: none;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, currentColor 22%, transparent);
  font-size: var(--fs-label); line-height: 1;
}

/* „Alle zurücksetzen" ist kein Filter und traegt deshalb keinen Chip:
   ein Verweis, der wie ein Chip aussaehe, laese sich als weiterer
   Filter lesen. */
.chip-reset {
  margin-inline-start: var(--s2);
  font-size: var(--fs-label); font-weight: 650;
  color: var(--ink-3); white-space: nowrap;
}
.chip-reset:hover { color: var(--ink); }

/* Die Filterkarte ueber einer Liste. Zwei Reihen, durch eine Linie
   getrennt: oben der Ausschnitt (welcher Status, wonach gesucht), unten
   die Fragen an den Bestand und die Trefferzahl.

   Sie steckt in einer `.card`, damit die Filter sichtbar zur Liste
   gehoeren. Vorher standen sie frei ueber der Seite und sahen aus wie
   Navigation. */
.filterbar { display: flex; flex-direction: column; padding: var(--s3) var(--s4); }
.filterbar__row {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  min-height: var(--touch);
}
.filterbar__row + .filterbar__row {
  margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
/* Was ans rechte Ende der Reihe gehoert — die Trefferzahl. Das
   Suchfeld schiebt sich ueber `.suche` selbst dorthin. */
.filterbar__ende {
  margin-inline-start: auto;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); color: var(--ink-2); white-space: nowrap;
}

/* Die Filterzeile der Geraeteliste: Suchfeld und Auswahlfelder in einem
   Formular. Sie umbricht, statt die Felder zu quetschen — vier
   Auswahlfelder nebeneinander passen auf einen Laptopschirm, auf einem
   Tablet nicht mehr. */
.filterform {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  flex: 1 1 auto; min-width: 0;
}

/* Ein Auswahlfeld, das seine Beschriftung selbst traegt („Kategorie:
   alle"). Schmaler als ein Formularfeld und nur so breit wie noetig:
   vier davon nebeneinander sind eine Filterzeile, vier in voller Breite
   waeren ein Formular.

   Die Obergrenze ist knapp gehalten, damit Suchfeld, vier Felder und
   Knopf auf einem gewoehnlichen Schirm in **eine** Zeile passen. Bricht
   die Reihe um, steht die Trefferzahl rechts daneben in der Luft — sie
   gehoert ans Ende der Zeile, nicht an den Rand eines Blocks. Ein
   laengerer Wert im Feld wird abgeschnitten; er steht darunter als
   abwaehlbarer Chip noch einmal in voller Laenge. */
.select--filter {
  width: auto; min-width: 8rem; max-width: 11rem;
  font-size: var(--fs-sm); color: var(--ink-2);
}

/* Das Suchfeld in einer Filterzeile: es schiebt sich hier nicht ans
   rechte Ende wie in der Ticketliste, sondern steht vorn — es ist der
   Filter, mit dem man anfaengt. Es gibt als erstes Breite ab, wenn der
   Platz eng wird: ein schmales Suchfeld bleibt benutzbar, ein
   umgebrochenes Auswahlfeld nicht. */
.suche--inline {
  margin-inline-start: 0;
  flex: 1 1 12rem;
}

/* Quadratische Statuskachel. Die Farbe kommt als `color` von aussen —
   Flaeche und Rand mischt sie sich daraus selbst, damit die Vorlage nur
   einen einzigen Wert setzen muss. */
.status-tile {
  width: 2.5rem; height: 2.5rem; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius);
  background: color-mix(in srgb, currentColor 16%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 32%, transparent);
}
.status-tile svg { width: 1rem; height: 1rem; }
/* Die eine Kachel, die keinen Zustand meint: die im Kopf einer Seite,
   die fuer den Bereich steht. Ihre Farbe steht fest und gehoert deshalb
   hierher — ein `style`-Attribut mit gleichbleibendem Wert waere eine
   Regel am falschen Ort, die jede Klasse schlaegt und beim naechsten
   Nachziehen niemand in dieser Datei findet. */
.status-tile--accent { color: var(--accent); }

/* Plakette fuer Prioritaet und Status. Anders als .pill hat sie keine
   festen Varianten: Status und Prioritaeten stehen in der Datenbank und
   bringen ihre Farbe mit. */
.badge {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 0.3em 0.7em; border-radius: var(--radius-sm); white-space: nowrap;
  font-size: var(--fs-label); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase;
  background: color-mix(in srgb, currentColor 14%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
/* Die Ausnahme: der Vermerk "Intern" haengt an keinem Datenbankwert.
   Seine Farbe steht fest und darum als Klasse hier. */
.badge--warn { color: var(--warn); }

/* Plakette, die nicht ueber ihren Platz hinauswaechst.
   Statusnamen kommen aus der Datenbank und sind frei vergeben —
   "Wartet auf Rückmeldung des Kunden" gibt es, und in der Legende des
   Rings drueckt so einer die Zahl daneben aus der Zeile. Hier bricht
   er nicht um (das ergaebe eine zweizeilige Plakette), sondern hoert
   mit drei Punkten auf. Der volle Name steht im `title`.

   `min-width: 0` ist der eigentliche Schalter: ohne ihn weigert sich
   ein Flex-Kind, unter seine Inhaltsbreite zu schrumpfen, und
   `text-overflow` kommt nie zum Zug. */
.badge--clip {
  display: inline-block;
  min-width: 0; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis;
}

/* Zwei Zeilen in einer Zelle: oben die Sache, unten der Zusatz dazu.
   Eng gesetzt, damit die beiden als ein Block lesen und nicht als zwei
   Angaben untereinander. `.stack-2` waere hier zu weit — dessen Abstand
   trennt Bloecke, dieser haelt zwei Zeilen zusammen. */
.zeile-2 { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.zeile-2__unten {
  display: flex; align-items: center; gap: var(--s2); min-width: 0;
  font-size: var(--fs-label); color: var(--ink-3); white-space: nowrap;
}

/* Das Alter in der Geraetezeile. Mono, weil es eine Messgroesse ist:
   „3 J 8 M" und „12 J 1 M" stehen darunter auf gleicher Breite. */
.zeile-alter {
  font-size: var(--fs-label); font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* Der Betreff in der Ticketzeile. Eine Stufe groesser und fetter als
   alles andere darin: er ist das, wonach in der Liste gesucht wird —
   vorher stand er in derselben Groesse wie die Abteilung daneben, und
   die Zeile hatte kein Gefaelle. */
.zeile-titel {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-base); font-weight: var(--fw-semi);
}

/* Avatar plus Name. In der Ticketliste steht das in einer Zeile, die
   selbst ein Verweis ist — deshalb ist hier nichts verlinkt. */
.who { display: inline-flex; align-items: center; gap: var(--s2); min-width: 0; }
.who .avatar { width: 1.75rem; height: 1.75rem; }
.who__name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-sm); font-weight: 600;
}
/* Der Ersteller steht gedaempft neben dem Bearbeiter. Beide Namen in
   voller Schrift lassen die Zeile raten, welcher der beiden gerade
   zustaendig ist — und das ist die Frage, die eine Ticketliste
   beantwortet. */
.who--gedaempft .who__name { color: var(--ink-2); font-weight: var(--fw-medium); }

/* Umschalter aus gewoehnlichen Verweisen. Kein Skript: die Ansicht bleibt
   teilbar, und der Zurueck-Knopf tut das Erwartete. */
.segment {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
.segment > * {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--touch); padding-inline: var(--s3);
  border: none; border-radius: calc(var(--radius-sm) - 2px);
  background: none; color: var(--ink-2); font: inherit;
  font-size: var(--fs-label); font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  white-space: nowrap; cursor: pointer;
  transition: color 0.15s, background 0.15s;
}
.segment > :hover { color: var(--ink); text-decoration: none; }
/* Gewaehlt. Zwei Schreibweisen, weil es zwei Bauformen gibt: die
   Verweis-Variante traegt `is-active` vom Server, die Knopf-Variante
   `aria-pressed` — und die muss sie ohnehin tragen, damit ein
   Bildschirmleser den Zustand nennt. Eine Klasse zusaetzlich waere
   dieselbe Aussage ein zweites Mal, mit der Aussicht, dass eine von
   beiden vergessen wird. */
.segment > .is-active,
.segment > [aria-pressed="true"] {
  background: var(--surface); color: var(--accent-strong); box-shadow: var(--shadow-sm);
}

/* Leinwaende fuer die Diagramme.

   Hier steht nur, wieviel Platz eine bekommt — gezeichnet wird in
   `diagramm.js`. Eine feste Hoehe ist noetig und kein Versehen: eine
   Leinwand hat keinen Inhalt, an dem sie wachsen koennte, und ohne
   Angabe faellt sie auf null zusammen. */
.diagramm { width: 100%; min-width: 0; }

.diagramm--ring { aspect-ratio: 1; }
.diagramm--verlauf { height: 16rem; }

/* Vier bis fuenf liegende Balken. Wuechse die Karte, wuerden die Balken
   dicker statt zahlreicher — deshalb eine feste Hoehe und keine
   Streckung. */
.diagramm--balken { height: 9rem; }

/* Der Verweis am rechten Ende einer Rubrikzeile — „Alle Vorgänge".
   Eine abgeschnittene Liste soll sagen, wo der Rest steht. */
.section-head__mehr {
  margin-inline-start: auto;
  font-size: var(--fs-label); font-weight: 650;
  color: var(--accent-strong); white-space: nowrap;
}

/* Der Ring mit der Gesamtzahl in der Mitte.

   Die Zahl liegt als eigenes Element ueber der Leinwand und nicht darin:
   so folgt sie den Tokens der Oberflaeche, bleibt markierbar und ist
   auch dann da, wenn die Leinwand leer bleibt. Ein Bildschirmleser
   kommt an eine Leinwand nicht heran. Der Ring ohne die Zahl ist eine
   Verhaeltnisangabe ohne Bezugsgroesse — man sieht, dass ein Viertel
   erledigt ist, aber nicht, ein Viertel wovon. */
.donut-wrap {
  position: relative;
  /* 13rem und nicht 16: gestapelt — auf dem Handy — nahm der Ring sonst
     fast die halbe Kartenhoehe fuer eine Angabe, die in der Legende
     darunter ohnehin in Zahlen steht. */
  width: min(100%, 13rem);
  align-self: center;
}
.donut__mitte {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  pointer-events: none;
}
.donut__zahl {
  font-size: var(--fs-2xl); font-weight: 800; line-height: 1;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}

/* Ring und Legende teilen sich die Karte.

   Untereinander gestapelt stand der Ring in der 30rem breiten
   Seitenspalte als 16rem grosse Scheibe mit viel Luft links und rechts,
   und die Karte wuchs weit ueber die Liste daneben hinaus. Nebeneinander
   ist sie halb so hoch und der Platz ist genutzt.

   Container- statt Medienabfrage: entscheidend ist die Breite der Karte,
   nicht die des Fensters. Dieselbe Karte steht auf der Startseite in der
   Seitenspalte und auf schmalen Schirmen ueber die volle Breite — die
   Abfrage stimmt in beiden Faellen von selbst. */
.donut-karte { container-type: inline-size; }
.donut-karte__inhalt { display: flex; flex-direction: column; gap: var(--s4); }

@container (min-width: 22rem) {
  .donut-karte__inhalt {
    flex-direction: row; align-items: center; gap: var(--s5);
  }
  /* Der Ring gibt seine feste Groesse auf und behaelt sein Quadrat. Er
     nimmt bewusst weniger als ein Drittel: was rechts danebensteht, sind
     Statusnamen aus der Datenbank, und die kuerzen sonst schon bei
     „Wartet auf…". Der Ring zeigt das Verhaeltnis — lesen muss man die
     Legende. */
  .donut-karte__inhalt .donut-wrap { width: min(32%, 9.5rem); flex: none; }
  .donut-karte__inhalt .legende { flex: 1 1 auto; min-width: 0; }
}

/* Die Legende unter dem Ring. Je Zeile ein Anteilsbalken zwischen Namen
   und Zahl: Boegen eines Rings lassen sich nicht vergleichen — 21 % und
   25 % sehen als Segment gleich aus, als Balken nicht. */
.legende { display: flex; flex-direction: column; }
.legende__zeile {
  display: flex; align-items: center; gap: var(--s2);
  padding-block: var(--s2);
  border-bottom: 1px solid var(--line);
}
.legende__zeile:last-child { border-bottom: none; }
/* Die Plakette nimmt den uebrigen Platz und kuerzt bei Bedarf; Balken
   und Zahl behalten ihre feste Breite, damit sie untereinander auf
   einer Kante stehen. */
/* Die Plakette nimmt ihre eigene Breite und kuerzt erst, wenn der Platz
   nicht reicht — `1 1 auto` streckte sie ueber die ganze freie Breite,
   und eine Plakette mit „Neu" links in einem 200px-Kasten liest sich wie
   ein Eingabefeld. Der Rest der Zeile wandert stattdessen ueber den
   automatischen Aussenabstand nach rechts, damit Balken und Zahl
   untereinander auf einer Kante stehen. */
.legende__zeile .badge--clip { flex: 0 1 auto; margin-inline-end: auto; }
/* `flex-basis` und nicht `width`: der Balken traegt zugleich `.meter`,
   und das setzt weiter unten in dieser Datei `width: 100%`. Beide
   Regeln wiegen gleich schwer, also gewann die spaetere — der Balken
   nahm die ganze Zeile, die Plakette schrumpfte auf einen Buchstaben
   und die Zahl rutschte aus der Karte heraus. Das schob die Seite um
   26px in die Breite. `flex-basis` sticht `width` beim Flex-Kind
   unabhaengig von der Reihenfolge, damit faellt die Falle weg. */
.legende__zeile .legende__balken { flex: 0 0 3.5rem; width: auto; }
.legende__zahl {
  width: 2.5rem; flex: none; text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Die drei Zahlen der Infrastrukturkarte in einer Zeile. „Assets" steht
   rechts, weil es die andere Groesse ist: erreichbar und offline
   ergaenzen sich, der Bestand ist der Rahmen darum. */
.infra-zahlen { display: flex; align-items: baseline; gap: var(--s5); }
.infra-zahlen__ende { margin-inline-start: auto; text-align: right; }
.stat__wert {
  font-size: var(--fs-xl); font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.infra-cpu { padding-top: var(--s3); border-top: 1px solid var(--line); }

/* Die Prozentzahlen unter dem Balkendiagramm. Nicht sein Ersatz: ein
   Balken sagt „viel", diese Zeile sagt „82,4 %" — und sie ist das, was
   ein Bildschirmleser vorliest. */
.infra-cpu__zahlen {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px;
  font-size: var(--fs-label);
}
.infra-cpu__zahlen li {
  display: flex; justify-content: space-between; gap: var(--s3);
}
.infra-cpu__zahlen span:first-child {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Balken fuer Messwerte. Der Fuellstand kommt als --wert (0 bis 100) aus
   der Vorlage; damit braucht es kein zweites Element und kein Skript. */
.meter {
  position: relative; height: 0.5rem; width: 100%;
  border-radius: 999px; overflow: hidden;
  background: var(--surface-3);
}
.meter::after {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  width: clamp(0%, calc(var(--wert, 0) * 1%), 100%);
  border-radius: inherit;
  background: var(--meter-farbe, var(--accent));
}

/* ---------- Formulare ---------- */

.field { display: flex; flex-direction: column; gap: var(--s2); }
.field__label { font-size: var(--fs-sm); font-weight: 650; }

/* Beschriftung links, Hilfsverweis rechts — auf einer Grundlinie, damit
   die beiden als Zeile ueber dem Feld lesen und nicht als zwei Angaben
   untereinander. */
.field__kopf {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3);
}
.field__hilfe {
  font-size: var(--fs-label); font-weight: 650;
  color: var(--accent-strong); white-space: nowrap;
}
.field__hint  { font-size: var(--fs-label); color: var(--ink-3); }
.field__error { font-size: var(--fs-label); font-weight: 650; color: var(--crit); }

.input, .select, .textarea {
  width: 100%; min-height: var(--touch);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink);
  font: inherit; font-size: var(--fs-base);
  transition: border-color 0.15s, background 0.15s;
}
.input:hover, .select:hover, .textarea:hover { border-color: var(--surface-4); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.textarea { min-height: 7rem; resize: vertical; line-height: 1.6; }
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .textarea { border-color: var(--crit); }

.check { display: flex; align-items: flex-start; gap: var(--s3);
         min-height: var(--touch); padding-block: var(--s1); cursor: pointer; }
.check input { width: 1.125rem; height: 1.125rem; margin-top: 0.2rem; flex: none;
               accent-color: var(--accent); }

/* Feldgruppe. Ein Abschnitt eines Formulars mit eigener Ueberschrift —
   auf der Systemseite stehen zwei davon nebeneinander, je einer fuer die
   helle und die dunkle Ansicht. `fieldset` und nicht `div`: die
   Ueberschrift gehoert zu allen Feldern darin, und nur `legend` sagt das
   auch einem Vorleseprogramm. */
.fieldset {
  display: flex; flex-direction: column; gap: var(--s4);
  margin: 0; padding: var(--s4) var(--s5) var(--s5);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2);
  min-width: 0;
}
.fieldset__titel {
  padding-inline: var(--s2);
  font-size: var(--fs-sm); font-weight: 650; color: var(--ink-2);
}

/* --- Kacheln der Geraeteuebersicht ------------------------------------
   Die Hauptwerte, gross genug, um sie im Vorbeigehen zu lesen.

   `auto-fill` mit `minmax`: die Zahl der Kacheln steht nicht fest — ein
   Server hat zwei Datentraeger, der naechste sieben. Eine feste
   Spaltenzahl liesse entweder Luecken oder quetschte. */
.kacheln {
  display: grid; gap: var(--s4);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  margin-bottom: var(--s5);
}

.kachel {
  display: flex; flex-direction: column; gap: var(--s2);
  padding: var(--s4) var(--s5);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);

  /* Der Zustand steht als farbige Kante links und nicht als
     eingefaerbte Flaeche: fuenf bunte Kacheln nebeneinander sind ein
     Warnsignal, das nichts mehr signalisiert. */
  border-left-width: 3px;
}

.kachel.is-ok   { border-left-color: var(--ok); }
.kachel.is-warn { border-left-color: var(--warn); }
.kachel.is-crit { border-left-color: var(--crit); }

.kachel__titel {
  font-size: var(--fs-sm); font-weight: 650; color: var(--ink-2);
  /* Ein langer Datentraegername darf die Kachel nicht sprengen. */
  overflow-wrap: anywhere;
}

/* `tabular-nums`: ohne das springt die Zahl bei jeder Aktualisierung,
   weil die Ziffern verschieden breit sind. */
.kachel__wert {
  font-size: 1.75rem; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.kachel__zusatz { font-size: var(--fs-sm); color: var(--ink-2); }

/* Fuellbalken. Bewusst flach und ohne Verlauf — er soll einen Anteil
   zeigen und keine Aufmerksamkeit fordern. */
.balken {
  height: 0.5rem; border-radius: 999px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
}
.balken__fuellung {
  height: 100%; border-radius: 999px;
  background: var(--ok);
  transition: width 240ms ease;
}
.kachel.is-warn .balken__fuellung { background: var(--warn); }
.kachel.is-crit .balken__fuellung { background: var(--crit); }

/* Bewegung abschalten, wo der Benutzer das verlangt hat. */
@media (prefers-reduced-motion: reduce) {
  .balken__fuellung { transition: none; }
}

/* --- Dienstliste ------------------------------------------------------
   Eine Zeile je Dienst: Zustand, Name, technische Kennung, Alter.

   Grid und nicht Flex, damit die Zustandsmarken untereinander stehen —
   eine Spalte, die man mit dem Auge abfahren kann, ist der ganze Zweck
   dieser Liste. */
.dienstliste { display: flex; flex-direction: column; }

.dienstliste__zeile {
  display: grid; align-items: center; gap: var(--s3);
  grid-template-columns: auto 1fr auto auto;
  padding-block: var(--s2);
  border-bottom: 1px solid var(--line);
}
.dienstliste__zeile:last-child { border-bottom: 0; }
.dienstliste__name { overflow-wrap: anywhere; }

/* Auf schmalem Schirm bricht die Zeile um: vier Spalten auf einem
   Telefon sind vier unlesbare Spalten. */
@media (max-width: 40rem) {
  .dienstliste__zeile {
    grid-template-columns: auto 1fr;
  }
  .dienstliste__zeile > :nth-child(3),
  .dienstliste__zeile > :nth-child(4) { grid-column: 2; }
}

/* --- Verlaufsdiagramme ------------------------------------------------
   Je Metrik eine Kachel: Kopf mit Namen und aktuellem Wert, die Kurve,
   ein Fuss mit Spanne und Herkunft.

   Die Kurve traegt keine eigene Farbe. Sie faerbt sich ueber `color`
   der Kachel — Fuellung und Linie holen sie sich mit `currentColor`.
   So steht die Ampel an einer Stelle und nicht an dreien. */
.verlaeufe {
  display: grid; gap: var(--s5);
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  padding: var(--s5);
}

.verlauf {
  display: flex; flex-direction: column; gap: var(--s2);
  margin: 0;
  color: var(--accent);
}
.verlauf.is-ok   { color: var(--ok); }
.verlauf.is-warn { color: var(--warn); }
.verlauf.is-crit { color: var(--crit); }

.verlauf__kopf {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3);
}
.verlauf__titel {
  font-size: var(--fs-sm); font-weight: 650; color: var(--ink-2);
  /* Ein Datentraegername wie „/var/lib/docker" darf die Kachel nicht
     sprengen. */
  overflow-wrap: anywhere;
}

/* Der aktuelle Wert in der Farbe der Kurve: das Auge verbindet Zahl und
   Linie dann von selbst. `tabular-nums`, sonst springt die Zahl bei
   jeder Aktualisierung. */
.verlauf__jetzt {
  font-size: var(--fs-lg); font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  color: currentColor;
}

.verlauf__feld { position: relative; }

.verlauf__grafik {
  display: block; width: 100%; height: 4.5rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2);
  /* Die Zeichnung reicht bis an den Rahmen; ohne das Beschneiden
     stuende die Fuellung ueber der abgerundeten Ecke. */
  overflow: hidden;
}

/* Die Fuellung ist bewusst blass. Sie soll der Linie Gewicht geben und
   nicht selbst gelesen werden. */
.verlauf__flaeche { fill: currentColor; opacity: 0.16; stroke: none; }

.verlauf__linie {
  fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linejoin: round; stroke-linecap: round;
}

/* Die Achsenbeschriftung liegt in der Zeichnung und nicht daneben —
   daneben kostete sie Breite, die der Kurve fehlt. Der Hintergrund
   schiebt sich unter die Schrift, damit sie auch ueber der Linie
   lesbar bleibt. */
.verlauf__achse {
  /* Buendig an der Kante und nicht eingerueckt: sonst laeuft die Kurve
     rechts an der Beschriftung vorbei und sieht dort aus wie ein
     abgerissener Strich. */
  position: absolute; right: 1px;
  padding: 0 0.3rem;
  font-size: var(--fs-label); line-height: 1.4;
  color: var(--ink-3); background: var(--surface-2);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.verlauf__achse--oben  { top: 1px;    border-radius: 0 var(--radius) 0 0.25rem; }
.verlauf__achse--unten { bottom: 1px; border-radius: 0.25rem 0 var(--radius) 0; }

.verlauf__fuss {
  display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
  font-size: var(--fs-label); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.verlauf__quelle { color: var(--ink-3); margin-left: auto; }

/* Die zweite Fusszeile traegt den technischen Namen und die Herkunft —
   beides Angaben, die man sucht und nicht liest. */
.verlauf__fuss--klein { color: var(--ink-3); }
.verlauf__fuss--klein .mono { overflow-wrap: anywhere; }

/* Auf einem Telefon steht eine Kachel je Zeile — zwei Kurven
   nebeneinander auf 20 Zeichen Breite sind zwei unlesbare Kurven. */
@media (max-width: 40rem) {
  .verlaeufe { grid-template-columns: 1fr; gap: var(--s4); padding: var(--s4); }
  .verlauf__quelle { margin-left: 0; }
}

/* Rollkasten. Eine lange Auswahlliste, die nicht die ganze Seite
   auseinanderzieht — bei den Diensten eines Windows-Servers sind das
   schnell zweihundert Kaestchen.

   Feste Hoechsthoehe und nicht `height`: eine kurze Liste soll auch
   kurz bleiben und keinen leeren Kasten hinterlassen. */
.scrollbox {
  max-height: 18rem; overflow-y: auto; overscroll-behavior: contain;
  padding-inline: var(--s2);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}

/* Farbfeld: Hexwert und Waehler als eine Einheit. Der Waehler allein
   sagt nicht, welcher Wert eingestellt ist, und ein Hexfeld allein
   laesst sich nicht mit dem Auge waehlen — deshalb beide, nebeneinander
   und aneinandergebunden. */
/* Gedeckelt: ein Hexwert ist sieben Zeichen lang, und ein Feld ueber
   die halbe Karte hinweg verspricht eine Eingabe, die es nicht gibt. */
.farbfeld { display: flex; align-items: stretch; gap: var(--s2); max-width: 22rem; }
.farbfeld .input { flex: 1 1 auto; min-width: 0; font-family: var(--font-mono); }
.farbfeld__waehler {
  flex: none; width: var(--touch); min-height: var(--touch);
  padding: var(--s1);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); cursor: pointer;
}
.farbfeld__waehler:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Passwortfeld mit Sichtbarkeitsschalter.

   Rahmen, Grund und Fokusring gehoeren der Huelle und nicht dem
   Eingabefeld darin: laege der Rahmen am Feld, staende der Umschalter
   ausserhalb davon, und der Fokusring umschloesse nur die Haelfte
   dessen, was wie ein Feld aussieht.

   Die Hoehe ist dieselbe --touch wie bei jedem anderen Feld — der
   Umschalter darf sie nicht bestimmen, sonst steht das Feld darueber
   auf einer anderen Kante. */
.pw {
  display: flex; align-items: center;
  height: var(--touch);
  padding-inline-start: var(--s3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.pw:hover { border-color: var(--surface-4); }
.pw:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.pw__input {
  flex: 1 1 auto; min-width: 0; height: 100%;
  border: none; background: none; color: var(--ink);
  font: inherit; font-size: var(--fs-base);
}
/* Der Fokusring liegt an der Huelle; ein zweiter am Feld darin ergaebe
   zwei Ringe ineinander. */
.pw__input:focus { outline: none; }

/* Volle Feldhoehe und --touch breit: auch mit dem Finger zu treffen,
   ohne dass das Feld dafuer hoeher wuerde. */
.pw__eye {
  width: var(--touch); height: 100%; flex: none;
  display: grid; place-items: center;
  border: none; border-radius: var(--radius-sm);
  background: none; color: var(--ink-3); cursor: pointer;
}
.pw__eye:hover { color: var(--ink); }
.pw__eye svg { width: 1rem; height: 1rem; }

/* ---------- Verlauf und Antwort ----------

   Der Ticketverlauf als Unterhaltung statt als Liste. Eigene Beitraege
   rechts, fremde links — dieselbe Ordnung, die jeder aus dem Messenger
   kennt, und man sieht ohne Lesen, wer zuletzt geschrieben hat. */

/* Der Verlauf steht in einer eigenen Karte — genauso wie die Kaesten
   der Seitenspalte und das Antwortfeld darunter. Vorher schwebten die
   Blasen frei auf der Seite und der linke Streifen war der einzige
   Bereich ohne Rahmen; jetzt tragen alle drei Bereiche dieselbe Kante.
   Die Karte ist die gewoehnliche `.card` — der Verlauf braucht dafuer
   keine eigene Bauart. */
.thread { display: flex; flex-direction: column; gap: var(--s5); }
.thread__item { display: flex; flex-direction: column; gap: var(--s2); align-items: flex-start; }
.thread__item--me { align-items: flex-end; }

/* Chatblasen. Die eigene Ecke oben rechts bleibt eckig, die fremde
   oben links — daher kommt die Sprechblasenwirkung. */
.bubble {
  max-width: min(85%, 44rem);
  padding: var(--s3) var(--s4);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-start-start-radius: 0;
  white-space: pre-wrap;
}
.bubble--me {
  background: var(--bubble-me);
  color: var(--bubble-me-ink);
  border-color: var(--accent-line);
  border-start-start-radius: var(--radius-lg);
  border-start-end-radius: 0;
}
.bubble--system { background: var(--surface); color: var(--ink-2); }
.bubble--internal {
  background: var(--warn-soft);
  color: var(--warn);
  border-color: var(--warn);
}

/* Name, Uhrzeit und gegebenenfalls die Marke ueber der Blase. */
.bubble__meta {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  font-size: var(--fs-label); font-weight: 650; color: var(--ink-3);
}

/* Der Umschalter „intern / oeffentlich" in der Kopfzeile der Blase.
   Ein Formular und keine Schaltflaeche im Sinne von `.btn`: er steht
   zwischen Zeitstempel und Kennzeichen und soll dort nicht groesser
   auftreten als die Zeile, in der er sitzt. */
.sicht-form { display: contents; }
.sicht-form__knopf {
  font: inherit; color: var(--ink-3);
  background: none; border: 0; padding: 0;
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.sicht-form__knopf:hover { color: var(--ink); }

/* Bildanhaenge stehen in der Blase. zoom-in kuendigt an, dass ein Klick
   vergroessert; ohne Skript bleibt es ein gewoehnlicher Verweis. */
.bubble__image {
  display: block; margin-top: var(--s3);
  max-width: min(100%, 22rem);
  border-radius: var(--radius); border: 1px solid var(--line);
  cursor: zoom-in;
}

/* Das vergroesserte Bild in einem <dialog>: der Browser bringt Fokusfalle
   und Escape mit, das muss kein Skript nachbauen. */
.lightbox {
  max-width: 92vw; max-height: 92dvh;
  padding: 0; border: none; background: none;
  border-radius: var(--radius-xl); overflow: hidden;
}
.lightbox::backdrop { background: var(--scrim); }
.lightbox img {
  display: block; width: auto; height: auto;
  max-width: 92vw; max-height: 92dvh;
}

/* Antwortkarte. Das Textfeld traegt keinen eigenen Rahmen — die Karte
   ist der Rahmen, sonst stehen zwei Kanten ineinander. */
.composer {
  display: flex; flex-direction: column; gap: var(--s3);
  padding: var(--s4) var(--s5);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.composer .textarea {
  border-color: transparent; background: transparent; padding-inline: 0;
}
.composer .textarea:hover { border-color: transparent; }
.composer .textarea:focus {
  border-color: transparent; box-shadow: none;
  outline: 2px solid var(--accent); outline-offset: 4px;
}
/* Werkzeugleiste unter dem Feld. Das letzte Element rueckt nach rechts —
   dort steht der Absendeknopf. */
.composer__tools {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding-top: var(--s3); border-top: 1px solid var(--line);
}
/* Setzt zwei oder mehr Kinder voraus — bei genau einem rueckte dieses
   nach rechts. Die Leiste traegt Datei, Kaestchen und Absendeknopf. */
.composer__tools > :last-child { margin-inline-start: auto; }

/* Dateiknopf. Ein <label> um ein verstecktes Dateifeld statt eines
   Knopfes mit Skript — so waehlt auch ohne JavaScript jeder eine Datei.
   Gestrichelter Rand, weil hier noch nichts liegt. */
.filepick {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--touch); max-width: 18rem; padding-inline: var(--s4);
  border: 1px dashed var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink-2);
  font-size: var(--fs-sm); font-weight: 650; cursor: pointer;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.filepick:hover { background: var(--surface-3); color: var(--ink); }
/* Das Feld bleibt bedienbar und auffindbar, nur unsichtbar — display:none
   nimmt es der Tastatur weg. */
.filepick input {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%);
}
.filepick:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Volltextsuche ----------

   Steht in der Filterzeile der Ticketlisten und rueckt dort ans rechte
   Ende: links die Knoepfe, die den Bestand zerschneiden, rechts das
   Feld, das ihn durchsucht.

   Ein gewoehnliches Formular, kein Skript. Die Lupe ist Zierat und
   liegt deshalb im Feld statt daneben — sie soll sagen, was das Feld
   tut, ohne eine Spalte zu kosten. */
.suche {
  display: flex; align-items: center; gap: var(--s2);
  position: relative;
  margin-inline-start: auto;
  min-width: min(100%, 20rem);
}
.suche__icon {
  position: absolute; inset-inline-start: var(--s3);
  width: 1rem; height: 1rem; pointer-events: none;
  color: var(--ink-3);
}
/* Platz fuer die Lupe im Feld selbst — sonst laeuft der getippte Text
   durch sie hindurch. */
.suche__feld { padding-inline-start: calc(var(--s3) * 2 + 1rem); }
/* Das eingebaute Kreuz der Browser sitzt in `type="search"` schon da
   und braucht keinen zweiten Knopf daneben. */
.suche__feld::-webkit-search-cancel-button { cursor: pointer; }

/* ---------- Tabellen ---------- */

.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: var(--fs-sm); }
thead th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; padding: var(--s3) var(--s4);
  background: var(--surface-2); color: var(--ink-3);
  font-size: var(--fs-label); font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
tbody td { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Meldungen ---------- */

.flash {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4); margin-bottom: var(--s5);
  border: 1px solid var(--line-strong); border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--surface); font-size: var(--fs-sm);
}
.flash--ok   { border-left-color: var(--ok);   background: var(--ok-soft); }
.flash--warn { border-left-color: var(--warn); background: var(--warn-soft); }
.flash--crit { border-left-color: var(--crit); background: var(--crit-soft); }

.empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--s3);
  padding: var(--s8) var(--s5); text-align: center; color: var(--ink-2);
}

/* Beschriftung links, Wert rechts — fuer Angaben, die man nebeneinander
   vergleicht. Auf schmalen Schirmen untereinander statt gequetscht. */
.kv { display: flex; flex-direction: column; }
.kv__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s4); padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--line);
}
.kv__row:last-child { border-bottom: none; }
.kv__value { text-align: right; word-break: break-all; }

/* Eine Zeile, deren linke Seite nachgibt und deren rechte stehen
   bleibt — die Legende des Rings: der Statusname darf schrumpfen, die
   Anzahl nie. Ohne `min-width: 0` an der linken Seite schiebt ein
   langer Name die Zahl aus der Karte. */
.kv__row--clip { align-items: center; }
.kv__row--clip > :first-child { min-width: 0; flex: 1 1 auto; }
.kv__row--clip > :last-child { flex: none; }

/* Die Farbmuster der Erscheinungsbild-Einstellungen: ein Farbfeld und
   ein Logo, jeweils auf hellem und dunklem Grund nebeneinander — nur so
   faellt ein dunkles Logo auf dunklem Grund auf, bevor es alle sehen. */
.swatch { width: 28px; height: 28px; border-radius: var(--radius-sm);
          border: 1px solid var(--line-strong); display: inline-block; }
.swatch-logo { display: inline-flex; align-items: center; padding: var(--s2);
               border-radius: var(--radius-sm); border: 1px solid var(--line-strong); }
.swatch-logo img { height: 24px; width: auto; object-fit: contain; }
.swatch-logo--hell   { background: #ffffff; }
.swatch-logo--dunkel { background: #020617; }

/* Das Logo der Vorschau steht gross auf seinem Grund — nicht als
   Musterchen: ob es traegt, entscheidet sich in der Groesse, in der es
   spaeter in der Kopfleiste steht, nicht in 24 Pixeln. */
.swatch-logo--gross { justify-content: center; padding: var(--s4); }
.swatch-logo--gross img { height: 40px; }

/* Farbzeile der Tafel: Waehler, Name, Feld. Der Name dehnt sich, damit
   die Felder rechts eine Spalte bilden statt zu flattern. */
.tonzeile { display: flex; align-items: center; gap: var(--s3); }
.tonzeile__name {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
  font-size: var(--fs-sm); font-weight: 650; cursor: pointer;
}
/* Name und Plakette in einer Zeile. Ohne die Huelle dehnte sich die
   Plakette auf die volle Spaltenbreite — die Spalte ist ein
   Flex-Container mit Spaltenrichtung, und da streckt sich jedes Kind. */
.tonzeile__kopf {
  display: flex; align-items: center; gap: var(--s2);
  flex-wrap: wrap;
}

/* Breit genug fuer `#1a2b3c` in der Mono und keinen Deut breiter — ein
   Feld ueber die halbe Zeile verspraeche eine laengere Eingabe. */
.tonfeld { width: 8.5rem; flex: none; }

/* Der Farbwaehler in Groesse des alten Musterquadrats: die Zeile soll
   sich nicht dadurch aendern, dass sie jetzt bedienbar ist. */
.tonwaehler {
  width: 28px; height: 28px; flex: none;
  padding: 2px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
/* Die Browser zeichnen in den Waehler von sich aus einen Rahmen um die
   Farbflaeche. Er sitzt innerhalb unseres eigenen und ergibt einen
   doppelten Strich. */
.tonwaehler::-webkit-color-swatch-wrapper { padding: 0; }
.tonwaehler::-webkit-color-swatch { border: 0; border-radius: 3px; }
.tonwaehler::-moz-color-swatch { border: 0; border-radius: 3px; }

/* Die Deckkraft steht als eigenes Feld daneben und nicht im Hexwert:
   ein `type="color"` kann sie nicht ausdruecken, und ein Schieber per
   Skript liefe der Zusage zuwider, dass die Anwendung ohne JavaScript
   bedienbar bleibt. Breit genug fuer drei Ziffern und das Prozent-
   zeichen dahinter.

   Die Pfeilchen, die Browser an ein Zahlenfeld haengen, sind hier
   Ballast: sie verdoppeln die Feldbreite fuer eine Eingabe, die man
   tippt. */
.tondeckkraft { width: 4.75rem; flex: none; text-align: right; }
.tondeckkraft::-webkit-outer-spin-button,
.tondeckkraft::-webkit-inner-spin-button { appearance: none; margin: 0; }
.tondeckkraft { -moz-appearance: textfield; appearance: textfield; }

/* Die Ueberschrift eines Abschnitts der Farbtafel. Sie traegt die
   Haarlinie darueber, damit die Gruppen auseinanderfallen, ohne dass
   jede einen eigenen Kasten braucht. */
.tongruppe { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.tongruppe:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

.tonzeile.is-invalid .tonfeld { border-color: var(--crit); }

/* Punkt und Plakette der Legende. Kein `.row-x`: das umbricht, und ein
   Statusname soll hier nicht in die naechste Zeile ausweichen, sondern
   schmaler werden. */
.legend__name {
  display: flex; align-items: center; gap: var(--s2); min-width: 0;
}

@media (max-width: 35.999rem) {
  .kv__row { flex-direction: column; align-items: stretch; gap: var(--s1); }
  .kv__value { text-align: left; }
}

/* ---------- Fenster ----------

   Ein <dialog> statt eines nachgebauten Overlays: Fokusfalle, Escape und
   die oberste Ebene bringt der Browser mit. Ohne Skript bleibt derselbe
   Inhalt als gewoehnliche Seite erreichbar. */

.modal {
  width: min(34rem, calc(100vw - 2 * var(--s5)));
  max-height: min(40rem, calc(100dvh - 2 * var(--s6)));
  padding: 0;
  border: 1px solid var(--line-strong); border-radius: var(--radius-xl);
  background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow); overflow: hidden;
}
/* Erst im geoeffneten Zustand ein Flexkasten — geschlossen muss der
   Browser das Fenster weiterhin ausblenden duerfen. */
.modal[open] { display: flex; flex-direction: column; }
.modal::backdrop { background: var(--scrim); }

.modal__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--line);
}
/* Nur der Rumpf rollt; Kopf und Fuss bleiben stehen. */
.modal__body { flex: 1 1 auto; overflow-y: auto; padding: var(--s4) var(--s5); }
.modal__foot {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--s2); padding: var(--s4) var(--s5);
  border-top: 1px solid var(--line);
}

/* Trefferzeile der Personensuche. */
.picker-row {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; min-height: var(--touch);
  padding: var(--s2) var(--s3); border-radius: var(--radius-sm);
  color: inherit; text-align: left; cursor: pointer;
}
.picker-row:hover { background: var(--surface-2); text-decoration: none; }
.picker-row__name  { font-size: var(--fs-sm); font-weight: 650; }
.picker-row__group { font-size: var(--fs-label); color: var(--ink-3); }

/* ---------- Fussbereich ---------- */

.shell-foot {
  padding: var(--s5) var(--gutter);
  border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: var(--s4);
  flex-wrap: wrap;
}

/* ---------- Anmeldung ----------

   Die Anmeldeseite setzt `Layout::bare` und traegt darum weder Kopf- noch
   Fussleiste: es gibt dort keine Navigation, zu der ein Nicht-Angemeldeter
   greifen koennte.

   Sie teilt das Fenster in zwei Spalten. Links stand vorher nichts —
   1280px Schwarz um eine Karte herum; jetzt steht dort, worauf man sich
   hier ueberhaupt anmeldet. Rechts der Weg hinein. Unter 60rem stapeln
   beide, die Markenspalte zuerst: wer auf dem Telefon ankommt, soll
   zuerst sehen, wo er ist. */

/* Der negative Rand hebt die Polsterung von <main> wieder auf. Die
   Spalten sollen bis an die Fensterkante laufen, und <main> weiss nichts
   davon, dass hier ausnahmsweise keine Seite mit Rand steht. */
.auth {
  margin: calc(var(--s6) * -1) calc(var(--gutter) * -1) calc(var(--s8) * -1);
  min-height: 100dvh;
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 60rem) {
  .auth { grid-template-columns: 1fr 1fr; }
}

/* Die Markenspalte. Der Wechsel von --surface auf das --ground der
   rechten Spalte trennt die beiden; die Linie zieht die Trennung nur
   nach. Gestapelt liegt dieselbe Linie waagerecht — eine senkrechte
   Kante ohne Spalte daneben zeigte ins Nichts. */
.auth__side {
  display: flex; flex-direction: column; justify-content: space-between;
  gap: var(--s6);
  padding: var(--s8) var(--s6);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

@media (min-width: 60rem) {
  .auth__side {
    border-bottom: none;
    border-inline-end: 1px solid var(--line);
  }
}

.auth__brand { display: flex; flex-direction: column; gap: var(--s5); }

/* Die grosse Marke. Sie kann `.brand__mark` nicht mitbenutzen: die ist
   auf Symbolgroesse in der Kopfleiste festgelegt, hier soll die Marke
   gross stehen. `currentColor` im SVG genuegt, damit sie die gewaehlte
   Ansicht mitwechselt — die Farbe setzt diese Regel.

   Die Groesse steht hier und nicht als `width` am SVG: ein Attribut
   schlaegt jede Regel, und wer die Seite nachzieht, sucht die Zahl
   vergeblich in dieser Datei. */
.auth__mark { width: 5.5rem; height: auto; flex: none; color: var(--accent); }

/* Ein Satz, kein Absatz: er sagt, was hinter der Anmeldung liegt. Die
   Begrenzung ist enger als --prose, damit er nicht ueber die halbe
   Spalte laeuft. */
.auth__claim { max-width: 34ch; color: var(--ink-2); }

/* Unten links steht nur noch die Versionsnummer — dieselbe `.version`
   wie im Fussbereich jeder anderen Seite. Die Zeilen „INTERNAL SECURITY
   LAYER • TICGET OS" und „TICGET SERVICE DESK PORTAL" standen hier
   vorher; beide sagten nichts, was nicht schon dastand. Die Version
   dagegen ist das, wonach an der Hotline als Erstes gefragt wird. */

/* Die Spalte mit dem Weg hinein. Ein Raster mit genau einer begrenzten
   Spalte statt eines Flex-Stapels: auf einem breiten Schirm waere die
   halbe Fensterbreite als Formularbreite unlesbar. */
.auth__main {
  position: relative;
  display: grid; align-content: center; justify-content: center;
  grid-template-columns: min(28rem, 100%);
  gap: var(--s4);
  padding: var(--s8) var(--s6);
}

.auth__intro { display: flex; flex-direction: column; gap: var(--s1); }

/* Der Umschalter hell/dunkel steht fest oben rechts — es gibt keine
   Kopfleiste, in der er sonst saesse. */
.auth__toggle {
  position: fixed;
  inset-block-start: var(--s5); inset-inline-end: var(--s5);
  z-index: 90;
}

/* Der Sky-Streifen wandert von der Oberkante an die linke Kante — dieselbe
   Geste wie an der hervorgehobenen Kennzahlkachel der Startseite. Oben
   quer trennte er die Karte vom Rest der Seite; an der Seite markiert er
   sie als die Stelle, an der man etwas tut. */
.auth__card {
  position: relative; overflow: hidden;
  padding: var(--s5);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-xl); box-shadow: var(--shadow);
}
.auth__card::before {
  content: ""; position: absolute;
  inset-block: 0; inset-inline-start: 0; width: 4px;
  background: var(--accent);
}

/* Beide Felder der Anmeldung sind exakt gleich hoch. Vorher bestimmte der
   Inhalt die Hoehe, und das Passwortfeld stand mit seinem Umschalter
   darin einen Tick anders als das Feld darueber — an zwei unmittelbar
   untereinander liegenden Feldern faellt genau das auf. Feste Hoehe,
   Polsterung nur waagerecht. */
.auth__card .input { height: var(--touch); padding-block: 0; }

/* Kurzes Ruetteln bei falscher Anmeldung. Die Meldung nennt bewusst nicht,
   was falsch war — damit die Seite kein Verzeichnis vorhandener Konten
   wird. Die Bewegung sagt dafuer unmissverstaendlich, dass etwas geschah.
   Der Block unter prefers-reduced-motion schaltet sie ab. */
@keyframes ruetteln {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-4px); }
  75%      { transform: translateX(4px); }
}
.auth__error { animation: ruetteln 0.2s ease-in-out 2; }

/* Beschriftete Trennlinie zwischen dem Weg via Microsoft und dem
   Formular. Sie sagt: das eine ist nicht die Fortsetzung des anderen,
   sondern die Alternative dazu.

   Die Linie steckt in den beiden Pseudoelementen und liegt nicht als
   Rahmen hinter dem Text. Das spart der Beschriftung einen Grund in
   Seitenfarbe, der sonst bei jedem Wechsel der Ansicht nachgezogen
   werden muesste. */
.auth__divider { display: flex; align-items: center; gap: var(--s4); }
.auth__divider::before,
.auth__divider::after {
  content: ""; flex: 1 1 0; height: 1px; background: var(--line);
}

/* Das vierfarbige Microsoft-Quadrat. Als einziges Zeichen der
   Oberflaeche folgt es nicht `currentColor` — die vier Farben sind
   Microsofts Marke und stehen im SVG. Hier steht nur die Groesse, die
   bisher als Attribut am SVG hing. */
.ms-mark { width: 1.125rem; height: 1.125rem; flex: none; }

/* =====================================================================
   Detailansicht: Leiste, Anwesenheit, Erwaehnungen, Ablage
   ===================================================================== */

/* Drei Spalten. `.split` bringt zwei mit; hier kommt links die
   Anheftungsleiste dazu.

   Zwei Schwellen statt einer: zwischen 60 und 80 rem ist Platz fuer den
   Verlauf und die Werkzeuge, aber nicht fuer eine dritte Spalte —
   dort faellt die Leiste ueber die volle Breite nach oben. Sie steht im
   Markup ohnehin zuerst, weil sie Navigation ist und Navigation nach
   oben gehoert; auf breiten Schirmen setzt `order` sie nach links. */
.split--rail { --rail: 15rem; }

/* Zwischen 60 und 80 rem gilt schon das zweispaltige `.split`, und das
   kennt nur zwei Spalten fuer drei Kinder: die Leiste besetzte die
   erste, der Verlauf rutschte in die schmale zweite und die Werkzeuge
   in die naechste Zeile. Deshalb liegt die Leiste hier ueber die volle
   Breite — sie ist Navigation, und die gehoert nach oben. */
@media (min-width: 60rem) {
  .split--rail > .split__rail { grid-column: 1 / -1; }
}

@media (min-width: 80rem) {
  .split--rail {
    grid-template-columns: var(--rail) minmax(0, 1fr) var(--aside, 21rem);
  }
  .split--rail > .split__rail { grid-column: auto; order: -1; }
  .split__rail {
    position: sticky;
    top: calc(var(--head-h) + var(--s4));
    /* Dieselbe Ueberlegung wie bei der Seitenspalte: fuenfundzwanzig
       angeheftete Vorgaenge sind hoeher als der Schirm, und ihr Ende
       war bisher nur ueber den Verlauf zu erreichen. */
    max-height: calc(100dvh - var(--head-h) - 2 * var(--s4));
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
  }
}

/* Zwei Spalten: die Leiste und der Inhalt. Fuer die Listen, die keine
   Werkzeugspalte haben — `.split--rail` setzt dort eine dritte Spalte,
   die leer bliebe. */
.split--nurleiste { --rail: 15rem; }

@media (min-width: 60rem) {
  .split--nurleiste {
    grid-template-columns: var(--rail) minmax(0, 1fr);
  }
  .split--nurleiste > .split__rail {
    position: sticky;
    top: calc(var(--head-h) + var(--s4));
    max-height: calc(100dvh - var(--head-h) - 2 * var(--s4));
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
  }
}

/* ---------- Eingeklappt ----------

   Zwei Richtungen, je nachdem, wo die Leiste steht.

   Auf dem Handy liegt sie quer ueber dem Ticket. Zur Seite wegzuklappen
   gaebe es dort nichts — die volle Breite bleibt die volle Breite, und
   eine Reihe namenloser Punkte quer ueber den Schirm ist keine
   Navigation. Deshalb klappt sie dort nach oben zu und nach unten auf,
   wie die Kaesten der Seitenspalte: der Kopf bleibt stehen, die Zeilen
   fallen weg.

   Erst wenn sie wirklich als Spalte links steht — im Ticket ab 80rem,
   in den Listen ab 60rem —, wird sie schmal statt leer: die
   Statuspunkte bleiben, der Text faellt weg, `title` traegt, was sonst
   danebenstuende. Ein Punkt fuehrt weiter, ein weggeklappter Kasten
   nicht.

   Der Pfeil dreht sich mit, statt ein zweites Zeichen zu tragen: ein
   Knopf, der seine Beschriftung wechselt, wechselt fuer den Betrachter
   die Identitaet. Das Zeichen zeigt nach links; gedreht zeigt es nach
   oben (offen) und nach unten (zu). */
.pinlist__schalter svg { transition: transform 0.15s; transform: rotate(90deg); }
.split__rail.is-schmal .pinlist__schalter svg { transform: rotate(-90deg); }

.split__rail.is-schmal .pinlist__item,
.split__rail.is-schmal .pinlist__leer { display: none; }

/* Dieselben Regeln an zwei Schwellen, weil die Leiste an zwei
   verschiedenen Stellen zur Spalte wird. `:where()` haelt die
   Gewichtung niedrig, damit die Reihenfolge entscheidet und nicht die
   Laenge des Ausdrucks. */
@media (min-width: 80rem) {
  :where(.split--rail) > .split__rail.is-schmal { --rail: 3.25rem; }
  .split--rail:has(> .split__rail.is-schmal) { --rail: 3.25rem; }

  :where(.split--rail) > .split__rail .pinlist__schalter svg { transform: none; }
  :where(.split--rail) > .split__rail.is-schmal .pinlist__schalter svg { transform: scaleX(-1); }

  :where(.split--rail) > .split__rail.is-schmal .pinlist__item {
    display: flex; justify-content: center; padding-inline: 0;
  }
  :where(.split--rail) > .split__rail.is-schmal .pinlist__leer { display: none; }
  :where(.split--rail) > .split__rail.is-schmal .pinlist__text,
  :where(.split--rail) > .split__rail.is-schmal .pinlist__ueberschrift,
  :where(.split--rail) > .split__rail.is-schmal .card__head .pill { display: none; }
  :where(.split--rail) > .split__rail.is-schmal .card__head {
    justify-content: center; padding: var(--s3) 0;
  }
}

@media (min-width: 60rem) {
  :where(.split--nurleiste) > .split__rail.is-schmal { --rail: 3.25rem; }
  .split--nurleiste:has(> .split__rail.is-schmal) { --rail: 3.25rem; }

  :where(.split--nurleiste) > .split__rail .pinlist__schalter svg { transform: none; }
  :where(.split--nurleiste) > .split__rail.is-schmal .pinlist__schalter svg { transform: scaleX(-1); }

  :where(.split--nurleiste) > .split__rail.is-schmal .pinlist__item {
    display: flex; justify-content: center; padding-inline: 0;
  }
  :where(.split--nurleiste) > .split__rail.is-schmal .pinlist__text,
  :where(.split--nurleiste) > .split__rail.is-schmal .pinlist__ueberschrift,
  :where(.split--nurleiste) > .split__rail.is-schmal .card__head .pill { display: none; }
  :where(.split--nurleiste) > .split__rail.is-schmal .card__head {
    justify-content: center; padding: var(--s3) 0;
  }
}

.icon-btn--klein { width: 1.75rem; height: 1.75rem; padding: 0; }
.icon-btn--klein svg { width: 1rem; height: 1rem; }

/* ---------- Klappkarten in der Seitenspalte ----------

   Nicht mehr die ganze Spalte auf einmal, sondern jeder Kasten fuer
   sich: wer den Verlauf lesen will, klappt Management und Anhaenge zu
   und behaelt die Mitwirkenden. Gebaut wie die Leiste links — Kopf mit
   Ueberschrift und kleinem Pfeilknopf, Rumpf darunter. Der Pfeil zeigt
   nach oben und nach unten, weil hier nichts zur Seite weicht. */
/* Die Spalte ist ein Flex-Stapel mit eigener Hoehenbegrenzung. Ohne
   dieses `flex: none` schrumpfen ihre Kaesten, sobald der Stapel hoeher
   wird als der Schirm — und weil `.card--flush` seinen Ueberlauf
   abschneidet, sah man das nicht als Bildlauf, sondern als abgeschnittene
   Kachel. Genau das passierte beim Aufklappen. */
.split__aside > * { flex: none; }

.klappkarte__schalter svg { transition: transform 0.15s; }
.klappkarte.is-zu > .card__head { border-bottom: 0; }
.klappkarte.is-zu > .klappkarte__inhalt { display: none; }
.klappkarte.is-zu .klappkarte__schalter svg { transform: scaleY(-1); }

/* Die Leiste selbst. Kein Innenabstand an der Karte — die Zeilen sind
   ganzflaechige Verweise und muessen bis an die Kante reichen, sonst
   klickt man neben das Ziel. */
.pinlist { overflow: hidden; }
.pinlist__leer { padding: var(--s4) var(--s5); font-size: var(--fs-sm); }

.pinlist__item {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  color: inherit; text-decoration: none;
  transition: background 0.15s;
}
.pinlist__item:last-child { border-bottom: none; }
.pinlist__item:hover { background: var(--surface-2); text-decoration: none; }

/* Wo man gerade steht. Der Streifen links traegt die Aussage, nicht die
   Farbe allein — ein reiner Farbwechsel waere fuer jeden vierten Mann
   mit Rot-Gruen-Schwaeche keiner. */
.pinlist__item.is-active {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}

.pinlist__text { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1; }
/* Zwei Zeilen und dann Schluss: ein Betreff darf die Leiste nicht in
   die Laenge ziehen, aber eine Zeile schnitte zu oft mitten im Wort. */
.pinlist__title {
  font-size: var(--fs-sm); font-weight: var(--fw-medium); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pinlist__meta { font-size: var(--fs-label); color: var(--ink-3); }

/* Das Zeichen fuer einen ungesendeten Entwurf. In der Warnfarbe: es
   sagt „hier liegt noch etwas", nicht „hier ist etwas kaputt".
   Bewusst ein Quadrat und kein Kreis — links in derselben Zeile steht
   der Statuspunkt, und der traegt in einigen Abteilungen dieselbe
   Farbe. Unterschieden wird deshalb ueber die Form, nicht ueber den
   Farbton allein. */
.pinlist__entwurf {
  width: 0.5rem; height: 0.5rem; border-radius: 2px; flex: none;
  background: var(--warn);
}

/* Wer gerade auf dem Ticket ist. */
.presence { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.presence__wer {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-size: var(--fs-label); font-weight: 650;
  padding: 0.3em 0.7em; border-radius: var(--radius-sm);
  background: var(--surface-3); color: var(--ink-2);
}
.presence__wer::before {
  content: ""; width: 0.45rem; height: 0.45rem; border-radius: 50%;
  background: var(--ok); flex: none;
}
/* Das Gesicht dazu. Klein genug, dass die Plakette nicht hoeher wird
   als die Zeile, in der sie steht — sie sitzt zwischen Verlauf und
   Antwortfeld, und dort darf nichts die Seite verschieben. */
.presence__wer .avatar--klein {
  width: 1.35rem; height: 1.35rem; font-size: 0.6rem; font-weight: 800;
}
.presence__wer.is-tippt { background: var(--accent-soft); color: var(--accent-strong); }
.presence__wer.is-tippt::before { background: var(--accent); animation: pulsen 1.4s infinite; }

@keyframes pulsen { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Der Hinweis auf neue Beitraege. Sieht aus wie eine Erfolgsmeldung,
   traegt aber bewusst **nicht** `.flash--ok`: die Meldungen im Kopf
   entstehen aus einem Code in der Adresse, und ein Test wacht darueber,
   dass eine nicht erkannte Adresse keine Meldung erzeugt. Ein zweites
   Element mit derselben Klasse haette diesen Waechter blind gemacht. */
.livehinweis {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line-strong); border-left-width: 3px;
  border-left-color: var(--ok); border-radius: var(--radius-sm);
  background: var(--ok-soft); font-size: var(--fs-sm);
}
.livehinweis > :last-child { margin-inline-start: auto; }

/* Die Vorschlagsliste bei `@`. Unter dem Textfeld statt an der
   Schreibmarke — die wandert, und in einem Textfeld mit Umbruechen
   waere ihre Lage nur ueber ein Schattenelement zu ermitteln. */
.mentions {
  display: flex; flex-direction: column;
  max-height: 15rem; overflow-y: auto;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); box-shadow: var(--shadow);
}
.mentions__item {
  display: flex; align-items: baseline; gap: var(--s2);
  padding: var(--s2) var(--s3); text-align: start;
  background: none; border: none; cursor: pointer; color: inherit;
}
.mentions__item:hover, .mentions__item.is-active { background: var(--accent-soft); }
.mentions__name { font-weight: var(--fw-medium); }
.mentions__wo { font-size: var(--fs-label); color: var(--ink-3); }

/* Die Ablage der noch nicht abgeschickten Anhaenge. */
.ablage { display: flex; flex-wrap: wrap; gap: var(--s2); }
.ablage__stueck {
  display: inline-flex; align-items: center; gap: var(--s2);
  max-width: 16rem; padding: var(--s1) var(--s2);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); font-size: var(--fs-label);
}
.ablage__stueck img {
  width: 2.25rem; height: 2.25rem; object-fit: cover; flex: none;
  border-radius: calc(var(--radius-sm) - 2px);
}
.ablage__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ablage__weg {
  border: none; background: none; cursor: pointer; color: var(--ink-3);
  font-size: 1rem; line-height: 1; padding: 0 0.2rem; flex: none;
}
.ablage__weg:hover { color: var(--crit); }

/* Der Antwortkasten, waehrend etwas darueber schwebt. */
.composer.is-drop { outline: 2px dashed var(--accent); outline-offset: 4px; }
/* Ein wiederhergestellter Entwurf bekommt eine Kante — er soll nicht
   unbemerkt mit dem naechsten Klick hinausgehen. */
.composer.is-entwurf { border-inline-start: 3px solid var(--warn); }
.composer__hinweis { font-size: var(--fs-label); margin-top: calc(-1 * var(--s3)); }

/* Der Rahmen um ein Bild in der Blase traegt den Anfasser. `resize`
   wirkt nur auf Elemente mit eigenem Ueberlauf, und das ist ein `<img>`
   nicht — deshalb der Rahmen darum. */
/* Der Rahmen um ein Bild in der Blase.

   **Ohne Anfasser.** Der sass hier, solange die Breite eine Vorliebe des
   Betrachters war und in seinem Browser lag. Damit konnte der
   Verfasser seinem Beitrag keine Form geben, und derselbe Beitrag sah
   bei jedem anders aus. Gezogen wird jetzt im Kommentarfeld, die Breite
   haengt am Anhang und steht hier als `style` — wie in einem
   Mailprogramm.

   `max-content` und nicht `min(100%, 22rem)`: sonst zoege der Rahmen
   ein kleines Bild — ein Symbol, ein Ausschnitt — auf volle Breite
   auseinander. Eine gesetzte Breite schlaegt beides. */
.bubble__figur {
  display: block; margin-top: var(--s3);
  width: max-content; max-width: min(100%, 22rem);
}
.bubble__figur .bubble__image { margin-top: 0; width: 100%; max-width: none; }

/* Ein Anhangsknopf traegt einen Dateinamen, und der ist beliebig lang.
   `.btn` setzt `white-space: nowrap` — ohne diese Ausnahme schob ein
   Anhang namens `bildschirmfoto-2026-08-21T11-33-34.webp` auf einem
   Telefon die ganze Seite seitlich hinaus, samt waagerechter
   Bildlaufleiste unter allem. Gekuerzt wird der Name, nicht die
   Groessenangabe daneben: die ist zwei Zeichen lang und traegt die
   Entscheidung, ob man die Datei ueber Mobilfunk oeffnet. */
.btn--datei { max-width: 100%; min-width: 0; white-space: normal; text-align: start; }
/* `anywhere` und nicht `break-word`: ein Dateiname hat keine
   Trennstellen, an denen ein Umbruch erlaubt waere. Ohne diese Erlaubnis
   bleibt seine kleinstmoegliche Breite die volle Laenge — und die
   schiebt ueber die Karte und die Rasterspalte die ganze Seite hinaus.
   Abschneiden waere die Alternative gewesen; ein Dateiname, von dem man
   nur den Anfang sieht, hilft aber niemandem weiter. */
.btn--datei .dateiname { overflow-wrap: anywhere; min-width: 0; }

/* Der Dateiknopf traegt mit Skript zusaetzlich das Einfuegen und das
   Ziehen — der gestrichelte Rand allein saehe danach aus, als ginge nur
   das Klicken. */
.filepick--erweitert { border-style: solid; }

/* Zwei Zeilen unter einem Ankreuzfeld: Beschriftung und Erklaerung. */
.check > span { display: flex; flex-direction: column; gap: 0.1rem; }

/* ---------- Schmale Schirme ---------- */

@media (max-width: 47.999rem) {
  /* Hier tauschen die offenen Zeilen und das Aufklappmenue die
     Plaetze. Drei Zeilen nebeneinander waeren auf einem Telefon drei
     Wischleisten uebereinander — man muesste in jeder einzeln
     scrollen, um zu sehen, was darin steht. Der ganze Baum steckt
     stattdessen hinter einem Knopf und klappt nach unten auf.

     Nicht `display: none` auf `.nav-top` allein: die Unterleisten
     muessen mit, sonst bliebe unter dem Kopf ein leerer Streifen. */
  .nav-top, .nav-sub { display: none; }
  .navmenu { display: block; }

  .topbar { gap: var(--s3); }
  /* Der Name rueckt nach rechts von den Werkzeugen weg — ohne die
     Bereichszeile dazwischen klebte sonst alles am linken Rand. */
  .topbar > .brand { margin-inline-end: auto; }

  /* Die Kopfleiste ist jetzt ueberall eine Zeile hoch, auch dort, wo
     breit drei stehen. */
  body, body.has-tree { --head-h: calc(var(--rainbow-h) + var(--topbar-h)); }
}

@media (min-width: 48rem) {
  .usermenu__name { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .rainbow, .shell-head, .shell-foot, .page-head__actions { display: none; }
  body { background: #fff; color: #000; }
}


/* =====================================================================
   Die E-Mail aus dem Ticket heraus
   ===================================================================== */

/* Ein eigener Kasten unter dem Antwortfeld und kein Kaestchen darin.

   Hier stand einmal „Auch als E-Mail versenden" — ein Haken zwischen
   „Datei" und „Antworten". Ein Schalter unter Schaltern, und genau so
   haekelt man versehentlich an. Eine Mail verlaesst das Haus; das ist
   keine Zutat zu einer Antwort, sondern eine eigene Handlung, und sie
   bekommt deshalb einen eigenen Rahmen mit eigenem Knopf.

   Die Kante in der Betonungsfarbe und nicht in der Warnfarbe: eine Mail
   zu schicken ist die Absicht und kein Versehen. */
.mailkasten {
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

/* Der Griff ist die ganze Zeile und nicht nur das Dreieck: ein
   Klickziel von zwei Zeichen Breite findet niemand auf Anhieb. */
.mailkasten__griff {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) var(--s4);
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  cursor: pointer; list-style: none;
}

.mailkasten__griff::-webkit-details-marker { display: none; }

.mailkasten__griff:hover { color: var(--accent); }

.mailkasten[open] .mailkasten__griff {
  border-block-end: 1px solid var(--line);
}

.mailform {
  display: flex; flex-direction: column; gap: var(--s4);
  padding: var(--s4);
}

/* Empfaenger und Kopie als Gruppe. Der Rahmen faellt weg — `fieldset`
   bringt ihn mit, und im Kasten waere er ein Rahmen im Rahmen. Die
   Beschriftung traegt die Trennung. */
.mailform__kreis {
  display: flex; flex-direction: column; gap: var(--s2);
  margin: 0; padding: 0; border: 0;
}

/* Ein Vorschlag je Zeile und nicht nebeneinander: die Adresse steht
   daneben, und zwei Haken in einer Zeile sind zwei Ziele, zwischen
   denen man beim Klicken danebengreift. */
.mailform__haken {
  display: flex; align-items: baseline; gap: var(--s2);
  font-size: var(--fs-sm); cursor: pointer;
}

.mailform__haken .mono { font-size: var(--fs-xs); }

/* =====================================================================
   Der Kopf einer verschickten Mail im Verlauf
   ===================================================================== */

/* Er steht ueber der Blase, nicht darin: die Blase gibt ihren Inhalt
   mit `white-space: pre-wrap` wieder, und ein Definitionslisten-Markup
   darin brauchte Leerraum, der als Leerzeile sichtbar wuerde.

   Inhaltlich ist er der Umschlag und nicht der Text. Ohne ihn stuende
   im Verlauf nur, *was* geschrieben wurde, und nicht, *wer* es bekommen
   hat — und in Kopie ging es womoeglich an jemanden, der mit dem
   Vorgang sonst nichts zu tun hat. */
.mailkopf {
  align-self: flex-start;
  max-width: min(100%, 44rem);
  margin-block-end: calc(var(--s1) * -1);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-block-end: 0;
  border-start-start-radius: var(--radius-sm);
  border-start-end-radius: var(--radius-sm);
  background: var(--surface-2);
  font-size: var(--fs-xs);
}

.thread__item--me .mailkopf { align-self: flex-end; }

.mailkopf__marke {
  display: inline-flex; align-items: center; gap: var(--s1);
  color: var(--accent); font-weight: var(--fw-medium);
}

/* Beschriftung und Wert nebeneinander, die Werte in einer Flucht — eine
   Kopfzeile liest man an der linken Kante entlang. */
.mailkopf__felder {
  display: grid; grid-template-columns: auto 1fr;
  gap: 0 var(--s2); margin: var(--s1) 0 0;
}

.mailkopf__felder dt { color: var(--muted); }
.mailkopf__felder dd { margin: 0; overflow-wrap: anywhere; }

/* Die Blase darunter schliesst an den Kopf an, statt daneben zu
   schweben: beides ist eine Nachricht. */
.bubble--mail {
  border-start-start-radius: 0;
  border-start-end-radius: 0;
}

/* =====================================================================
   Bilder im Kommentarfeld: hier wird gezogen
   ===================================================================== */

/* Ein Bild in der Ablage ist keine Miniatur mehr, sondern die Vorschau
   dessen, was im Verlauf stehen wird — mitsamt Anfasser. Deshalb faellt
   es aus der Zeile heraus und bekommt eine eigene Spalte. */
.ablage__stueck--bild {
  flex-direction: column; align-items: flex-start;
  max-width: min(100%, 24rem); gap: var(--s1);
  padding: var(--s2);
}

/* Der Rahmen traegt den Anfasser: `resize` wirkt nur auf Elemente mit
   eigenem Ueberlauf, und das ist ein `<img>` nicht. */
.ablage__figur {
  display: block;
  width: max-content; min-width: 5rem; max-width: 100%;
  resize: horizontal; overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
}
.ablage__figur img {
  display: block; width: 100%; height: auto;
  border-radius: 0; object-fit: contain;
}

/* Die eingestellte Breite in Zahlen. Ohne sie zieht man ins Blaue —
   und zwei Bilder in einem Beitrag gleich breit zu bekommen waere
   Gluecksache. */
.ablage__masse {
  font-size: var(--fs-label); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* Die Miniatur der uebrigen Dateien bleibt, wie sie war — die Regel
   oben greift nur fuer `--bild`. */

/* =====================================================================
   Verweise auf Personen
   ===================================================================== */

/* `@Name` im Verlauf. Gepunktete Unterlinie statt einer vollen: der
   Text ist ein Beitrag und keine Verweisliste, und ein Dutzend blau
   unterstrichener Namen darin liest sich wie ein Formular. */
.mention-link {
  color: var(--accent-strong); font-weight: var(--fw-medium);
  text-decoration: underline dotted; text-underline-offset: 0.15em;
}
.mention-link:hover { text-decoration: underline solid; }

/* In der eigenen Blase liegt die Betonungsfarbe auf der Betonungsfarbe
   — und ist damit nicht zu lesen. Dort traegt der Verweis die
   Schriftfarbe der Blase; unterschieden wird ueber die Linie, nicht
   ueber den Farbton. Dieselbe Ueberlegung wie beim Statusstreifen in
   der Ticketliste. */
.bubble--me .mention-link,
.bubble--internal .mention-link { color: inherit; }

/* Bild und Name ueber der Blase fuehren aufs Profil. Der Verweis traegt
   keine eigene Farbe: er steht in der Kopfzeile der Blase, und die hat
   ihre eigene. Sichtbar wird er beim Ueberfahren. */
.who-link {
  display: inline-flex; align-items: center; gap: var(--s2);
  color: inherit; text-decoration: none; border-radius: var(--radius-sm);
}
.who-link:hover { text-decoration: none; }
.who-link:hover strong { text-decoration: underline; }
.who-link:hover .avatar { box-shadow: 0 0 0 2px var(--accent); }
.who-link .avatar { transition: box-shadow 0.15s; }

/* Der Kopf des Profils: dasselbe Zeichen wie ueberall, nur groesser.

   Der Selektor nennt `.who` mit, obwohl `.avatar--gross` allein
   gereicht haette — `.who .avatar` weiter oben ist gleich spezifisch
   und steht spaeter im Stylesheet, und damit gewaenne dort die kleine
   Groesse. Ein `!important` waere die andere Loesung und die
   schlechtere: es gilt dann auch dort, wo es niemand erwartet. */
.who--gross { gap: var(--s4); }
.who .avatar--gross,
.avatar--gross {
  width: 4rem; height: 4rem; font-size: var(--fs-lg); flex: none;
}

/* Eine Trennlinie innerhalb einer Karte — dort, wo zwei Dinge in
   derselben Karte stehen und trotzdem zwei Dinge bleiben sollen:
   Dateien und Geraete. */
.trennlinie {
  height: 1px; background: var(--line); margin-block: var(--s1);
}
