/* Chat – Gestaltung.
 *
 * Eigene Datei, weil an public/styles.css die andere Spur arbeitet
 * (AUSTAUSCH/REVIER.md, Abschnitt "Dauerhaft bei Kamakasis Spur"). Alle Werte
 * kommen aus styles.css: keine eigene Farbe, keine eigene Schriftgrösse,
 * keine erfundene Rundung. Auch die Pixelwerte sind solche, die dort schon
 * vorkommen. Was hier steht, ist Anordnung – die Sprache kommt von dort.
 * Eingebunden als LETZTES Stylesheet in public/index.html, damit die wenigen
 * Regeln, die an fremde Klassen andocken (die Fusszeilen-Entscheidung ganz
 * unten), bei gleicher Spezifität gewinnen.
 *
 * ---------------------------------------------------------------------------
 * DER SATZ, AN DEM SICH JEDE ZEILE HIER MESSEN LASSEN MUSS
 * ---------------------------------------------------------------------------
 * Ein Chat ist NICHT in der Prüfkette und ist KEIN Beweismittel.
 *
 * Wenn das nicht auf den ersten Blick sichtbar ist, schreibt jemand ein
 * Vorkommnis in den Chat und glaubt, es sei erfasst – und merkt es erst, wenn
 * es zählt. Das ist das grösste Risiko dieser Funktion, grösser als jede
 * technische Frage.
 *
 * Die Gestaltung trägt das an vier Stellen, und zwar RUHIG:
 *   1. Das Band über dem Fenster (.ch-hinweis) – steht immer da, scrollt nie
 *      weg, weil das Fenster selbst nicht scrollt (nur Liste und Verlauf tun
 *      das).
 *   2. Die gestrichelte Kante um das Gespräch (.ch-gespraech) und um jedes
 *      Bild (.ch-bild).
 *   3. Die gestrichelte Marke im Gesprächskopf (.ch-marke).
 *   4. Die Zeile unter dem Eingabefeld (.ch-fusszeile) – dort, wo jemand
 *      gerade tippt, also genau im Augenblick des Irrtums.
 *
 * KEIN Rot, kein Ausrufezeichen, kein Alarmton. Ein Warnton, den man bei
 * jedem Öffnen sieht, ist nach einer Woche unsichtbar; eine dauerhafte
 * Eigenschaft der Fläche ist es nicht. Die gestrichelte Form ist im Haus
 * bereits belegt mit "Angabe ohne Beleg": rundgang.css:256-264
 * (.rg-art.stufe-angegeben, Kommentar wörtlich "Die Form sagt es schon,
 * bevor jemand den Text liest") und rundgang.css:367 (.rg-zeile.ist-leer).
 * Wer beide Ansichten benutzt, hat die Bedeutung schon gelernt.
 *
 * ---------------------------------------------------------------------------
 * DATENSCHUTZ – was hier BEWUSST FEHLT
 * ---------------------------------------------------------------------------
 * Ein Chat zwischen Mitarbeitern ist keine Betriebsauswertung. Dieselbe Linie
 * wie bei der Ortung (livemap.js:2103-2108) und bei der Kontenliste
 * (src/routes/admin.js:58: "hat Spuren: ja/nein", keine Anzahl, weil eine Zahl
 * je Person sortierbar ist und dann als Rangliste gelesen wird – Profiling
 * nach Art. 5 lit. f DSG, Verhaltensüberwachung nach Art. 26 ArGV 3).
 *
 * Es gibt darum in dieser Datei KEINE Klasse für:
 *   - "schreibt gerade ..." – das wäre eine Anwesenheitsanzeige je Person,
 *     und der Server hat dafür ohnehin keinen Weg: der Live-Kanal trägt nie
 *     Inhalte, nur den Anstoss zum Nachladen (src/live.js:34-39).
 *   - "zuletzt online", "aktiv vor 5 Minuten" – dasselbe in langsam.
 *   - eine Lesequote, eine Antwortzeit, eine Auswertung je Person.
 *
 * Die Lesebestätigung (.ch-zustand.ist-gelesen) bleibt – aber sie ist eine
 * Auskunft ZWISCHEN den zwei Beteiligten, keine Kennzahl FÜR die Leitung.
 * Das Gegenstück, das es hier nicht geben darf, steht in
 * src/routes/sites.js:1081 (zwei Listen "gelesen"/"offen" für die Leitung) –
 * dort ist es bei Dienstvorschriften richtig, hier wäre es Kontrolle.
 *
 * ---------------------------------------------------------------------------
 * FÜR WEN das gebaut ist
 * ---------------------------------------------------------------------------
 * Handy, Nacht, Handschuhe, eine Hand am Gitter. Daraus folgt: 44 px
 * Trefferfläche überall, ein Bereich pro Bildschirm (Liste ODER Gespräch,
 * nie beides gequetscht), das Eingabefeld klebt unten und wandert nie, und
 * kein Dialog – ein Dialog auf dem Handy ist ein Bildschirm, den man
 * versehentlich schliesst.
 *
 * ---------------------------------------------------------------------------
 * KLASSENVERZEICHNIS – der Vertrag mit public/chat.js
 * ---------------------------------------------------------------------------
 * .ch                     Wurzel im Halter #chatHalter. Trägt .ist-gespraech,
 *                         solange auf dem Handy das Gespräch offen ist.
 *   .ch-hinweis           Das Band. Immer da.
 *   .ch-flaechen          Das zweispaltige Raster.
 *     .ch-liste           Linke Spalte: Kopf, Suche, Einträge.
 *       .ch-liste-kopf / .ch-suche / .ch-eintraege / .ch-eintrag
 *       .ch-avatar (.ist-gruppe) / .ch-ungelesen / .ch-leer
 *     .ch-gespraech       Rechte Spalte: Kopf, Verlauf, Eingabe.
 *       .ch-kopf (.ch-zurueck, .ch-marke)
 *       .ch-verlauf (.ch-trenner, .ch-blase.ist-eigen|.ist-fremd)
 *       .ch-eingabe (.ch-anhang, .ch-feld, .ch-senden)
 *       .ch-fusszeile
 *   .ch-meldung           Ladefehler/Leermeldung mit "Nochmal versuchen".
 *
 * Zustände immer als ist-Klasse, nie als style-Attribut – index.html und die
 * Module halten sich durchgehend daran (rundgang.css:24, styles.css:138-139).
 */

/* ========================================================================== */
/* Wurzel und Höhe                                                           */
/* ========================================================================== */

/* Das Fenster ist höhenbegrenzt und scrollt NICHT. Gescrollt wird nur innen –
   die Chatliste und der Verlauf. Nur so bleibt das Eingabefeld unten stehen,
   ohne dass es klebend über den Text rutscht, und nur so kann das Band oben
   nicht wegscrollen: Was nicht scrollt, kann man nicht wegscrollen.

   Die Zahl 88 ist nicht geraten: main hat oben 24 px und unten 64 px
   Innenabstand (styles.css:223). 100dvh minus beides ergibt genau eine Seite
   ohne Rollbalken. dvh statt vh, weil auf dem Handy die Adressleiste sonst
   den letzten Zentimeter frisst – styles.css:147 und 338 machen es ebenso. */
.ch {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Dieselbe Breite wie die übrigen Ansichten (heim.css:21). Zwei Ansichten
     mit zwei Breiten wirken verschoben, und genau das hat Sven schon einmal
     gemeldet. */
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  height: calc(100dvh - 88px);
  /* Unter dieser Höhe wird aus dem Fenster ein Streifen. Dann darf die Seite
     lieber rollen als der Verlauf auf drei Zeilen schrumpfen. */
  min-height: 420px;
}

/* ========================================================================== */
/* Das Band: "kein Beweismittel"                                              */
/* ========================================================================== */

/* Ruhig, aber unübersehbar. Unübersehbar wird es nicht durch Farbe, sondern
   durch drei Dinge, die alle dauerhaft sind: die gestrichelte Kante, der
   schraffierte Streifen links, und die Tatsache, dass es nie weggeht.

   Die Schraffur ist ein Muster, kein Farbwert – sie rechnet ausschliesslich
   mit var(--border-strong). Für das Auge sagt sie "andere Sorte Fläche",
   noch bevor jemand ein Wort liest. */
.ch-hinweis {
  position: relative;
  flex: none;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 11px 14px 11px 20px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-1);
  color: var(--text-secondary);
  font-size: var(--fs-md);
  line-height: 1.55;
  overflow: hidden;
}

.ch-hinweis::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 6px;
  background: repeating-linear-gradient(
    135deg,
    var(--border-strong) 0 2px,
    transparent 2px 6px
  );
}

.ch-hinweis svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 1px;
  color: var(--text-muted);
}

/* WORTBUDGET, an das sich chat.js halten muss. Auf 375 px gemessen:
     55 Zeichen  → 2 Zeilen, Band 129 px
     90 Zeichen  → 3 Zeilen, Band 149 px
    140 Zeichen  → 4 Zeilen, Band 168 px
   Bei 140 Zeichen frisst der Satz ein Fünftel des Bildschirms – und ein Satz,
   der so viel Platz nimmt, wird beim zehnten Öffnen weggelesen. Zwei Zeilen
   sind das Ziel. Kürze ist hier nicht Sparsamkeit, sondern die Bedingung
   dafür, dass der Satz überhaupt stehen bleiben darf. */
.ch-hinweis-text { margin: 0; }

/* Das eine Wort, das hängen bleiben soll. Fett statt farbig: Fett überlebt
   den Ausdruck, die Farbenblindheit und den Bildschirm im Sonnenlicht. */
.ch-hinweis-text b { color: var(--text-primary); font-weight: 600; }

/* Der Gegenweg gehört direkt neben die Warnung, nicht drei Bildschirme
   weiter: "Nicht erfasst" ohne "so wird es erfasst" ist eine Sackgasse. */
.ch-hinweis-weg {
  flex: none;
  margin-left: auto;
  min-height: 44px;
  align-self: center;
}

/* ========================================================================== */
/* Das Raster: Liste und Gespräch                                            */
/* ========================================================================== */

.ch-flaechen {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: 12px;
  /* Ohne diese Zeile wachsen die Spalten mit ihrem Inhalt aus dem Fenster
     heraus statt innen zu rollen – der bekannteste Fallstrick am Raster. */
  min-height: 0;
}

.ch-liste,
.ch-gespraech {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  border-radius: var(--radius);
  background: var(--surface-1);
}

/* Die Liste ist eine gewöhnliche Fläche: eine Aufzählung von Gesprächen
   ist keine Aussage über Beweiswert. */
.ch-liste {
  border: 1px solid var(--border);
  overflow: hidden;
}

/* Das Gespräch trug hier eine gestrichelte Kante, in derselben Bedeutung wie
   überall im Haus: "noch ohne Beleg". Die Bedeutung stimmt – die GRÖSSE nicht.
   Auf einer Fläche von 800 Pixeln liest sich eine gestrichelte Umrandung nicht
   als Aussage, sondern als unfertige Seite; Sven hat es genau so gemeldet
   ("sieht nicht schön aus, merkst du selber oder?").

   Die Aussage bleibt, sie steht nur dort, wo sie im Blick ist: im Band ganz
   oben, an jeder Sprechblase, an jedem Bild. Das sind die Stellen, an denen
   jemand eine Nachricht für erfasst halten könnte – nicht der Rahmen um den
   ganzen Bildschirm. */
.ch-gespraech { border: 1px solid var(--border); }

/* ========================================================================== */
/* Chatliste – Kopf und Suche                                                 */
/* ========================================================================== */

.ch-liste-kopf {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
}

.ch-liste-titel {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 600;
}
.ch-liste-titel:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 4px; }

.ch-neu {
  flex: none;
  min-height: 44px;
  min-width: 44px;
  justify-content: center;
}

.ch-suche {
  position: relative;
  flex: none;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

.ch-suche-lupe {
  position: absolute;
  left: 24px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  pointer-events: none;
  color: var(--text-muted);
}

/* 44 px hoch, und links Platz für die Lupe. Die Suche ist der Weg, der bei
   dreissig Gesprächen als einziger noch trägt – sie steht darum fest oben
   und rollt nicht mit der Liste weg. */
.ch-suche-feld {
  width: 100%;
  min-height: 44px;
  padding-left: 38px;
  border-radius: 9px;
  font-size: var(--fs-lg);
}

/* Das Kreuz, das Safari und Chrome in ein type=search hängen, ist 12 px
   gross und liegt am Rand – mit Handschuhen nicht zu treffen und daneben
   liegt das Feld selbst. Weg damit; geleert wird durch Überschreiben. */
.ch-suche-feld::-webkit-search-cancel-button { display: none; }

/* ========================================================================== */
/* Chatliste – Einträge                                                      */
/* ========================================================================== */

.ch-eintraege {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ch-eintrag {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* 64 px: eine Zeile, die man mit Handschuhen trifft, ohne dass vier Stück
     den Bildschirm füllen. heim.css:158-160 nimmt aus demselben Grund 56 –
     hier kommt der Vorschautext dazu, darum eine Stufe höher. */
  min-height: 64px;
  padding: 9px 12px;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-schnell) var(--easing);
}
.ch-eintrag:last-child { border-bottom: 0; }
.ch-eintrag:hover { background: var(--surface-sunken); }
/* Der Ring liegt innen: aussen würde ihn der Rollbereich abschneiden. */
.ch-eintrag:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: -2px; }

/* Das offene Gespräch. Fläche statt Farbbalken – ein Balken links wäre die
   dritte Bedeutung, die an dieser Kante hängt (siehe styles.css:558-560, wo
   die gleiche Überlegung die Farbkante am Dienst gestrichen hat). */
.ch-eintrag.ist-aktiv {
  background: var(--accent-soft);
  color: var(--accent-fg);
}

.ch-eintrag-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ch-eintrag-kopf {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.ch-eintrag-name {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-lg);
  font-weight: 400;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ch-eintrag-zeit {
  flex: none;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ch-eintrag-vorschau {
  font-size: var(--fs-md);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Ungelesen wird DOPPELT getragen: der Zähler rechts und das Gewicht der
   Schrift. Farbe allein ist für rund jeden zwölften Mann keine Information –
   dieselbe Regel wie heim.css:174-176. */
.ch-eintrag.ist-ungelesen .ch-eintrag-name { font-weight: 600; }
.ch-eintrag.ist-ungelesen .ch-eintrag-vorschau { color: var(--text-primary); }

/* ========================================================================== */
/* Avatar und Ungelesen-Zähler                                               */
/* ========================================================================== */

/* Einzelchat rund, Gruppenchat eckig. Die FORM sagt, ob dahinter ein Mensch
   oder eine Gruppe steht – nicht die Farbe und nicht ein zusätzliches
   Symbol. Wer "Team ZH37" nicht liest, sieht es trotzdem. */
.ch-avatar {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 999px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  overflow: hidden;
}
.ch-avatar.ist-gruppe { border-radius: 11px; }
.ch-avatar svg { width: 19px; height: 19px; }

/* Ein gelöschtes Konto trägt kein Kürzel mehr (src/routes/datenschutz.js
   macht aus dem Namen "Gelöschte Person"). Gestrichelt statt leer, damit die
   Lücke als Lücke erkennbar bleibt. */
.ch-avatar.ist-unbekannt {
  border-style: dashed;
  border-color: var(--border-strong);
  color: var(--text-muted);
}

/* Die Zahl sagt, wie viele es SIND. Gleiche Bauart wie heim.css:122-134,
   damit dieselbe Aussage im ganzen Programm dieselbe Gestalt hat. */
.ch-ungelesen {
  display: inline-grid;
  place-items: center;
  flex: none;
  min-width: 24px;
  height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Die Vorlage zeigt an manchen Zeilen nur einen Punkt. Er braucht ein
   aria-label aus chat.js – ein Punkt allein sagt einer Sprachausgabe nichts. */
.ch-ungelesen.ist-punkt {
  min-width: 10px;
  width: 10px;
  height: 10px;
  padding: 0;
  background: var(--accent);
  font-size: 0;
}

/* Im offenen Gespräch steht die Blase auf accent-soft – dort ginge sie
   unter. Umgekehrte Füllung, gleiche Bedeutung. */
.ch-eintrag.ist-aktiv .ch-ungelesen {
  background: var(--accent);
  color: var(--accent-ink);
}

/* ========================================================================== */
/* Gesprächskopf                                                             */
/* ========================================================================== */

.ch-kopf {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-1);
  border-radius: var(--radius) var(--radius) 0 0;
}

/* Zurück gibt es nur, solange Liste und Gespräch einander verdrängen –
   also auf dem Handy. Auf dem Schreibtisch stehen beide nebeneinander, dort
   wäre der Knopf ein Weg ohne Ziel. */
.ch-zurueck {
  display: none;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  border-radius: 9px;
  cursor: pointer;
  place-items: center;
}
.ch-zurueck:hover { background: var(--surface-sunken); color: var(--text-primary); }
.ch-zurueck svg { width: 21px; height: 21px; }

.ch-kopf-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }

/* Bekommt nach jedem Wechsel den Fokus (tabindex="-1" im Markup), damit eine
   Sprachausgabe zuerst sagt, MIT WEM man schreibt. Gleiches Vorgehen wie
   rundgang.js an vier Stellen (u.a. 897/974). */
.ch-kopf-name {
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ch-kopf-name:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 4px; }

.ch-kopf-unter {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Die Marke im Kopf – dieselbe Form wie rundgang.css:256-264
   (.rg-art.stufe-angegeben): gestrichelt, eckig, gedämpft, normales Gewicht.
   Sie schreit nicht, sie steht einfach immer da. */
.ch-marke {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  font-weight: 400;
  white-space: nowrap;
}

/* ========================================================================== */
/* Verlauf                                                                    */
/* ========================================================================== */

.ch-verlauf {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 12px;
  scroll-behavior: smooth;
}

/* Der Datumstrenner: eine Beschriftung mit Haarlinie links und rechts. Er ist
   die einzige Ordnung im Verlauf – Uhrzeiten allein beantworten "gestern oder
   letzte Woche" nicht. */
.ch-trenner {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 8px 0 4px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.ch-trenner::before,
.ch-trenner::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.ch-trenner:first-child { margin-top: 0; }

/* "Ältere Nachrichten laden" steht ganz oben im Verlauf und muss dort ruhig
   bleiben: Es ist ein Weg, keine Handlung. Darum mittig, schmal und ohne
   Farbe – der Blick soll an ihm vorbei nach unten zur letzten Nachricht. */
.ch-aelter { align-self: center; margin-bottom: 8px; }

/* -------------------------------------------------------------- Sprechblase */

.ch-blase {
  position: relative;
  /* Zwei Grenzen, weil eine nicht reicht: Der Anteil hält die Blase auf dem
     Handy schmal genug, dass die Gegenseite sichtbar bleibt; die 640 px halten
     sie auf dem Schreibtisch lesbar. Eine Zeile über die volle Breite eines
     grossen Bildschirms verliert beim Umbruch den Anschluss – 640 px ist die
     Zeilenlänge, mit der auch der Rundgang rechnet (rundgang.css:30). */
  max-width: min(78%, 640px);
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 9px 13px;
  border-radius: var(--radius);
  /* Kein box-shadow. Schatten sind im Haus die Sprache der Karten – und eine
     Karte ist ein Dokument. Eine Blase liegt flach auf der Fläche. */

  /* Antippen öffnet den Weg zum Rapport. Der Zeiger ist der einzige Hinweis
     darauf, den es braucht: Ein Text, der das erklärt, stünde unter jeder
     Nachricht – und damit wären wir wieder bei dem Wald, den wir gerade
     abgeholzt haben. */
  cursor: pointer;
}
/* Beim Überfahren nur eine Spur heller. Nicht mehr: Der Verlauf soll ruhig
   bleiben, wenn die Maus darüberfährt. */
.ch-blase:hover { filter: brightness(1.03); }
.ch-blase.ist-offen { filter: none; }

/* Fremd links, eigen rechts – und zusätzlich verschieden gefüllt. Die Seite
   allein trägt es nicht: bei einer Reihe eigener Nachrichten am Stück sieht
   man keinen Rand zum Vergleichen. */
.ch-blase.ist-fremd {
  align-self: flex-start;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-bottom-left-radius: 4px;
}
.ch-blase.ist-eigen {
  align-self: flex-end;
  background: var(--accent-soft);
  /* Dieselbe Kante wie beim laufenden Dienst (styles.css:570) – ein
     Akzentton, der die Fläche nicht zur Schaltfläche macht. */
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-bottom-right-radius: 4px;
}

/* Aufeinanderfolgende Nachrichten derselben Person rücken zusammen und
   verlieren den Namen. Ein Absendername über jeder Zeile macht aus zehn
   Zurufen eine Behördenakte. */
.ch-blase.ist-folge { margin-top: -4px; }

.ch-blase-von {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-secondary);
}
/* Am eigenen Beitrag ist "von wem" durch die Seite schon beantwortet.
   ACHTUNG, Vertrag mit chat.js: Der Name muss trotzdem IM MARKUP stehen –
   ausgeblendet, nicht weggelassen. Auf Papier gibt es kein Links und kein
   Rechts, dort wird er wieder eingeblendet (siehe @media print). Fehlt das
   Element, steht im Ausdruck ein Satz ohne Sprecher. */
.ch-blase.ist-eigen .ch-blase-von { display: none; }

.ch-blase-text {
  margin: 0;
  font-size: var(--fs-lg);
  line-height: 1.5;
  /* Zeilenumbrüche des Absenders bleiben stehen, und ein 40 Zeichen langes
     Kennzeichen ohne Leerschlag sprengt die Blase nicht. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* -------------------------------------------------------- Fuss der Blase */

.ch-blase-fuss {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.ch-blase.ist-fremd .ch-blase-fuss { justify-content: flex-start; }

.ch-zeit { font-variant-numeric: tabular-nums; }

/* Der Zustand ist eine Auskunft an den Absender über SEINE Nachricht – nicht
   über den Empfänger. Darum steht er nur an eigenen Blasen, nur als Wort
   (chat.js setzt den Text), und nirgends als Zahl.

   Wichtig für die Wahrheit der Anzeige: "gesendet" darf sich nie auf das
   Live-Ereignis stützen – publish() kehrt stumm zurück, wenn niemand
   verbunden ist (src/live.js:27), und meldet nie, ob etwas angekommen ist.
   Massgeblich ist allein die Antwort des Servers. */
.ch-zustand { white-space: nowrap; }
.ch-zustand.ist-gelesen { color: var(--accent-fg); font-weight: 600; }
.ch-zustand.ist-fehler { color: var(--critical-ink); font-weight: 600; }

/* Wartet: liegt in der Warteschlange, weil das Netz fehlt. Gestrichelt, weil
   es dieselbe Aussage ist wie überall im Haus – noch ohne Beleg. Kein Rot:
   Offline im Untergeschoss ist ein Zustand, kein Fehler (rundgang.css:300). */
.ch-zustand.ist-wartet {
  padding: 0 6px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
}

/* ----------------------------------------------------------------- Bilder */

/* Auch hier die gestrichelte Kante: ein Foto sieht mehr nach Beweis aus als
   jeder Satz. Der Rahmen liegt UM das Bild, nicht darauf – so bleibt das
   Bild unangetastet und trotzdem erkennbar eingeordnet.

   Der zweite Grund steht im Server: ein Chatbild bekommt bewusst kein
   record() und geht in keine Prüfsumme ein, während ein Rapport-Anhang mit
   [original, size, sha256, user_id] in den Hash wandert (src/audit.js:44).
   Genau dieser Unterschied ist hier gezeichnet. */
.ch-bild {
  margin: 2px 0 0;
  padding: 3px;
  border: 1px dashed var(--border-strong);
  border-radius: 11px;
  background: var(--surface-1);
  max-width: 100%;
}

.ch-bild img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: cover;
  border-radius: 8px;
  background: var(--surface-sunken);
  cursor: zoom-in;
}

.ch-bild figcaption {
  margin-top: 4px;
  padding: 0 4px 2px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* Im Untergeschoss lädt kein Bild. Der Service Worker hält nur die Schale
   vor und lässt /api/ unberührt (public/sw.js:79) – das ist richtig so, muss
   aber ehrlich dastehen statt als ewiger Ladekreis. */
.ch-bild.ist-offline img { opacity: .35; }
.ch-bild.ist-offline figcaption { color: var(--text-secondary); }

/* ------------------------------------------------- Weg in den Rapport */

/* Das Gegenstück zur Warnung, und der Grund, warum die Warnung keine
   Sackgasse ist: aus dem Zuruf wird mit einem Tipp ein Rapport, Text
   übernommen. Der Weg liegt an der einzelnen Nachricht, weil dort steht,
   was übernommen wird.

   Er erscheint erst an der geöffneten Blase – ein Knopf unter JEDER Zeile
   wäre ein Wald, in dem man den einen nicht mehr sieht. */
.ch-wege {
  display: none;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.ch-blase.ist-offen .ch-wege { display: flex; }
.ch-blase.ist-eigen .ch-wege { border-top-color: color-mix(in srgb, var(--accent) 30%, transparent); }

/* Fehlt der Rückruf aus app.js, wird der Knopf sichtbar gesperrt und trägt
   ein title – nicht weggelassen. rundgang.js:1459-1465 begründet es
   wörtlich: "Ein Knopf ohne Wirkung ist die schlimmste Form von erledigt."
   Das Aussehen von [disabled] kommt aus styles.css:245. */
.ch-weg {
  min-height: 44px;
  flex: 1;
  min-width: 170px;
  justify-content: center;
}

/* ========================================================================== */
/* Eingabe                                                                    */
/* ========================================================================== */

.ch-eingabe {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  background: var(--surface-1);
}

/* Das Feld wächst mit dem Text, aber nur bis 140 px – danach rollt es innen.
   Ein Feld, das den halben Bildschirm einnimmt, verdeckt genau das, worauf
   man antwortet. Die Höhe stellt chat.js, hier steht nur die Grenze. */
.ch-feld {
  flex: 1;
  min-width: 0;
  /* Übersteuert styles.css:255 (textarea min-height: 92px) – eine Zeile
     Zuruf braucht kein Formularfeld in Postkartengrösse. */
  min-height: 44px;
  max-height: 140px;
  padding: 11px 12px;
  border-radius: 9px;
  /* 1rem = 16 px. Alles darunter löst auf dem iPhone beim Antippen einen
     Zoom aus, aus dem die Seite nicht mehr zurückfindet. */
  font-size: var(--fs-lg);
  line-height: 1.4;
  /* Der Griff unten rechts liegt genau dort, wo der Daumen den Senden-Knopf
     sucht. Die Höhe regelt ohnehin der Inhalt. */
  resize: none;
}

/* ========================================================================== */
/* Zeichen einfügen                                                          */
/* ========================================================================== */

/* Der Knopf steht LINKS vom Schreibfeld, der Senden-Knopf rechts. Die beiden
   dürfen nie nebeneinander liegen: Wer nachts mit Handschuhen tippt, trifft
   sonst beim Greifen nach einem Zeichen das Absenden. */
.ch-smiley {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 9px;
  background: var(--surface-2);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--dur-schnell) var(--easing), color var(--dur-schnell) var(--easing);
}
.ch-smiley:hover { background: var(--surface-sunken); color: var(--text-primary); }
.ch-smiley:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 2px; }
.ch-smiley[aria-expanded="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-fg);
}

/* Die Tafel hängt an der Eingabezeile und klappt nach OBEN auf – nach unten
   wäre auf dem Handy die Tastatur. Sie ist absolut gesetzt, damit sie die
   Eingabezeile nicht auseinanderschiebt. */
.ch-eingabe { position: relative; }

.ch-smileys {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: calc(100% + 8px);
  z-index: 30;
  /* Höchstens 40 % der Fensterhöhe: Eine Tafel, die den halben Verlauf
     verdeckt, nimmt einem den Zusammenhang, in dem man gerade antwortet. */
  max-height: 40vh;
  overflow-y: auto;
  padding: 10px 12px 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
  box-shadow: var(--shadow);
}

.ch-smiley-gruppe + .ch-smiley-gruppe { margin-top: 10px; }
.ch-smiley-titel {
  margin: 0 0 6px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Raster statt freiem Umbruch. Mit flex-wrap blieb je Gruppe genau ein
   Zeichen allein in der zweiten Zeile stehen ("OK", dann die Kamera) – das
   sieht nach Versehen aus. Das Raster verteilt gleichmässig, egal wie breit
   das Fenster ist, und die Gruppen stehen untereinander in derselben
   Spaltenzahl. */
.ch-smiley-reihe {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 4px;
  justify-items: center;
}

/* 40px: über der Mindestfläche für einen Finger, und zehn davon passen auf
   die schmalste Breite, mit der wir rechnen. */
.ch-smiley-zeichen {
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 9px;
  background: none;
  cursor: pointer;
  /* Die Zeichen kommen aus der Schrift des Geräts – kein Nachladen, und sie
     sehen aus wie überall sonst auf demselben Telefon. */
  font-size: 22px;
  line-height: 1;
  transition: background var(--dur-schnell) var(--easing);
}
.ch-smiley-zeichen:hover { background: var(--surface-sunken); }
.ch-smiley-zeichen:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 1px; }

@media (prefers-reduced-motion: reduce) {
  .ch-smiley,
  .ch-smiley-zeichen { transition: none; }
}

.ch-anhang,
.ch-senden {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 9px;
  cursor: pointer;
}

.ch-anhang {
  /* Bezugspunkt für das unsichtbare Dateifeld weiter unten – ohne ihn misst
     es sich am nächsten positionierten Vorfahren, und der liegt ausserhalb
     dieser Ansicht. */
  position: relative;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text-secondary);
  transition: background var(--dur-schnell) var(--easing);
}
.ch-anhang:hover { background: var(--surface-sunken); color: var(--text-primary); }
.ch-anhang svg { width: 19px; height: 19px; }
/* Das Dateifeld selbst ist unsichtbar, aber vorlesbar – die Beschriftung
   trägt das Label darum (styles.css:134-137). */
.ch-anhang input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.ch-senden svg { width: 19px; height: 19px; }

/* Senden bleibt gesperrt, solange nichts dasteht – sichtbar gesperrt, nicht
   versteckt. Ein Knopf, der verschwindet und wiederkommt, lässt die Leiste
   springen und den Daumen ins Leere greifen. */
.ch-senden[disabled] { cursor: not-allowed; }

/* Die vierte und letzte Stelle, an der der Satz steht: direkt unter dem Feld,
   in dem gerade getippt wird. Wer hier ein Vorkommnis hineinschreibt, hat den
   Hinweis buchstäblich unter den Fingern. */
.ch-fusszeile {
  flex: none;
  margin: 0;
  padding: 0 12px 10px;
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--text-muted);
  background: var(--surface-1);
  border-radius: 0 0 var(--radius) var(--radius);
}

/* Die Vorschau eines gewählten Bildes, bevor es hinausgeht. Sie steht über
   der Eingabe, weil dort noch etwas zu entscheiden ist. */
.ch-vorschau {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-md);
  color: var(--text-secondary);
}
.ch-vorschau img {
  width: 44px;
  height: 44px;
  flex: none;
  object-fit: cover;
  border-radius: 8px;
  border: 1px dashed var(--border-strong);
}
.ch-vorschau-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-vorschau-weg { flex: none; min-height: 44px; min-width: 44px; justify-content: center; }

/* ========================================================================== */
/* Neues Gespräch: Empfängerliste                                           */
/* ========================================================================== */

/* Kein Dialog, sondern eine Bühne an der Stelle des Gesprächs – dieselbe
   Entscheidung wie im Rundgang (rundgang.css:14-17). Ein Dialog auf dem Handy
   ist ein Bildschirm, den man mit dem Handballen wieder schliesst.

   Die Liste zeigt Name und Benutzername, sonst nichts. Keine Rolle, keine
   E-Mail, keine letzte Anmeldung – die Zurückhaltung gehört nicht in die
   Namensliste, sondern in das, was daneben NICHT steht. */
.ch-wahl {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ch-wahl-gruppe {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: 8px 14px;
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ========================================================================== */
/* Meldungen, Leere, Warteschlange                                            */
/* ========================================================================== */

/* Jeder Ladefehler landet als Text IM Behälter, mit einem Knopf daneben –
   kein Dialog, kein toter Bildschirm (Vorbild rundgang.js:748-754). */
.ch-meldung {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 48px 24px;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-lg);
  line-height: 1.55;
}
.ch-meldung .btn { min-height: 44px; }
.ch-meldung svg { width: 44px; height: 44px; opacity: .4; }

/* Kein Gespräch gewählt. Auf dem Schreibtisch ist das der Normalzustand
   beim Öffnen und keine Störung – also keine Farbe und kein Ausrufezeichen. */
.ch-leer {
  flex: 1;
  display: grid;
  place-items: center;
  padding: 24px;
  margin: 0;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-lg);
  line-height: 1.55;
}

/* Was noch nicht hinausgegangen ist. Ruhig, aber dauerhaft sichtbar, solange
   etwas liegt: Eine Nachricht, die im Keller getippt wurde, darf weder
   verschwinden noch doppelt ankommen – und der Absender muss wissen, dass sie
   noch nicht weg ist. */
.ch-warteschlange {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 8px 12px;
  border-top: 1px dashed var(--border-strong);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.ch-warteschlange .btn { margin-left: auto; min-height: 44px; }

/* ========================================================================== */
/* Ein Bereich pro Bildschirm                                                 */
/* ========================================================================== */

/* Die Schwelle ist 1080 und NICHT 860, obwohl 860 sonst die Grenze des Hauses
   ist (styles.css:393). Der Grund ist eine Rechnung, keine Vorliebe:

   Bei 861 px steht die Seitenleiste noch links und nimmt 250 px, dazu kommt
   der Innenabstand von main (styles.css:223). Für den Inhalt bleiben rund
   560 px – abzüglich der 320 px Chatliste bleiben dem Gespräch etwa 240 px.
   Darin bricht jeder zweite Satz dreimal um, und die Sprechblase steht in
   einer Spalte, die schmaler ist als die Liste daneben. Zwei halbe Spalten
   sind schlechter als eine ganze.

   Ab 1080 px – dieselbe Zahl, mit der styles.css:630 rechnet – gehen sich
   Liste und Gespräch nebeneinander aus.

   Welche Seite sichtbar ist, steuert chat.js über .ist-gespraech an der
   Wurzel. Der Zustand lebt am Zustandsobjekt des Moduls, nicht im DOM –
   sonst weiss nach einem Neuzeichnen niemand mehr, wo man war. */
@media screen and (max-width: 1079px) {
  .ch-flaechen { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .ch-gespraech { display: none; }
  .ch.ist-gespraech .ch-liste { display: none; }
  .ch.ist-gespraech .ch-gespraech { display: flex; }

  /* Erst jetzt hat der Knopf ein Ziel: Solange beide Spalten nebeneinander
     stehen, gibt es kein "zurück". */
  .ch-zurueck { display: grid; }
}

/* ========================================================================== */
/* Handy                                                                      */
/* ========================================================================== */

@media screen and (max-width: 860px) {
  /* Wieviel unten belegt ist, weiss nur navigation.css – seit es die Leiste
     mit Licht und Notruf gibt, sind es nicht mehr 86 px. Mit der festen Zahl
     rutschten Eingabezeile und Fusszeile hinter die Leiste. Die 16 px sind
     der Abstand nach oben, der bleibt. */
  .ch {
    gap: 10px;
    height: calc(100dvh - 16px - var(--rand-unten));
  }

  .ch-hinweis {
    padding: 10px 12px 10px 18px;
    font-size: var(--fs-sm);
    flex-wrap: wrap;
  }
  .ch-hinweis-weg { margin-left: 0; width: 100%; }

  /* Auf schmalem Bildschirm zählt jeder Millimeter Breite – und die
     Gegenseite muss sichtbar bleiben, sonst sieht man nicht mehr, wessen
     Blase man liest. */
  .ch-blase { max-width: 86%; }
  .ch-verlauf { padding: 12px 10px; }
  .ch-weg { min-width: 130px; }

  /* Der Kopf hat auf 375 px vier Bewerber um dieselbe Zeile: Zurück, Avatar,
     Name, Marke. Gemessen bleiben dem Namen dann 50 px – "Team ZH37" stand
     als "Tea...". Der Avatar weicht, weil er als Einziger nichts sagt, was
     nicht daneben steht: Ein Kürzel neben dem ausgeschriebenen Namen ist
     Schmuck, und Schmuck verliert gegen die Angabe, die man im Vorbeigehen
     lesen muss. */
  .ch-kopf .ch-avatar { display: none; }
}

/* ========================================================================== */
/* Was auf dem Handy sonst noch über dem Chat schwebt                         */
/* ========================================================================== */

/* Zwei schwebende Knöpfe liegen auf dem Handy auf derselben Linie, 76 px über
   dem unteren Rand: rechts der runde Knopf für einen neuen Rapport (.fab,
   styles.css:490-496), links die Hilfe (hilfe.css:389-394). Das Chatfenster
   endet 86 px über dem Rand – beide liegen also GENAU auf der Eingabezeile.
   Gemessen auf 375x812: der Anhang-Knopf links und die Fusszeile darunter
   verschwinden zur Hälfte darunter.

   Beide sind an ihrem Platz richtig. Sie treffen sich nur auf dieser einen
   Ansicht – dieselbe Lage, die livemap.css:1131-1142 schon einmal hatte, und
   darum auch dieselbe Machart: :has() statt einer Klasse am body, damit
   chat.js dafür nichts anfassen muss und die Regel genau dann gilt, wenn
   diese Ansicht offen ist. */
@media screen and (max-width: 860px) {

  /* Der Rapport-Knopf WEICHT NICHT AUS, er geht weg. Das ist die einzige
     Stelle dieser Datei, an der etwas verschwindet statt sich zu verschieben,
     und der Grund ist der Satz ganz oben: Ein unbeschrifteter violetter Kreis,
     der einen Rapport anlegt und dabei auf dem Satz "das hier ist kein
     Rapport" liegt, ist genau die Verwechslung, die diese Funktion nicht
     erzeugen darf.
     Der Weg zum Rapport geht dadurch nicht verloren – er steht in dieser
     Ansicht zweimal beschriftet da: im Band oben (.ch-hinweis-weg) und an der
     einzelnen Nachricht (.ch-weg). Beide sagen, was sie tun. */
  body:has(#view-chat:not(.hidden)) .fab { display: none; }

  /* Die Hilfe verschwindet NICHT – wer nicht weiterweiss, muss sie überall
     erreichen. Sie rückt auf dieselbe Höhe, auf die sie in der Livemap schon
     rückt (livemap.css:1140). Dieselbe Zahl mit Absicht: Ein Knopf, der je
     nach Ansicht auf einer anderen Höhe steht, wird zweimal gesucht.
     Dort liegt sie über dem unteren linken Rand des Verlaufs – über Text
     also, nicht über einem Bedienelement, und der Verlauf lässt sich rollen. */
  /* Auch hier stand eine feste 150 - dieselbe Zahl, dasselbe Problem.
     Der Wunsch dahinter bleibt richtig und wird in navigation.css erfuellt:
     ein Knopf, der in jeder Ansicht auf derselben Hoehe steht. */
}

/* ========================================================================== */
/* Der Knopf in der Navigation                                                */
/* ========================================================================== */

/* Die Seitenleiste hat Platz; die Fusszeile auf dem Handy hat ihn nicht.
   navigation.css:104-114 hält fest, was passiert, wenn man es trotzdem
   versucht: "Acht Knoepfe passten nicht: 'Rundgang' stand auf 430 px als
   'Rundga'". Übrig geblieben sind sechs Ziele mit vollem Wort – Start,
   Zeiterfassung, Rapport, Rundgang, Livemap, Mehr. Chat wäre der siebte und
   würde die Wörter wieder abschneiden.

   Darum: In der Fusszeile nicht sichtbar, dafür als voller Eintrag unter
   "Weitere Bereiche" (index.html, Karte .nur-handy) – verschoben, nicht
   verschwunden. Auf dem Schreibtisch steht er ganz normal in der Rubrik
   "Dienst".

   Die Regel steht in chat.css und nicht in navigation.css oder styles.css,
   weil diese beiden der anderen Spur gehören (AUSTAUSCH/REVIER.md). Sie ist
   additiv und trifft ausschliesslich den Chat-Knopf.

   WENN SVEN ANDERS ENTSCHEIDET – der Chat soll in die Fusszeile –, dann ist
   das eine ÄNDERUNG VON ZWEI ZEILEN und keine Umgestaltung: diesen Block
   löschen und in navigation.css:112-114 einen anderen Eintrag ergänzen, der
   dafür weicht. Was nicht geht, ist beides: sieben Knöpfe passen nicht. */
@media screen and (max-width: 860px) {
  .navbtn[data-view="chat"] { display: none; }
}

/* Ungelesenes am Knopf in der Seitenleiste. Ein Punkt, keine Zahl: Die Zahl
   am Menü hätten alle den ganzen Tag im Blick und sie erzeugt Druck, ohne
   etwas zu sagen, was der Chat nicht besser sagt. */
.navbtn[data-view="chat"] { position: relative; }
.ch-nav-punkt {
  position: absolute;
  top: 6px;
  left: 24px;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--surface-1);
}

/* ========================================================================== */
/* Weniger Bewegung                                                           */
/* ========================================================================== */

/* styles.css:1579-1596 räumt bereits global auf. Hier steht ausdrücklich,
   was diese Ansicht selbst anstellt – vor allem das weiche Rollen im Verlauf:
   Wer "Bewegung reduzieren" gesetzt hat, will nicht, dass ihm bei jeder neuen
   Nachricht der Bildschirm unter dem Finger wegfährt. */
@media (prefers-reduced-motion: reduce) {
  .ch-verlauf { scroll-behavior: auto; }
  .ch-eintrag,
  .ch-anhang { transition: none; }
}

/* ========================================================================== */
/* Druck                                                                      */
/* ========================================================================== */

/* Ein ausgedruckter Chat ist der gefährlichste Chat: Auf Papier sieht jeder
   Text nach Dokument aus, und niemand sieht mehr, dass er aus einem Fenster
   ohne Prüfkette stammt. Darum bleibt hier ausgerechnet der Hinweis stehen –
   und die gestrichelten Kanten bleiben es auch. Weg kommt nur die Bedienung. */
@media print {
  .ch { height: auto; min-height: 0; display: block; }
  .ch-flaechen { display: block; }
  .ch-liste,
  .ch-zurueck,
  .ch-suche,
  .ch-eingabe,
  .ch-vorschau,
  .ch-wege,
  .ch-warteschlange { display: none !important; }

  .ch-hinweis {
    display: flex !important;
    border: 1px dashed #000;
    color: #000;
    margin-bottom: 12px;
    break-inside: avoid;
  }
  .ch-hinweis::before { background: none; border-left: 6px double #000; }
  .ch-hinweis-weg { display: none !important; }

  .ch-gespraech { border-color: #000; }
  .ch-verlauf { display: block; overflow: visible; height: auto; padding: 0; }
  .ch-marke { border-color: #000; color: #000; }

  /* Links und rechts sind Bildschirmsprache: Auf Papier hat niemand einen
     Bezugspunkt, um zu sehen, welche Seite "meine" ist. Also stehen alle
     Blasen gleich, und wer gesprochen hat, steht als WORT darüber – auch über
     den eigenen, wo der Name auf dem Bildschirm ausgeblendet ist.

     Die drei Selektoren tragen die ist-Klasse mit, obwohl das hier
     umständlich aussieht: .ch-blase allein hat ein Gewicht weniger als
     .ch-blase.ist-eigen und würde von der Bildschirmregel überstimmt. Beim
     ersten Versuch stand die eigene Blase im Ausdruck weiterhin rechts und
     violett – der Grund war genau das. */
  .ch-blase,
  .ch-blase.ist-eigen,
  .ch-blase.ist-fremd {
    max-width: none;
    align-self: stretch;
    border: 1px solid #bbb;
    border-radius: 8px;
    break-inside: avoid;
    margin-bottom: 6px;
  }
  .ch-blase.ist-eigen .ch-blase-von { display: block; }
  .ch-blase.ist-folge { margin-top: 0; }

  /* Der Rahmen legt sich um das Bild, nicht um die Spalte: sonst steht ein
     150 px breites Foto in einem randlos breiten gestrichelten Kasten. */
  .ch-bild { width: fit-content; break-inside: avoid; }
  .ch-bild img { max-height: none; width: 150px; }
}
