/* Mobile zuerst. Die Kommentare markieren die Stellen, die ausschliesslich
   wegen iOS Safari oder Android Chrome so aussehen.

   DIE FARBEN STEHEN NICHT HIER. Sie kommen aus /css/profile.css, das der
   Server aus profile/*.json erzeugt (SPECS.md § 4.16). Dieses Blatt benutzt
   sie nur — jede Farbe ist ein var(--…).

   Wer hier eine Farbe fest hineinschreibt, hebelt damit jedes Profil aus:
   sie bleibt in allen sechs gleich und faellt genau in dem auf, zu dem sie
   nicht passt. tests/public/farben-im-blatt.test.js wacht darueber und
   kennt genau eine Ausnahme — den Block der Medienfarben unten. */

:root {
  --radius: 14px;

  /* DIE EINZIGEN FESTEN FARBEN IM GANZEN BLATT: was auf Video und Fotos
     liegt. Dahinter ist es in JEDEM Profil schwarz — ein geteilter
     Bildschirm wird nicht beschnitten, seine Raender bleiben schwarz
     (.kachel.schirm weiter unten), und ein Foto steht auf dunklem Grund.

     Deshalb duerfen diese fuenf nicht aus dem Profil kommen: die Schrift auf
     einer Videokachel richtete sich sonst nach --text, und das ist in
     Sandstein und Papier fast schwarz — schwarz auf schwarz. Genau dieser
     Fehler steckte hier, bevor die Profile kamen: .vollbildknopf trug
     `color: var(--text)` auf schwarzem Grund und fiel nur nicht auf, weil es
     nur ein dunkles Bild gab. */
  --medien-hg:       #000;      /* hinter Video und Kacheln */
  --medien-flaeche:  #000000b0; /* Knoepfe, die auf dem Bild liegen */
  --medien-marke:    #0009;     /* das Namensschild an der Kachel */
  --auf-medien:      #e9eaec;   /* Schrift und Symbole darauf */
  --medien-schleier: #000d;     /* der Grund der Bildschau */
}

* { box-sizing: border-box; }

/* MUSS vor allen Regeln mit display stehen — und braucht !important.
   [hidden] { display: none } kommt nur aus dem Standard-Stylesheet des
   Browsers; jede eigene display-Regel schlaegt es, weil die Kaskade die
   Herkunft hoeher wertet als die Spezifitaet. Ohne diese Zeile bleibt der
   Anmeldebildschirm nach dem Login stehen. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--hg);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Verhindert, dass iOS beim Drehen die Schrift eigenmaechtig vergroessert */
  -webkit-text-size-adjust: 100%;
}

/* 100dvh statt 100vh: In iOS Safari ist 100vh groesser als der sichtbare
   Bereich, weil die Adressleiste nicht mitgerechnet wird. Mit 100vh
   verschwindet das Eingabefeld unter der Leiste. svh/dvh loesen das. */
/* --app-hoehe setzt chat.js NUR, wenn 100dvh nachweislich nicht mehr stimmt.
   Am 2026-08-22 stand nach einem ERR_NETWORK_CHANGED beim Laden ein zu
   grosser Wert: die Anwendung war hoeher als der sichtbare Bereich, und die
   Eingabeleiste lag darunter — sichtbar blieb nur ihre Oberkante. Drehen
   reparierte es dauerhaft, weil dabei neu gerechnet wurde. */
body { height: var(--app-hoehe, 100dvh); overflow: hidden; }
@supports not (height: 100dvh) { body { height: 100vh; } }

button, input { font: inherit; color: inherit; }

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

.anmeldung {
  height: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.anmeldekarte {
  width: 100%; max-width: 340px;
  background: var(--hg-karte);
  border: 1px solid var(--rand);
  border-radius: var(--radius);
  padding: 24px 20px;
  display: flex; flex-direction: column; gap: 6px;
}
.anmeldekarte h1 { margin: 0 0 14px; font-size: 20px; color: var(--marke, var(--text)); }
.anmeldekarte label { font-size: 13px; color: var(--text-leise); margin-top: 8px; }

/* WICHTIG: mindestens 16px. Bei kleinerer Schrift zoomt iOS Safari beim
   Antippen eines Eingabefelds automatisch in die Seite hinein — und zoomt
   danach nicht wieder heraus. */
.anmeldekarte input, .eingabe textarea {
  font-size: 16px;
  color: var(--text);
  background: var(--hg);
  border: 1px solid var(--rand);
  border-radius: 10px;
  padding: 11px 12px;
  width: 100%;
}
.anmeldekarte input:focus, .eingabe textarea:focus {
  outline: 2px solid var(--akzent); outline-offset: 1px;
}
.eingabe textarea {
  /* Ein textarea erbt weder Schriftart noch Farbe von selbst. Ohne diese
     zwei Zeilen tippt man in Courier und in der Feldfarbe des Browsers. */
  font-family: inherit;
  color: var(--text);
  line-height: 1.35;
  /* Die Hoehe steuert chat.js. Der Anfasser zum Ziehen waere hier nur eine
     Falle: er kaeme mit dem Mitwachsen in Streit. */
  resize: none;
  /* Die Bildlaufleiste erscheint erst, wenn der Deckel erreicht ist — sonst
     zeigt der Browser sie dauerhaft an, obwohl es nichts zu scrollen gibt.
     Umgeschaltet wird sie in chat.js, wo die Hoehe gerechnet wird. */
  overflow-y: hidden;
  max-height: 140px;

  /* DER BODEN. chat.js setzt die Hoehe beim Tippen als Inline-Stil; rechnet
     es sich dabei aus irgendeinem Grund auf 0, verschwindet das Feld und
     uebrig bleibt ein Streifen aus Polsterung. Genau das ist am 2026-08-22
     zweimal passiert — einmal durch ein input-Ereignis im verborgenen
     Zustand, einmal im Zusammenhang mit ERR_NETWORK_CHANGED beim Laden.
     Beide Male habe ich das jeweilige Rennen abgedichtet; das zweite kam
     trotzdem. min-height macht die ganze Fehlerklasse unmoeglich: welchen
     Wert die Rechnung auch liefert, unter eine Zeile kommt das Feld nicht.
     44px ist zugleich die kleinste zuverlaessig treffbare Flaeche. */
  min-height: 44px;
}
.anmeldekarte button {
  margin-top: 18px;
  background: var(--akzent-hg); color: var(--akzent);
  border: 1px solid var(--akzent);
  border-radius: 10px; padding: 12px;
  font-weight: 600; cursor: pointer;
}
.anmeldekarte button:disabled { opacity: .6; cursor: default; }
.fehler { color: var(--fehler); font-size: 13.5px; margin: 12px 0 0; }

/* Ein Satz ueber der Anmeldung, der kein Fehler ist (§ 3.7): „Das Passwort
   wurde geändert" und „Dein Benutzername: anna". Nicht rot — niemand hat
   etwas falsch gemacht. */
.anmeldehinweis {
  margin: 0 0 6px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--text-leise);
}

/* Der Ruecksetzlink in der Verwaltungsmeldung (§ 3.7). Ein Feld statt Text:
   am Handy markiert ein Tippen den ganzen Link, bei Text muesste man ihn mit
   zwei Anfassern einfangen. 16 px aus demselben Grund wie in der
   Anmeldekarte — darunter zoomt iOS Safari beim Antippen in die Seite. */
.linkfeld {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 8px 10px;
  font-size: 16px;
  color: var(--text);
  background: var(--hg);
  border: 1px solid var(--rand);
  border-radius: 6px;
}

/* --- Anwendung ------------------------------------------------------- */

/* position: relative ist der Bezug fuer den Videobereich, wenn waehrend eines
   Gespraechs der Chat davorliegt: er wird dann absolut ueber die ganze
   Anwendung gelegt und unsichtbar gemacht, statt ihn zu entfernen. Warum
   nicht entfernen, steht ganz unten bei der Gespraechsaufteilung. */
.anwendung { position: relative; height: 100%; display: flex; flex-direction: column; }

.kopf {
  /* Anker fuer die Formatierhilfe, die darunter aufklappt */
  position: relative;
  display: flex; align-items: center; gap: 10px;
  background: var(--hg-erhoben);
  border-bottom: 1px solid var(--rand);
  padding: 10px 12px;
  /* Unter dem Notch bzw. der Statusleiste nicht verschwinden */
  padding-top: calc(10px + env(safe-area-inset-top));
}
.kopftitel { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kopftitel strong { font-size: 15px; }
.gedaempft { color: var(--text-leise); font-size: 12px;
             white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Die Anwesenheitszeile ist seit dem 2026-08-31 ein Knopf: sie oeffnet die
   Mitgliederliste. Sie soll aber weiter wie eine Zeile aussehen — ein Rahmen
   behauptet hier ein Gewicht, das sie nicht hat (dieselbe Ueberlegung wie
   bei .leistenlink in der Fusszeile).

   Das Antippziel wird trotzdem gross genug: die Kopfzeile ist eng, deshalb
   holt es sich den Platz ueber padding statt ueber eine Mindesthoehe — die
   wuerde die Kopfzeile auseinanderschieben. */
.anwesenheit {
  background: none; border: none;
  text-align: left; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--text-leise);
  max-width: 100%;

  /* Polster nach aussen, Rand nach innen: die TREFFERFLAECHE wird 44 px hoch,
     die Zeile bleibt im Layout 26 px hoch. Ohne diesen Kniff waere die
     Kopfzeile um 18 px gewachsen — auf einem 420 px hohen Bild (Handy mit
     aufgeklappter Tastatur) ist das der Platz von zwei Nachrichten.
     Bei 12 px Schrift misst die Zeile selbst 18 px (Zeilenhoehe), also
     18 + 2x13 = 44. In Chrome bei 390x420 nachgemessen, nicht gerechnet:
     mit 9 px kamen 36 heraus, weil ich die 26 px der frueheren Fassung fuer
     die Zeilenhoehe hielt — darin steckten schon 2x4 px Polster. */
  padding: 13px 8px;
  margin: -13px -8px;
}
.anwesenheit:hover, .anwesenheit:focus-visible { color: var(--text); }

/* Solange jemand schreibt, steht der Satz da statt der Anwesenheit. In der
   Akzentfarbe, weil es die einzige Zeile hier ist, die sich JETZT gerade
   aendert — die Anwesenheit steht meistens still. */
.anwesenheit.tippt { color: var(--akzent); }

/* --- Wer ist im Kanal (§ 4.1) ---------------------------------------- */

/* Absolut und nicht im Fluss: im Fluss schoebe die Liste beim Oeffnen den
   ganzen Verlauf nach unten, und die Nachricht, wegen der man nachsieht,
   waere weg. Dieselbe Entscheidung wie bei der Formatierhilfe. */
.mitgliederliste {
  position: absolute; z-index: 5;
  top: 100%; left: 8px;
  min-width: 220px; max-width: calc(100% - 16px);
  background: var(--hg-karte);
  border: 1px solid var(--rand); border-radius: var(--radius);
  padding: 10px 12px;
  box-shadow: 0 8px 24px var(--schatten);
}
.mitgliederkopf {
  margin: 0 0 6px; font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-leise);
}
.mitgliederliste ul { list-style: none; margin: 0; padding: 0; }
/* Mit Formular und QR-Code (G3) wird die Liste hoeher als ein Handy: sie
   scrollt in sich, statt unter den Bildschirmrand zu wachsen. */
.mitgliederliste { max-height: min(75dvh, 640px); overflow-y: auto; }

/* Gastgeber im Gaestezimmer (G3). Die Aktionen einer Zeile in einer eigenen
   Zeile darunter — neben Name, Stand und „Nachricht" ist bei 360 px kein
   Platz mehr. */
.mitgliederliste li { flex-wrap: wrap; }
.mitgliedaktionen { flex-basis: 100%; display: flex; gap: 14px; padding-left: 2px; }
.mitgliedaktionen button {
  background: none; border: 0; padding: 4px 0;
  font-size: 12px; color: var(--akzent); cursor: pointer;
}
.zimmerverwaltung { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--rand); }
.zimmerknoepfe { display: flex; gap: 16px; flex-wrap: wrap; }
.kollegenwahl { margin: 6px 0; }
.kollegenwahl button {
  width: 100%; text-align: left; background: none; border: 0;
  padding: 8px 4px; font-size: 14px; color: var(--text); cursor: pointer;
}
.zimmerform { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.zimmerform label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--text-leise); }
.zimmerform input, .zimmerform select {
  font: inherit; font-size: 15px; padding: 7px 9px; color: var(--text);
  background: var(--hg); border: 1px solid var(--rand); border-radius: 8px;
}
.zimmerformtitel { margin: 0; font-size: 13px; color: var(--text); }
/* Mehrere auf einmal (G4): eine Zeile je Person, ab der zweiten mit ✕. */
.gastpersonen { display: flex; flex-direction: column; gap: 6px; }
.gastpersonenkopf { font-size: 12px; color: var(--text-leise); }
#gastzeilen { display: flex; flex-direction: column; gap: 6px; }
.gastzeile { display: flex; gap: 6px; }
.gastzeile input { flex: 1; min-width: 0; }
.gastzeileweg {
  flex: none; width: 36px; border-radius: 8px; cursor: pointer;
  background: none; border: 1px solid var(--rand); color: var(--text-leise);
}
.gastpersonen .leistenlink { align-self: flex-start; }
.zimmerlinks { margin-top: 8px; }
.zimmerlinks .zimmerformtitel { margin-top: 10px; }
#allekopieren { padding: 8px 14px; border-radius: 8px; font-size: 14px; cursor: pointer; }
.zimmerformknoepfe { display: flex; gap: 10px; }
.zimmerformknoepfe button { padding: 8px 14px; border-radius: 8px; font-size: 14px; cursor: pointer; }
.mitgliederliste li {
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 0; font-size: 13px;
}
/* Der Name darf kuerzen, die Zeitangabe nicht: "vor 2 Std" halb abgeschnitten
   ist keine Auskunft mehr. */
.mitgliedername { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; }
.mitgliedstand { flex: none; font-size: 12px; color: var(--text-leise); }
/* Gaestezimmer (G2): die Herkunft als zweite Zeile unter dem Namen. Sie
   kuerzt fuer sich — eine lange Organisation schneidet nicht den Namen ab. */
.mitgliedgast { display: block; overflow: hidden; text-overflow: ellipsis;
                font-size: 12px; color: var(--text-leise); }
/* Wer da ist, faellt auf. Ein Punkt und nicht nur eine Farbe: eine Farbe
   allein ist fuer Rotgruenblinde keine Auskunft. */
.mitgliedstand.online { color: var(--akzent); }
.mitgliedstand.online::before { content: "● "; }

.mitgliederfussnote {
  margin: 8px 0 0; padding-top: 8px;
  border-top: 1px solid var(--rand);
  font-size: 11px; line-height: 1.4; color: var(--text-leise);
  white-space: normal;
}

/* 44px ist die kleinste Flaeche, die sich auf einem Touchscreen zuverlaessig
   treffen laesst — Apples eigene Empfehlung. */
.symbolknopf {
  min-width: 44px; height: 44px;
  background: none; border: none; font-size: 20px;
  cursor: pointer; border-radius: 10px;
}
.symbolknopf:disabled { opacity: .35; cursor: default; }

/* Der Punkt am Menueknopf (Jess, 2026-09-19): „wenn eine Markierung bei den
   Menuestrichen waere, wuesste ich, dass noch woanders was neues ist."

   Punkt und nicht Zahl — Jess: „Muss so oder so auf machen, da spielt die
   Zahl keine Rolle." Die Zahl steht drinnen am Eintrag.

   Gebaut wie der Punkt am Chatknopf weiter unten (.chatpunkt): gleiche
   Groesse, gleiche Farbe, gleicher Trennrand. Der Rand traegt
   --hg-erhoben, weil der Knopf in der Kopfleiste sitzt und die diese Farbe
   hat — ohne ihn verschwimmt der Punkt mit dem Zeichen darunter.

   position:relative muss hier stehen und nicht bei .symbolknopf: der Anker
   gehoert zu DIESEM Knopf, und die anderen Symbolknoepfe sollen davon
   nichts erben. */
#menueknopf { position: relative; }
.menuepunkt { display: none; }
#menueknopf.hat-neues .menuepunkt {
  display: block;
  position: absolute;
  /* 14 px, nicht 10 — Jess am 2026-09-19 an vier Groessen nebeneinander
     gewaehlt: „Farblich passt es super, aber er ist mir zu klein."
     16 px waere noch groesser gegangen, ruecken aber bis an die oberste
     Menuestrichlinie. Der Abstand ist dabei von 6 auf 5 px gegangen, damit
     der groessere Punkt nach aussen waechst statt ins Zeichen hinein. */
  top: 5px; right: 5px;
  width: 14px; height: 14px;
  background: var(--akzent);
  border: 2px solid var(--hg-erhoben);
  border-radius: 50%;
}

.seitenleiste {
  position: fixed; z-index: 3; top: 0; bottom: 0; left: 0;
  /* Geschlossen ausgefahren statt versteckt: so laesst sie sich animieren,
     und auf dem Desktop hebt die Media-Query das einfach auf. */
  transform: translateX(-100%);
  transition: transform .18s ease-out;
  width: 78%; max-width: 300px;
  background: var(--hg-erhoben);
  border-right: 1px solid var(--rand);
  /* Unten mit Rand fuer die Gestenleiste: dort stehen seit dem 2026-09-13
     die zwei Symbole. */
  padding: calc(16px + env(safe-area-inset-top)) 14px calc(12px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
}
.seitenleiste h2 { font-size: 13px; color: var(--text-leise);
                   text-transform: uppercase; letter-spacing: .05em; margin: 0 0 10px; }
.seitenleiste ul { list-style: none; margin: 0; padding: 0; flex: 1; }
.seitenleiste li {
  padding: 11px 10px; border-radius: 10px;
  display: flex; align-items: center; gap: 8px; cursor: pointer;
}
/* Kanalnamen duerfen Leerzeichen und Emoji enthalten — ein langer Name soll
   die Leiste trotzdem nicht auseinanderziehen. */
.seitenleiste li span:first-child {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.seitenleiste li[aria-current="true"] { background: var(--akzent-hg); color: var(--akzent); }
.zaehler {
  margin-left: auto; background: var(--akzent); color: var(--auf-akzent);
  border-radius: 999px; padding: 1px 8px; font-size: 12px; font-weight: 700;
}
.installhinweis {
  font-size: 12px; line-height: 1.45; color: var(--text-leise);
  background: var(--hg-karte); border: 1px solid var(--rand);
  border-radius: 10px; padding: 9px 10px; margin: 12px 0 0;
}
.installhinweis strong { color: var(--text); font-weight: 600; }

/* --- Direktgespraeche (§ 4.17) ---

   Zwei Listen in einer Leiste: nur die Kanalliste darf wachsen, sonst teilten
   sich beide den Platz, und bei drei Kanaelen stuende die Gruppe „Direkt"
   mitten in der Leiste. Die Direktliste haengt direkt darunter. */
.seitenleiste #kanalliste { flex: none; }
/* Alles ausser dem Fuss scrollt (Jess, 2026-09-26). `min-height: 0`, weil ein
   Flex-Kind sonst nie kleiner wird als sein Inhalt — dann gaebe es nichts zu
   scrollen, und die Leiste liefe wie vorher unten aus dem Bild. */
.leistenrumpf {
  flex: 1; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column;
}
.seitenleiste #direktliste, .seitenleiste #direktpersonen { flex: none; }
.seitenleiste .leistengruppe { margin-top: 18px; }

/* Wie ein Kanaleintrag, nur leiser: er bietet etwas an, er zeigt nichts an.
   Die Polsterung haelt das Antippziel bei rund 40 px (§ 4.14). */
.direktknopf {
  align-self: flex-start;
  margin: 2px 0 0; padding: 10px 10px;
  background: none; border: 0; border-radius: 10px;
  font-size: 14px; color: var(--text-leise); cursor: pointer;
}
.direktknopf:hover, .direktknopf[aria-expanded="true"] { color: var(--akzent); }

/* Die Auswahl klappt in der Leiste auf — kein Deckel, am Handy ist die Leiste
   ohnehin gerade offen. Eingerueckt und gerahmt, damit sie nicht aussieht wie
   eine zweite Liste von Gespraechen. */
.direktwahl {
  margin: 4px 0 0; padding: 6px 4px;
  background: var(--hg-karte); border: 1px solid var(--rand); border-radius: 10px;
}
.direktwahltitel { margin: 4px 8px 6px; font-size: 12px; color: var(--text-leise); }
.direktleer { color: var(--text-leise); cursor: default; }

/* „⋯" am Direkteintrag (Ausblenden, 2026-09-13): leise wie der Direktknopf.
   Die negativen Raender geben die Polsterung an den Eintrag zurueck — die
   Flaeche bleibt gross genug fuer den Daumen, der Eintrag wird nicht hoeher.
   12 px und nicht 10: bei 10 mass die Flaeche in der Bildprobe bei 390 px
   36 × 36 — unter den rund 40 px, die hier fuer Antippziele gelten (§ 4.14). */
.direktmehr {
  flex: none; margin: -12px -8px -12px 0; padding: 12px 12px;
  background: none; border: 0; border-radius: 8px;
  font-size: 16px; line-height: 1; color: var(--text-leise); cursor: pointer;
}
.direktmehr:hover, .direktmehr[aria-expanded="true"] { color: var(--akzent); }

/* Das Menue als Zeile unter dem Eintrag, gerahmt wie die Auswahl von
   „+ Direkt" — ungerahmt saehe es aus wie ein weiteres Gespraech. */
.seitenleiste li.direktmenue {
  flex-wrap: wrap; gap: 2px 10px; cursor: default;
  margin: 0 0 6px; padding: 4px 10px 8px;
  background: var(--hg-karte); border: 1px solid var(--rand);
}
.direktmenue button {
  padding: 10px 0; background: none; border: 0;
  font-size: 14px; color: var(--akzent); cursor: pointer;
}
.direktmenue span { font-size: 12px; line-height: 1.4; color: var(--text-leise); }

/* „Nachricht" in der Mitgliederliste: ein kleiner Textknopf neben der
   Zeitangabe. Die Polsterung macht die Flaeche, nicht die Schrift. */
.mitgliednachricht {
  flex: none; padding: 6px 8px; margin: -6px -4px -6px 0;
  background: none; border: 0; border-radius: 8px;
  font-size: 12px; color: var(--akzent); cursor: pointer;
}

/* Klein und gedaempft an unauffaelliger Stelle (§ 12) */
.version { font-size: 11px; color: var(--text-leise); margin: 14px 0 0;
           user-select: all; -webkit-user-select: all; }

/* Ein Textknopf, kein Schaltknopf: er steht zwischen zwei gedaempften Zeilen
   und soll dort nicht schreien. Gross genug zum Treffen bleibt er trotzdem. */
/* Abmelden traegt seit dem 2026-08-29 .leistenlink — denselben Stil wie die
   Verbindungsprobe daneben. Vorher stand hier eine eigene, gleichlautende
   Regel; zwei Kopien desselben Aussehens laufen frueher oder spaeter
   auseinander. Der Stil steht bei der Fusszeile, weiter unten. */

/* Haekchen im Anmeldeformular. Die ganze Zeile ist anklickbar — am Handy ist
   das Kaestchen allein ein zu kleines Ziel. */
.anmeldekarte .haekchen {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 2px;
  font-size: 13px;
  color: var(--text-leise);
  cursor: pointer;
}
.anmeldekarte .haekchen input { width: 18px; height: 18px; }

.anwendung.menue-offen .seitenleiste { transform: none; }

.abdunkeln { position: fixed; z-index: 2; inset: 0; background: var(--schleier); }

@media (prefers-reduced-motion: reduce) {
  .seitenleiste { transition: none; }
}

.verlauf {
  flex: 1; overflow-y: auto;
  /* Traegheitsscrollen auf iOS */
  -webkit-overflow-scrolling: touch;
  padding: 14px 12px; display: flex; flex-direction: column; gap: 8px;
}

.nachricht { max-width: 82%; background: var(--hg-karte);
             border-radius: var(--radius); padding: 8px 11px; }
.nachricht.eigen { align-self: flex-end; background: var(--eigen); }
.nachricht .absender { font-size: 12px; color: var(--akzent); font-weight: 600; }
.nachricht .zeit { font-size: 11px; color: var(--text-leise); margin-left: 6px; }
/* Eine lange URL ohne Leerzeichen sprengt sonst das Layout */
/* pre-wrap, damit Zeilenumbrueche aus dem Eingabefeld erhalten bleiben.
   Ohne das faellt ein mehrzeiliger Codeblock zu einer Zeile zusammen. */
.nachricht .rumpf { overflow-wrap: anywhere; white-space: pre-wrap; }
.nachricht a { color: var(--akzent); }

/* --- Textformatierung (public/js/formatieren.js) --------------------- */

.nachricht .rumpf strong { font-weight: 700; }
.nachricht .rumpf em { font-style: italic; }
.nachricht .rumpf s { opacity: .7; }

.nachricht .rumpf code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em;
  background: var(--aufhell);
  border-radius: 4px;
  padding: .1em .35em;
}

.nachricht .rumpf pre.codeblock {
  margin: .4em 0;
  padding: .55em .7em;
  background: var(--code-hg);
  border: 1px solid var(--rand);
  border-radius: 8px;
  /* Ein langer Befehl soll IM Kasten scrollen. Ohne das schiebt er die
     Nachrichtenblase auf und am Handy die ganze Seite quer. */
  overflow-x: auto;
  white-space: pre;
}
.nachricht .rumpf pre.codeblock code {
  background: none; padding: 0; font-size: .88em;
}

.nachricht .rumpf blockquote {
  margin: .3em 0;
  padding: .1em 0 .1em .6em;
  border-left: 3px solid var(--akzent);
  color: var(--text-leise);
}

/* Die KI antwortet gegliedert: "## Titel" und "---" zwischen den Abschnitten.
   Der Chat kennt keine Ueberschriftgroessen — eine fette Zeile mit etwas Luft
   darueber ist genug Gliederung fuer eine Nachrichtenblase. inline-block,
   weil ein margin-top an einem reinen Inline-Element nichts bewirkt. */
.nachricht .rumpf .ueberschrift {
  display: inline-block;
  margin-top: .55em;
  font-size: 1.02em;
}
.nachricht .rumpf .ueberschrift:first-child { margin-top: 0; }

.nachricht .rumpf hr.trenner {
  margin: .5em 0;
  border: 0;
  border-top: 1px solid var(--rand);
}

/* --- Lesebestaetigung (public/js/gelesen.js) -------------------------- */

/* Nur an eigenen Nachrichten, und erst, wenn wirklich jemand gelesen hat —
   gelesenZeichnen() setzt sonst hidden. Rechtsbuendig unter dem Text:
   margin-left auto, weil die Blase kein Raster hat.
   Ein Knopf, damit die Namen am Handy antippbar sind; er soll aber nicht wie
   einer aussehen. */
.nachricht .gelesen {
  display: block;
  margin: 3px 0 -1px auto;
  padding: 2px 0 0 8px;
  background: none;
  border: 0;
  font: inherit;
  font-size: 11px;
  line-height: 1;
  letter-spacing: .5px;
  color: var(--text-leise);
  cursor: pointer;
}

/* Haben ALLE gelesen, bekommt der Haken die Akzentfarbe. Das ist der einzige
   Zustand, den man aus dem Augenwinkel erkennen soll. */
.nachricht .gelesen.alle { color: var(--akzent); }

/* --- Bearbeiten und Loeschen (Wunsch von Jess, 2026-09-11) ----------- */

/* Die Kopfzeile traegt seit dem 2026-09-11 eine Klasse und ist ein Flex-
   Kasten: nur so laesst sich der ⋯-Knopf an den rechten Rand schieben, ohne
   ihn absolut zu legen. Sie heisst `nachrichtkopf` und nicht `kopf`, weil
   die Seitenkopfzeile diesen Namen schon traegt. `baseline`, damit Name, Uhrzeit und Vermerk auf einer
   Schriftlinie sitzen — bei `center` tanzt die kleinere Schrift. */
.nachricht .nachrichtkopf { display: flex; align-items: baseline; }

.nachricht .bearbeitet {
  font-size: 11px;
  color: var(--text-leise);
  font-style: italic;
  margin-left: 6px;
}

/* Der ⋯-Knopf. Er sitzt IN der Kopfzeile und nicht ueber der Nachricht:
   schwebend ueber dem Text verdeckte er am Handy genau das, was man lesen
   will, und beim Scrollen liefe er der Zeile hinterher.

   44 px hoch waere hier falsch — das ist das Mass fuer eine Kopfzeile, die
   einmal im Bild steht, nicht fuer einen Knopf, den es fuenfzig Mal gibt.
   Bei 34 px bleibt er sicher zu treffen und sprengt die Zeile nicht. */
.nachricht .eingriff {
  margin-left: auto;
  min-width: 34px;
  min-height: 34px;
  padding: 0 6px;
  background: none;
  border: 0;
  font: inherit;
  font-size: 16px;
  line-height: 1;
  color: var(--text-leise);
  cursor: pointer;
  border-radius: 8px;
}
.nachricht .eingriff:hover,
.nachricht .eingriff[aria-expanded="true"] {
  background: var(--hg-erhoben);
  color: var(--text);
}

.nachricht .eingriffleiste {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--rand);
}

.nachricht .eingriffaktion {
  min-height: 34px;
  padding: 0 12px;
  background: var(--hg-erhoben);
  border: 1px solid var(--rand);
  border-radius: 8px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}
.nachricht .eingriffaktion:hover { border-color: var(--akzent); }
/* Loeschen ist der einzige Weg hier, der nichts rueckgaengig macht. */
.nachricht .eingriffaktion.warnung { color: var(--fehler); border-color: var(--warn-rand); }
.nachricht .eingriffaktion.bestaetigen { border-color: var(--akzent); color: var(--akzent); }

.nachricht .eingriff-nichts { font-size: 12px; color: var(--text-leise); }

/* Das Feld beim Bearbeiten. Es steht an der Stelle des Rumpfes und ist so
   breit wie die Nachricht — schmaler waere der Text beim Aendern anders
   umbrochen als danach, und man korrigierte an der falschen Stelle. */
.nachricht .bearbeitung { margin-top: 4px; }

.nachricht .bearbeitungsfeld {
  width: 100%;
  box-sizing: border-box;
  min-height: 34px;
  max-height: 200px;
  padding: 6px 8px;
  background: var(--hg);
  color: var(--text);
  border: 1px solid var(--akzent);
  border-radius: 8px;
  font: inherit;
  resize: none;
}

.nachricht .bearbeitungsknoepfe { display: flex; gap: 6px; margin-top: 6px; }

/* Die geloeschte Nachricht behaelt ihren Platz. Gedaempft, damit das Auge
   beim Lesen darueber hinweggeht — aber sichtbar, damit der Bezug bleibt. */
.nachricht.geloescht { opacity: .75; }
.nachricht.geloescht .rumpf .nachricht-weg {
  display: inline-block;
  padding: 2px 0;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--text-leise);
}

.nachricht.system {
  align-self: center; max-width: 90%; background: none;
  color: var(--text-leise); font-size: 12.5px; text-align: center; padding: 2px;
}

/* Der Hinweis auf die Loeschfrist, ganz oben im Kanal (§ 3.6). Leise wie eine
   Systemzeile, aber mit einer Linie darunter: er steht nicht IM Gespraech,
   sondern ueber dessen Anfang. */
.verlauf-frist {
  align-self: center; max-width: 90%;
  margin: 2px 0 8px; padding: 4px 12px 8px;
  color: var(--text-leise); font-size: 12.5px; text-align: center;
  border-bottom: 1px solid var(--rand);
}

/* Der Tag ueber seiner ersten Nachricht (verlaufansicht.js, tageTrennen).
   Anders als eine Systemzeile: die ist ein Ereignis IM Gespraech, der Tag
   ist eine Gliederung. Deshalb die Linien zu beiden Seiten und das Schild
   in der Mitte — und etwas mehr Luft darueber als darunter, damit er zum
   Folgenden gehoert. */
.tag-trenner {
  display: flex; align-items: center; gap: 10px;
  margin: 10px 0 2px;
  color: var(--text-leise); font-size: 12px; font-weight: 600;
}
.tag-trenner::before, .tag-trenner::after {
  content: ''; flex: 1; border-top: 1px solid var(--rand);
}
.tag-trenner span {
  padding: 2px 10px; border-radius: 999px;
  background: var(--hg-erhoben); border: 1px solid var(--rand);
  white-space: nowrap;
}

.eingabe {
  display: flex; gap: 8px; padding: 10px 12px;
  /* Waechst das Feld, bleibt der Senden-Knopf unten buendig statt in der
     Mitte zu schweben. */
  align-items: flex-end;
  background: var(--hg-erhoben); border-top: 1px solid var(--rand);
  /* Home-Indicator-Bereich auf dem iPhone freihalten */
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
}
.eingabe button {
  min-width: 48px; border-radius: 10px;
  background: var(--akzent-hg); color: var(--akzent);
  border: 1px solid var(--akzent); cursor: pointer; font-size: 18px;
}
/* Die Formatierhilfe haengt als Klappliste an der Kopfzeile und steht
   ABSOLUT — sie nimmt keinen Platz im Fluss ein. Im Fluss haette sie im
   Rasterlayout ab 760 px eine eigene Zeile beansprucht und die Aufteilung
   verschoben; als Ueberlagerung kann sie in keinem Layout etwas kaputt
   machen. */
/* Klappt seit dem 2026-09-13 nach OBEN ueber den Symbolen unten in der
   Leiste auf (vorher nach unten aus der Kopfzeile). Links buendig: am
   breiten Bildschirm ragt sie ueber die 260 px der Leiste in den Verlauf,
   am Handy bleibt sie in den 360 px. */
.formathilfe {
  position: absolute; z-index: 5;
  bottom: calc(100% + 6px); left: 0;
  max-width: min(320px, calc(100vw - 24px));
  background: var(--hg-erhoben);
  border: 1px solid var(--rand);
  border-radius: 12px;
  box-shadow: 0 8px 24px var(--schatten);
  padding: 10px 12px 2px;
  font-size: 13px;
}
#formathilfeknopf[aria-expanded="true"] {
  background: var(--akzent-hg); color: var(--akzent);
}

/* --- Suche im Verlauf (§ 4.18) ---

   Ein Deckel unter der Kopfzeile, ueber die ganze Breite: am Handy braucht
   die Trefferliste jeden Pixel, und ein schmales Klappmenue wie die
   Formathilfe schnitte die Saetze nach drei Woertern ab. */
#suchknopf[aria-expanded="true"] {
  background: var(--akzent-hg); color: var(--akzent);
}
.suche {
  position: absolute; z-index: 6;
  top: 100%; left: 0; right: 0;
  max-height: min(70vh, 560px); overflow-y: auto;
  background: var(--hg-erhoben);
  border-bottom: 1px solid var(--rand);
  box-shadow: 0 8px 24px var(--schatten);
  padding: 6px 10px 10px;
}
/* Am breiten Bildschirm ein Kasten unter der Lupe statt eines Streifens: bei
   Jess' 3440 px lief das Suchfeld sonst ueber den ganzen Monitor, und die
   Treffer standen am anderen Ende (Bildprobe vom 2026-09-13). */
@media (min-width: 760px) {
  .suche {
    left: auto; right: 10px;
    width: min(720px, calc(100% - 20px));
    border: 1px solid var(--rand); border-radius: 12px;
    top: calc(100% + 6px);
  }
}
/* Das Feld bleibt oben stehen, wenn die Liste darunter scrollt. */
.suchform {
  position: sticky; top: -6px; z-index: 1;
  display: flex; align-items: center; gap: 6px;
  background: var(--hg-erhoben); padding-top: 6px;
}
/* 16 px und nicht 14: darunter vergroessert iOS die Seite beim Antippen. */
.suchfeld {
  flex: 1; min-width: 0;
  font: inherit; font-size: 16px;
  padding: 9px 11px; border-radius: 10px;
  border: 1px solid var(--rand);
  background: var(--hg-karte); color: var(--text);
}
.suchstand { margin: 8px 4px 4px; font-size: 12px; color: var(--text-leise); }
.suchliste { list-style: none; margin: 0; padding: 0; }
.suchtreffer {
  display: block; width: 100%;
  text-align: left; cursor: pointer;
  background: none; border: 0; border-top: 1px solid var(--rand);
  border-radius: 0;
  padding: 10px 6px;
  font: inherit; color: var(--text);
}
.suchtreffer:hover, .suchtreffer:focus-visible { background: var(--akzent-hg); }
.suchtrefferkopf { display: block; margin-bottom: 3px; font-size: 12px; color: var(--text-leise); }
.suchtreffertext { display: block; font-size: 14px; line-height: 1.4; overflow-wrap: anywhere; }
.suchtrefferdatei { color: var(--text-leise); }
.suchtreffertext mark {
  background: var(--akzent-hg); color: var(--akzent);
  border-radius: 3px; padding: 0 1px;
}

/* Die Zeile, an die die Suche gesprungen ist: ein Rahmen, der vergeht. Als
   Schatten und nicht als Hintergrund — die Blase behaelt ihre Farbe, und
   eigene und fremde Nachrichten sehen beim Aufleuchten gleich aus. */
.nachricht.aufgeleuchtet { animation: aufleuchten 2.4s ease-out; }
@keyframes aufleuchten {
  0%, 40% { box-shadow: 0 0 0 3px var(--akzent); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
.formathilfe table { border-collapse: collapse; }
.formathilfe td { padding: 2px 14px 2px 0; vertical-align: baseline; }
.formathilfe code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em;
  background: var(--aufhell);
  border-radius: 4px; padding: .1em .35em;
}
/* .gedaempft kuerzt sonst mit Auslassungspunkten (nowrap) — im Kasten soll
   der Satz umbrechen statt zu verschwinden. */
.formathilfe p { margin: 8px 0; white-space: normal; }

.verbindungshinweis {
  background: var(--hinweis-hg); color: var(--hinweis-text);
  text-align: center; font-size: 13px; padding: 7px;
}

/* Die Gastfrist im Zimmer (G7). Dieselben Farben wie der Hinweis darueber:
   beides sagt „hier ist etwas, das gleich nicht mehr geht". Der Knopf bricht
   bei 360 px unter den Satz statt ihn abzuschneiden. */
.gastfristleiste {
  background: var(--hinweis-hg); color: var(--hinweis-text);
  font-size: 13px; padding: 5px 12px;
}
.gastfristleiste p {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 4px 10px; margin: 2px 0; text-align: center;
}
.gastfristleiste button {
  font: inherit; color: inherit; background: none; cursor: pointer;
  border: 1px solid currentColor; border-radius: 4px; padding: 2px 8px;
}
.laeuftmit { font-size: 11px; color: var(--text-leise); text-align: center; margin-top: 14px; }

/* --- Video-Raum ------------------------------------------------------ */

.videobereich {
  display: flex; flex-direction: column;
  background: var(--video-hg); border-bottom: 1px solid var(--rand);
  /* Fester Anteil der Fensterhoehe, nicht die Hoehe des Inhalts: die Kacheln
     leiten ihre Groesse aus dem Bereich ab, der Bereich darf sie also nicht
     seinerseits aus den Kacheln ableiten — sonst faellt er auf ein paar
     Dutzend Pixel zusammen und jedes Bild wird zum Streifen.
     Der Chat bleibt darunter sichtbar: auf dem Handy ist das der einzige Weg,
     waehrend eines Gespraechs etwas mitzuschreiben. */
  flex: 0 0 46dvh; min-height: 0;
}
@supports not (height: 1dvh) { .videobereich { flex-basis: 46vh; } }
/* Teilt jemand seinen Bildschirm, braucht er die Flaeche */
.anwendung.teilt .videobereich { flex-basis: 62dvh; }

.kacheln {
  flex: 1; min-height: 0;
  display: grid; gap: 6px; padding: 6px;
  /* Spaltenzahl und Kachelmass rechnet layout.js aus und setzt sie hier als
     Inline-Stil. Diese Werte sind die Rueckfallebene, falls das Skript nicht
     laeuft — untereinander, das funktioniert in jeder Fensterform. */
  grid-auto-flow: row; grid-auto-rows: 1fr;
  justify-items: center; align-items: center;
  justify-content: center; align-content: center;
}

/* Sprecheransicht: einer gross, die uebrigen als Streifen darunter (§ 4.3) */
.kacheln.sprecher {
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: 1fr 24%;
  grid-auto-rows: 24%;
}
.kacheln.sprecher .kachel { grid-row: 2; }
.kacheln.sprecher .kachel.gross { grid-row: 1; grid-column: 1 / -1; }

.vollbildknopf {
  position: absolute; top: 6px; right: 6px;
  min-width: 40px; min-height: 40px;
  display: none; align-items: center; justify-content: center;
  border: none; border-radius: 10px;
  background: var(--medien-flaeche); color: var(--auf-medien); cursor: pointer;
}
.vollbildknopf svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
/* Im Raster hat ihn jede Kachel: dort sind alle gleich wichtig, und ein Knopf,
   der mit dem lautesten Sprecher von Kachel zu Kachel springt, waere nicht zu
   treffen. In der Sprecheransicht nur auf der grossen und auf geteilten
   Bildschirmen — auf den Streifenkacheln waere er groesser als ihr Inhalt. */
.kacheln:not(.sprecher) .vollbildknopf,
.kacheln.sprecher .kachel.gross .vollbildknopf,
.kachel.schirm .vollbildknopf { display: inline-flex; }

/* Der Umschalter fuer die Kamera — gegenueber vom Vollbildknopf, auf der
   eigenen Kachel. Nur da, wo es eine zweite Kamera gibt: `.kamerawahl` setzt
   chat.js, nachdem rtc.js sie gezaehlt hat (Jess' Wunsch, 2026-09-04). */
.kamerawechselknopf {
  position: absolute; right: 6px; bottom: 6px;
  min-width: 40px; min-height: 40px;
  display: none; align-items: center; justify-content: center;
  border: none; border-radius: 10px;
  background: var(--medien-flaeche); color: var(--auf-medien); cursor: pointer;
}
.kamerawechselknopf svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.kamerawechselknopf[disabled] { opacity: .5; }
.kacheln.kamerawahl [data-wer="eigen"] .kamerawechselknopf { display: inline-flex; }

/* Auf sehr kleinen Kacheln wuerden sie das Bild verdecken */
.kachel.klein .vollbildknopf,
.kachel.klein .kamerawechselknopf { display: none; }

/* … ausser auf der Kachel, die gerade die ganze Flaeche einnimmt. Dort ist
   der Vollbildknopf der EINZIGE Ausweg: die uebrige Oberflaeche liegt
   darunter auf `visibility: hidden`, eine Browserleiste gibt es in der
   installierten App nicht. Fuer `:fullscreen` steht diese Ausnahme seit dem
   2026-08-21 weiter unten — fuer die Vollflaeche fehlte sie, und genau die
   greift in der installierten App.

   Weg genommen hat ihn danach jede der beiden Regeln darueber: `.klein` bei
   vielen Teilnehmern, und in der Sprecheransicht der Wechsel der grossen
   Kachel — der laeuft alle 400 ms mit der Pegelmessung. Jess, 2026-09-04:
   „kann ich es waehrend des Calls nicht mehr klein machen." */
.kachel.vollflaeche .vollbildknopf { display: inline-flex; }
.kacheln.kamerawahl [data-wer="eigen"].vollflaeche .kamerawechselknopf { display: inline-flex; }

/* Im Vollbild gilt das Seitenverhaeltnis der Kachel nicht mehr — sonst bliebe
   der groesste Teil des Bildschirms schwarz. */
.kachel:fullscreen {
  aspect-ratio: auto;
  width: 100%; height: 100%; max-width: none; max-height: none;
  border-radius: 0;
}
.kachel:fullscreen video { object-fit: contain; }
.kachel:fullscreen .vollbildknopf { display: inline-flex; }
.kacheln.kamerawahl [data-wer="eigen"]:fullscreen .kamerawechselknopf { display: inline-flex; }

/* Ein geteilter Bildschirm wird NIE beschnitten — bei Text waere genau die
   Zeile weg, auf die es ankommt. Schwarze Raender sind das kleinere Uebel. */
.kachel.schirm video { object-fit: contain; background: var(--medien-hg); }
.kachel.schirm .name { background: var(--akzent-hg); color: var(--akzent); }

/* Angetippte Kachel bleibt gross, bis das Gespraech endet (§ 4.3) */
.kachel.gepinnt { outline: 2px solid var(--akzent); outline-offset: -2px; }
/* Ein inset-Schatten wird HINTER den Kindelementen gezeichnet — das Video
   fuellt die Kachel vollstaendig aus und deckte ihn zu. Der Rahmen war da und
   nie zu sehen (Feldtest 2026-08-22). Deshalb eine eigene Auflage: ::after
   entsteht als letztes Kind und liegt damit ueber dem Video. */
.kachel.spricht::after {
  content: "";
  position: absolute; inset: 0;
  border: 2px solid var(--akzent);
  border-radius: inherit;
  pointer-events: none;
}

/* Das feste Seitenverhaeltnis ist der Kern: ohne es folgt die Breite dem
   Fenster, waehrend die Hoehe gedeckelt bleibt — die Kachel wird zum Streifen
   und object-fit schneidet alles ausser dem Kinn weg. Mit aspect-ratio ergibt
   sich die eine Kantenlaenge aus der anderen; max-width und max-height sorgen
   dafuer, dass die jeweils knappere Richtung gewinnt. */
/* Vollflaeche in der installierten App: dort brauchen wir die Fullscreen-API
   nicht, denn es gibt ohnehin keine Browserleiste. Der Unterschied ist nicht
   kosmetisch — die API laesst Chrome eine Meldung einblenden ("zum Beenden
   von oben ziehen"), die sich nicht abschalten laesst und im geteilten Bild
   dauerhaft im Weg steht. Die Drehsperre funktioniert bei installierten Apps
   auch ohne sie. Im Browser bleibt es bei der API — dort gibt es die
   Browserleiste, die wirklich weg soll. */
.kachel.vollflaeche {
  position: fixed; inset: 0; z-index: 60;
  width: 100%; height: 100%;
  max-width: none; max-height: none;
  aspect-ratio: auto;
  border-radius: 0;
}
/* Im Vollbild wird NICHTS abgeschnitten — dieselbe Regel wie oben bei
   `.kachel:fullscreen video`, und genau die fehlte hier.

   Zum zweiten Mal dasselbe Muster: eine Kachel wird auf zwei Wegen gross,
   die Sonderregel stand nur beim ersten. Der Vollflaechen-Weg ist der, den
   die installierte App geht — Jess' S24 also immer. In der Kachel ist
   `object-fit: cover` richtig (ein Hochkantbild wuerde das 16:9-Fach sonst
   verzerren), im Vollbild ist es falsch: dort gibt es kein Fach mehr, das
   gefuellt werden muesste, und cover schneidet vom Hochkantbild die Haelfte
   weg. Jess, 2026-09-19: „kommt er immer in quer und natuerlich
   abgeschnitten, weil die meisten in hochkant senden."

   Schwarze Raender sind das kleinere Uebel — dieselbe Abwaegung wie bei
   `.kachel.schirm video`. */
.kachel.vollflaeche video { object-fit: contain; }

/* Der Rest der Oberflaeche darf darunter nicht durchscheinen oder scrollen. */
body.kachel-vollflaeche .anwendung { visibility: hidden; }
body.kachel-vollflaeche .kachel.vollflaeche { visibility: visible; }

.kachel {
  aspect-ratio: 16 / 9;
  width: auto; height: 100%;
  max-width: 100%; max-height: 100%;
  position: relative; background: var(--medien-hg); border-radius: 10px;
  overflow: hidden; min-height: 0;
}
.kacheln video {
  width: 100%; height: 100%; display: block; background: var(--medien-hg);
  /* Ohne object-fit verzerrt ein Hochkant-Handybild die Kachel */
  object-fit: cover;
}
.kachel .name {
  position: absolute; left: 6px; bottom: 6px;
  font-size: 11px; background: var(--medien-marke); color: var(--auf-medien);
  padding: 2px 7px; border-radius: 6px;
}

/* wrap ist keine Kosmetik: ohne es laufen die Knoepfe auf einem schmalen
   Display seitlich aus dem Bild, ohne dass etwas darauf hinweist. */
.videoleiste {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 8px; justify-content: center;
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
}

.videoknopf {
  /* 44px in beide Richtungen — die kleinste Flaeche, die sich auf einem
     Touchscreen zuverlaessig treffen laesst. */
  min-width: 44px; min-height: 44px; padding: 0 10px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: 12px;
  background: var(--hg-karte); border: 1px solid var(--rand);
  color: var(--text); cursor: pointer; font-size: 14px;
}
.videoknopf svg {
  width: 22px; height: 22px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Der Querstrich erscheint nur im abgeschalteten Zustand */
.videoknopf .strich { display: none; }
.videoknopf[aria-pressed="true"] .strich { display: inline; }
.videoknopf[aria-pressed="true"] {
  background: var(--warn-hg); border-color: var(--warn-rand); color: var(--warn-text);
}
.videoknopf.verlassen {
  background: var(--warn-hg); border-color: var(--warn-rand); color: var(--warn-text);
}

/* Ansichtsknopf: sichtbar ist das Symbol der LAUFENDEN Ansicht, nicht das
   der Handlung. Auf dem Handy fehlen die Beschriftungen — ohne diese
   Unterscheidung sagt der Knopf gar nichts ueber den Zustand. */
#ansichtknopf .symbol-sprecher { display: none; }
#ansichtknopf.ist-sprecher .symbol-raster { display: none; }
#ansichtknopf.ist-sprecher .symbol-sprecher { display: inline; }

/* Akzent heisst: die Ansicht ist FESTGEHALTEN — von Hand gewaehlt oder vom
   Bildschirmteilen erzwungen. Ohne Akzent entscheidet die Automatik nach
   Teilnehmerzahl. Das ist die Auskunft, die auf dem Handy sonst fehlt. */
#ansichtknopf.festgehalten {
  background: var(--akzent-hg); border-color: var(--akzent); color: var(--akzent);
}
/* Den Chatknopf gibt es nur dort, wo Video und Chat UEBEREINANDER liegen.
   Steht der Chat daneben (breiter Monitor, Handy quer), ist er sichtbar und
   es gibt nichts umzuschalten — sichtbar wird er deshalb erst unten in der
   Gespraechsaufteilung. */
#chatknopf { position: relative; display: none; }

/* Der Punkt: waehrend das Video das Fenster fuellt, liegt der Verlauf
   dahinter. Ohne dieses Zeichen bekaeme niemand mit, dass gerade etwas
   hereinkam — und genau darum ging es Jess bei diesem Wunsch. */
.chatpunkt { display: none; }
#chatknopf.hat-neues .chatpunkt {
  display: block;
  position: absolute;
  top: 4px; right: 4px;
  width: 10px; height: 10px;
  background: var(--akzent);
  border: 2px solid var(--hg-karte);
  border-radius: 50%;
}

.videoknopf:disabled { opacity: .4; cursor: default; }

/* Der Lautstaerkeregler fuers Gespraech (Jess, 2026-09-19).

   Eine eigene Zeile UNTER der Leiste, kein schwebender Deckel — derselbe
   Grund wie beim „⋯"-Menue der Direktgespraeche: auf 390 px ragt ein Deckel
   ueber den Rand. Der Regler nimmt die volle Breite, weil ein Schieber, den
   man nicht trifft, keiner ist. */
.lautregler {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 10px 12px;
  background: var(--hg-erhoben);
  border-top: 1px solid var(--rand);
}
.lautregler label { font-size: 13px; color: var(--text-leise); }
.lautregler input[type="range"] {
  flex: 1 1 160px; min-width: 120px;
  /* Fingergross: die Bahn allein ist zu schmal zum Treffen. */
  height: 32px;
  accent-color: var(--akzent);
}
.lautregler output {
  font-size: 13px; font-variant-numeric: tabular-nums;
  min-width: 52px; text-align: right; color: var(--text);
}
/* Steht der Regler oben, soll man das am Knopf sehen, ohne ihn aufzuklappen. */
#lautknopf[aria-pressed="true"] .welle-fern,
#lautknopf[aria-pressed="true"] .welle-nah { stroke: var(--akzent); }
.lauthinweis {
  flex-basis: 100%; margin: 0;
  font-size: 12px; color: var(--text-leise);
}

/* Auf dem Handy nur die Symbole. Die Beschriftung bleibt im Markup — ein
   Vorlesegeraet liest weiterhin das aria-label, und ab Tablet ist sie da. */
.videoknopf .beschriftung { display: none; }
@media (min-width: 600px) {
  .videoknopf .beschriftung { display: inline; }
}
.videohinweis { margin: 0; padding: 7px 10px; font-size: 12.5px;
                color: var(--hinweis-text); background: var(--hinweis-hg); }
/* Der Weg ins Handbuch, wenn Kamera oder Mikrofon fehlen (rtc.js): in der
   Schriftfarbe des Hinweises, unterstrichen und fett — das Akzentblau stuende
   auf dem warmen Grund schlecht lesbar. Fingermass ueber die Polsterung. */
.videohinweis a { color: inherit; font-weight: 600; padding: 6px 0; white-space: nowrap; }

/* Ab Tablet die Seitenleiste dauerhaft zeigen */
@media (min-width: 760px) {
  .anwendung { display: grid; grid-template-columns: 260px 1fr;
               grid-template-rows: auto 1fr auto; height: 100%; }
  /* Dauerhaft sichtbar — die Schublade gibt es hier nicht mehr.

     ABSOLUT ueber die volle Hoehe, nicht als Grid-Zeile. Vorher stand hier
     `grid-row: 1 / 4`: sobald eine der Leisten rechts (Gast-Frist, Anruf,
     Kurzmeldung, Anhangstreifen) sichtbar war, legte das Grid eine Zeile an,
     und die Kanalleiste endete an der Oberkante der Eingabe (Jess am PC,
     2026-10-24). `1 / -1` hilft nicht — das reicht nur bis zur letzten
     AUSDRUECKLICHEN Zeile. Spalte 1 bleibt als Platzhalter stehen.
     Bewacht von tests/rauchtest/rechner.spec.js. */
  .seitenleiste { position: absolute; z-index: auto; width: 260px; max-width: none;
                  border-right: 1px solid var(--rand);
                  transform: none; }
  /* ALLES Uebrige rechts — ausdruecklich, nicht per Auto-Platzierung. Ohne die
     Kanalleiste im Grid fiele sonst jede Leiste ohne Spalte nach links, unter
     die Kanalleiste, und waere unsichtbar. Die Regeln fuer breite Monitore
     und Handy quer weiter unten sind spezifischer und gewinnen. */
  .anwendung > :not(.seitenleiste) { grid-column: 2; }
  /* Nur bei laufendem Gespraech eine eigene Zeile — sonst bekaeme der
     Verlauf die falsche und das Eingabefeld saesse in der Mitte.
     minmax(0, …) ist noetig, weil eine Grid-Zeile sonst nie kleiner wird als
     ihr Inhalt und der Verlauf das Fenster nach unten schoebe. */
  /* Waehrend eines Gespraechs ist das Video die Hauptsache; der Verlauf
     behaelt genug Platz fuer die letzten Nachrichten. */
  .anwendung.video-offen {
    grid-template-rows: auto minmax(0, 1.7fr) minmax(0, 1fr) auto;
  }
  .anwendung.video-offen.teilt {
    grid-template-rows: auto minmax(0, 3.4fr) minmax(0, 1fr) auto;
  }
  /* Hier bestimmt die Grid-Zeile die Hoehe, nicht der Flex-Anteil von oben */
  .videobereich { flex: initial; height: 100%; }
  #menueknopf, .abdunkeln { display: none; }
}

/* Ab einem breiten Monitor steht der Chat waehrend eines Gespraechs NEBEN dem
   Video statt darunter. Uebereinander haengt die Kachelgroesse an der Hoehe:
   auf 3440x1440 blieben so zwei Drittel der Flaeche ungenutzt, waehrend die
   Gesichter klein blieben. Darunter (Laptop, Tablet) bleibt es gestapelt — dort
   waere eine dritte Spalte fuer beides zu schmal. */
@media (min-width: 1200px) {
  .anwendung.video-offen {
    grid-template-columns: 260px minmax(0, 1fr) minmax(320px, 24%);
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  .anwendung.video-offen .kopf { grid-column: 2 / 4; grid-row: 1; }
  .anwendung.video-offen .videobereich {
    grid-column: 2; grid-row: 2 / 4; height: 100%;
    border-bottom: none; border-right: 1px solid var(--rand);
  }
  .anwendung.video-offen .verlauf { grid-column: 3; grid-row: 2; }
  .anwendung.video-offen .eingabe { grid-column: 3; grid-row: 3; }
}

/* Handy quer waehrend eines Gespraechs: uebereinander bleiben vom Video keine
   100 px, weil die Hoehe schon vom Kopf, der Leiste und dem Eingabefeld
   aufgebraucht ist. Nebeneinander bekommt das Video die volle Hoehe. */
@media (orientation: landscape) and (max-height: 560px) {
  .anwendung.video-offen {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  .anwendung.video-offen .kopf { grid-column: 1 / 3; grid-row: 1; }
  .anwendung.video-offen .videobereich {
    grid-column: 1; grid-row: 2 / 4;
    flex: initial; height: 100%;
    border-bottom: none; border-right: 1px solid var(--rand);
  }
  .anwendung.video-offen .verlauf { grid-column: 2; grid-row: 2; }
  .anwendung.video-offen .eingabe { grid-column: 2; grid-row: 3; }

  /* Quer ist das Fenster breit, die Videospalte aber schmal — beschriftete
     Knoepfe braechen dort um und fraessen die knappe Hoehe. */
  .videoknopf .beschriftung { display: none; }
}
/* Dasselbe ab 760 px Breite: dort steht die Kanalleiste dauerhaft links,
   absolut ueber die Polsterkante. Die Aufteilung oben hat keine Spalte fuer
   sie — also rueckt das Grid um ihre Breite ein, sonst laege sie ueber Kopf
   und Video (rechner.spec.js). */
@media (orientation: landscape) and (max-height: 560px) and (min-width: 760px) {
  .anwendung.video-offen { padding-left: 260px; }
}

/* --- Der Weg zurueck ins Videobild ------------------------------------ */

/* Standardmaessig nicht da: der Balken ergibt nur Sinn, solange ein Gespraech
   laeuft UND der Chat davorliegt. Sichtbar wird er unten in der
   Gespraechsaufteilung — und nur in den Fensterformen, in denen ueberhaupt
   umgeschaltet wird.

   Ueber die Polsterung bleibt das Antippziel 42 px hoch, obwohl der Text
   13 px misst — dasselbe Fingermass wie in der Fusszeile der Seitenleiste. */
.zurueckbalken {
  display: none;
  flex: none;
  align-items: stretch;
  width: 100%;
  background: var(--akzent-hg);
  border-bottom: 1px solid var(--rand);
}

/* Der Weg zurueck nimmt die ganze Breite, die der Auflegenknopf uebrig
   laesst: er ist die Handlung, die man im Chat oft will, und ein breites
   Ziel trifft man auch mit dem Daumen.

   Ueber die Polsterung bleibt das Antippziel 42 px hoch, obwohl der Text
   13 px misst — dasselbe Fingermass wie in der Fusszeile der Seitenleiste. */
.zurueckbalken .zumvideo {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 0;
  padding: 12px;
  font: inherit;
  font-size: 13px;
  color: var(--akzent);
  background: none;
  border: 0;
  cursor: pointer;
}

/* Rot wie der Verlassen-Knopf in der Videoleiste: derselbe Griff, dieselbe
   Farbe — auch wenn er hier an einer anderen Stelle sitzt. */
.zurueckbalken .auflegen {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 52px;
  color: var(--warn-text);
  background: var(--warn-hg);
  border: 0;
  border-left: 1px solid var(--rand);
  cursor: pointer;
}
.zurueckbalken .auflegen svg {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* --- Verbindungsprobe (§ 10.1) ---------------------------------------- */

/* Fusszeile der Seitenleiste: Verbindungsprobe und Abmelden nebeneinander.
   Beides sind Handlungen, die man einmal im Quartal braucht — sie stehen
   unten und sehen aus wie Links, nicht wie Knoepfe.

   Bis zum 2026-08-29 war die Verbindungsprobe ein umrandeter Knopf ueber
   volle Breite, weiter oben zwischen den Schaltern fuer Ton und Signale.
   Dort behauptete ihr Rahmen ein Gewicht, das sie im Alltag nicht hat. */
/* Seit dem 2026-09-13 (Jess): zwei Symbole GANZ UNTEN — ⚙️ und ?.
   `margin-top: auto` schiebt sie ans Ende der Leiste; der freie Platz steht
   darueber. `position: relative` ist der Anker fuer die Formatierhilfe, die
   nach oben aufklappt. */
.leistenfuss {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: auto;
  padding-top: 8px;
}


.leistenlink {
  margin: 0;
  /* Der Rahmen ist weg, das Fingermass nicht. 12 px Polsterung oben und
     unten machen aus einer 12-px-Zeile ein Ziel von rund 40 px. Die
     Verbindungsprobe wird genau dann gebraucht, wenn etwas klemmt — oft am
     Handy, oft unterwegs; ein Ziel von Textgroesse waere dort die falsche
     Sparsamkeit. Der Platz ist da: die Kanalliste darueber traegt flex: 1
     und gibt ihn ab. */
  padding: 12px 0;
  background: none;
  border: 0;
  color: var(--text-leise);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}

.leistenlink:hover { color: var(--text); }

/* Die eigene Auskunft ist ein echter Link (<a download>, § 3.5), die
   anderen sind Knoepfe. Ein <a> ist von Haus aus inline — dann zaehlte die
   Polsterung oben und unten nicht zur Zeilenhoehe, und das Fingermass von
   rund 40 px waere weg.

   `block` und nicht `inline-block`: die Einstellungsgruppe stapelt nicht von
   selbst, und „Abmelden" folgt direkt dahinter. Nebeneinander stuenden zwei
   unterstrichene Saetze wie einer da — und am Handy laege der Daumen genau
   zwischen ihnen. `fit-content`, damit nicht die ganze Zeile anklickbar ist. */
a.leistenlink { display: block; width: fit-content; }

/* Waehrend die Probe laeuft: sichtbar taub, und ohne Unterstreichung, damit
   sie in dem Moment nicht wie etwas Anklickbares aussieht. */
.leistenlink:disabled { opacity: 0.6; cursor: default; text-decoration: none; }

/* --- Einstellungsbereich (Jess' Freigabe vom 2026-09-11) -------------

   z-index 5: ueber der Bildschau (4), der Seitenleiste (3) und der
   Abdunkelung (2). Die Seitenleiste bleibt darunter offen stehen, der Deckel
   legt sich darueber.

   Kein <dialog>, deshalb auch kein ::backdrop — der dunkle Grund ist der
   Deckel selbst, wie bei der Bildschau. */
.einstellungen {
  position: fixed;
  z-index: 5;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--schleier-tief);
}

.einstellungskarte {
  width: 100%;
  max-width: 420px;
  /* dvh, nicht vh: sonst liegt der untere Rand am Handy hinter der
     Browserleiste — derselbe Grund wie bei der Bildschau. */
  max-height: 88dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 18px calc(16px + env(safe-area-inset-bottom));
  background: var(--hg-erhoben);
  border: 1px solid var(--rand);
  border-radius: var(--radius);
}

.einstellungskopf { display: flex; align-items: center; gap: 8px; }
.einstellungskopf h2 { margin: 0; font-size: 17px; flex: 1; }

.einstellungskopf button {
  /* Fingermass wie bei der Bildschau: 44 px. Dieser Knopf steht EINMAL im
     Bild, nicht fuenfzigmal — hier ist das volle Mass richtig. */
  width: 44px; height: 44px;
  margin: -8px -8px -8px 0;
  font-size: 26px;
  line-height: 1;
  color: var(--text-leise);
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.einstellungskopf button:hover { color: var(--text); background: var(--hg-karte); }

.einstellungsgruppe { margin-top: 18px; }
.einstellungsgruppe h3 {
  margin: 0 0 6px;
  font-size: 12px;
  color: var(--text-leise);
  text-transform: uppercase;
  letter-spacing: .05em;
}

.einstellungshinweis {
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-leise);
}

/* Die Versionszeile ganz unten, abgesetzt: sie ist keine Einstellung,
   sondern die Auskunft, an der ablesbar ist, welcher Stand laeuft. */
.einstellungskarte > .version {
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid var(--rand);
}

/* Die beiden Schalter darueber: Ton und Signale. Sie behalten ihren Rahmen —
   sie zeigen einen ZUSTAND an, und der braucht eine Flaeche, auf der er
   stehen kann. Die Fusszeile darunter zeigt keinen Zustand, sondern bietet
   eine Handlung an. */
.tonknopf {
  display: block;
  width: 100%;
  margin-top: 0.5rem;
  padding: 0.45rem 0.6rem;
  font: inherit;
  font-size: 0.85rem;
  color: var(--text-leise);
  background: transparent;
  border: 1px solid var(--rand);
  border-radius: 6px;
  cursor: pointer;
}

.tonknopf[aria-pressed="false"] { opacity: 0.55; }
.tonknopf:hover { color: var(--text); }

/* --- Die Farbprofilwahl (SPECS.md § 4.16) ------------------------------
   Kacheln und keine Auswahlliste: eine Farbe waehlt man, indem man sie
   ansieht. In einer <select> stuenden sechs Namen, und "Salbei" sagt einem
   nichts, der es nicht schon kennt.

   Die drei Baender zeigen Grund, Blase und Akzent — dieselben drei, an denen
   sich die Profile in der Vorschau unterscheiden liessen. Ihre Farben setzt
   profil.js als Inline-Stil aus der Profilliste; sie sind die EINZIGE Stelle,
   an der Farbe nicht aus dem geltenden Profil kommt, denn sie zeigen ja
   gerade die anderen. */
/* GENAU ZWEI Spalten, dunkel links, hell rechts — jede Zeile ist ein Paar
   (SPECS.md § 4.16). Mit `auto-fill` waeren es je nach Breite mal zwei, mal
   drei, und die Paare stuenden wieder wild verstreut. Die Karte ist ohnehin
   auf 420px gedeckelt; mehr als zwei Spalten gibt es hier nie. */
.profilwahl {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 0.5rem;
}

/* „Wie das Geraet" ueber beide Spalten. Nicht der Betonung wegen, sondern
   aus Rechnung: staende es in einer Spalte, saesse das erste dunkle Profil
   daneben und ALLE Paare waeren um eins verschoben. Nebenbei passt der Name
   dann ganz hinein, statt als „Wie das G…" abzubrechen. */
.profilkachel[data-kennung="automatisch"] {
  grid-column: 1 / -1;
}

.profilkachel {
  display: flex;
  align-items: center;
  gap: 8px;
  /* 40px hoch wie die uebrigen Antippziele im Menue: der Text misst 12px,
     die Polsterung macht die Flaeche. Siehe .leistenlink. */
  padding: 7px 9px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--text-leise);
  background: transparent;
  border: 1px solid var(--rand);
  border-radius: 8px;
  cursor: pointer;
  min-width: 0;
}

.profilkachel:hover { color: var(--text); }

/* Das gewaehlte Profil traegt den Akzent — dieselbe Sprache wie der offene
   Kanal in der Seitenleiste. */
.profilkachel[aria-checked="true"] {
  color: var(--akzent);
  border-color: var(--akzent);
  background: var(--akzent-hg);
}

.profilkachel:focus-visible {
  outline: 2px solid var(--akzent);
  outline-offset: 1px;
}

.profilbaender {
  display: flex;
  flex: none;
  width: 26px;
  height: 18px;
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid var(--rand);
}

.profilbaender i { flex: 1; }

/* Bei „Wie das Geraet" sind zwei Dinge wahr: gewaehlt ist die Automatik, in
   Kraft ist gerade ein bestimmtes Profil. Der Haken sagt das erste, dieser
   gestrichelte Ring das zweite. Er ist bewusst NICHT der Akzentrahmen der
   Wahl — sonst saehen zwei Kacheln gleich gewaehlt aus. */
.profilkachel[data-aktiv="true"] {
  border-style: dashed;
  border-color: var(--akzent);
  color: var(--text);
}

/* Nur fuer die Vorlesestimme. Nicht `display: none` und nicht
   `visibility: hidden` — beides nimmt den Text auch ihr weg. */
.nuraufgelesen {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.profilname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Anhaenge -------------------------------------------------------- */

/* Mehrere Anhaenge an EINER Nachricht (seit dem 2026-09-08).

   Bei einem einzelnen aendert dieser Block nichts: die Huelle ist dann ein
   gewoehnliches div, und das Bild steht darin wie eh und je. Ab zwei wird ein
   Raster mit gleich grossen Feldern daraus — fuenf Fotos in voller Breite
   untereinander sind keine Nachricht mehr, sondern eine Wand, an der man
   minutenlang vorbeiscrollt.

   Ueber vier zaehlt chat.js nicht weiter (data-anzahl deckelt bei 4): an der
   Aufteilung aendert sich ab da nichts mehr. */
.anhangraster { margin-top: 6px; }

.anhangraster:not([data-anzahl="1"]) {
  display: grid;
  gap: 6px;
  max-width: min(340px, 100%);
}
.anhangraster[data-anzahl="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.anhangraster[data-anzahl="3"],
.anhangraster[data-anzahl="4"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* Im Raster fuellt jedes Bild sein Feld und wird beschnitten statt verzerrt.
   Der Zuschnitt ist der Preis fuer eine Uebersicht, die in eine Bildschirmhoehe
   passt — angetippt zeigt die Bildschau ohnehin das ganze Bild. */
.anhangraster:not([data-anzahl="1"]) .anhang {
  width: 100%;
  max-width: none;
  height: auto;
  margin-top: 0;
  aspect-ratio: 1;
  object-fit: cover;
}

/* Eine Datei zwischen Bildern: gleiche Feldgroesse, damit das Raster nicht
   ausfranst. Der Name wird dort kurz — die volle Auskunft steht beim Antippen
   in der Kurzmeldung. */
.anhangraster:not([data-anzahl="1"]) .anhang-datei {
  max-width: none;
  height: 100%;
  margin-top: 0;
  padding: 6px;
}
.anhangraster:not([data-anzahl="1"]) .anhang-datei .anhangsymbol {
  width: 28px; height: 28px;
}


/* breite und hoehe stehen als Attribute am img. Der Browser haelt daraus den
   Platz frei, BEVOR das Bild da ist — sonst springt der Verlauf beim Laden,
   und wer gerade liest, verliert seine Stelle. */
.rumpf .anhang {
  display: block;
  max-width: min(320px, 100%);
  height: auto;
  margin-top: 6px;
  border-radius: 10px;
  background: var(--hg-erhoben);
  cursor: zoom-in;
}

/* „verfällt morgen, 14:30" unter einer Datei mit kurzer Frist (Anhangstufen,
   2026-09-26). Leise, aber eine eigene Zeile: sie betrifft den Empfaenger. */
.rumpf .anhang-frist {
  display: block;
  margin-top: 4px;
  font-size: 0.8rem;
  color: var(--text-leise);
}

.rumpf .anhang-weg {
  display: inline-block;
  margin-top: 6px;
  padding: 8px 12px;
  font-size: 0.85rem;
  color: var(--text-leise);
  background: var(--hg-erhoben);
  border: 1px dashed var(--rand);
  border-radius: 10px;
}

/* Eine Datei im Verlauf: Kuerzel, Name, Groesse — anklickbar wie ein Link,
   weil sie genau das ist. Das Herunterladen erzwingt der Server ueber
   Content-Disposition; das download-Attribut gibt dem Browser nur schon
   vorher den richtigen Namen. */
.rumpf .anhang-datei {
  display: flex;
  gap: 10px;
  align-items: center;
  max-width: min(320px, 100%);
  margin-top: 6px;
  padding: 8px 10px;
  color: inherit;
  text-decoration: none;
  background: var(--hg-erhoben);
  border: 1px solid var(--rand);
  border-radius: 10px;
}
.rumpf .anhang-datei:hover { border-color: var(--text-leise); }

.anhang-datei .anhangsymbol {
  width: 36px; height: 36px;
  background: var(--hg);
}

.anhang-datei .anhangtext { overflow: hidden; }

.anhang-loeschen {
  display: block;
  margin-top: 4px;
  padding: 2px 0;
  font: inherit;
  font-size: 0.78rem;
  color: var(--text-leise);
  background: none;
  border: 0;
  cursor: pointer;
}
.anhang-loeschen:hover { color: var(--fehler); }

/* Solange die KI arbeitet. Gedaempft und kursiv: das ist kein Beitrag,
   sondern eine Auskunft ueber den Zustand. Fluechtig — er steht in keiner
   Datenbank und ist nach einem Neuladen weg. */
.ki-denkt {
  font-style: italic;
  opacity: 0.75;
}

/* Zeigt, in welchen Kanaelen @AI und @WEB etwas tun. */
.kanal-ki {
  margin-left: 6px;
  font-size: 0.8em;
  opacity: 0.7;
}

/* Kurze Rueckmeldung ueber dem Eingabefeld. Gedaempft statt alarmierend:
   sie meldet, dass etwas geklappt hat — die warnfarbene Leiste weiter oben
   ist fuer Stoerungen da. Anklickbar, damit sie nicht im Weg stehen muss. */
.kurzmeldung {
  padding: 7px 12px;
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--text-leise);
  text-align: center;
  background: var(--hg-erhoben);
  border-top: 1px solid var(--rand);
  cursor: pointer;
}

/* Der angenommene Anruf. Liegt ueber dem Eingabefeld, wo der Daumen ohnehin
   ist — wer aus der Meldung kommt, soll nicht suchen muessen.

   Gruen und nicht in der Akzentfarbe: das ist der eine Knopf hier, der etwas
   EINSCHALTET (Kamera und Mikrofon). Er darf aussehen wie der Annehmen-Knopf
   eines Telefons und nicht wie die uebrigen. */
.anrufleiste {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--hg-erhoben);
  border-top: 1px solid var(--rand);
}

.anrufleiste #anruftext {
  flex: 1;
  font-size: 0.9rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.anrufleiste #annehmenknopf {
  /* 44 px hoch: die Groesse, die ein Finger sicher trifft — dieselbe Regel
     wie beim Vollbildknopf. */
  min-height: 44px;
  padding: 0 18px;
  font: inherit;
  font-weight: 600;
  color: var(--auf-gut);
  background: var(--gut);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}

.anrufleiste #anrufweg {
  min-width: 36px;
  min-height: 36px;
  font-size: 1.2rem;
  color: var(--text-leise);
  background: transparent;
  border: 0;
  cursor: pointer;
}

/* Der Streifen ueber dem Eingabefeld: was gleich mitgeht.

   Seit dem 2026-09-08 koennen es mehrere sein. Deshalb umbrechend UND mit
   Hoehengrenze: zehn Karten untereinander schoeben sonst das Eingabefeld aus
   dem Bild, und getippt wird blind — derselbe Fehler, den die Tastatur am
   2026-08-31 verursacht hat. */
.anhangstreifen {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 10px;
  align-items: flex-start;
  max-height: 30dvh;
  overflow-y: auto;
  padding: 8px 12px;
  background: var(--hg-erhoben);
  border-top: 1px solid var(--rand);
}

/* „8 Dateien noch nicht gesendet — ➤ schickt sie ab" (Jess, 2026-09-27):
   eine eigene Zeile ueber den Karten, deshalb die volle Breite. Leise wie
   die Groessenangabe — sie erklaert, sie mahnt nicht. */
.anhanghinweis {
  flex: 1 1 100%;
  font-size: 0.75rem;
  color: var(--text-leise);
}

/* Eine Karte je Datei. Bei EINER nimmt sie die ganze Breite — dann sieht der
   Streifen aus wie bis zum 2026-09-08. Ab zwei stehen sie nebeneinander: der
   Name ist dann kuerzer zu lesen, dafuer sieht man auf einen Blick, wie viel
   mitgeht. 220px ist die Grenze, unter der Kuerzel, Name und Kreuz sich
   gegenseitig verdraengen. */
.anhangkarte {
  display: flex;
  flex: 1 1 220px;
  min-width: 0;
  gap: 10px;
  align-items: center;
}

.anhangkarte img {
  flex: none;
  width: 44px; height: 44px;
  object-fit: cover;
  border-radius: 8px;
}

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

/* min-width: 0 am Elternteil und hier: ohne das waechst ein langer Dateiname
   den Streifen ueber die Fensterbreite hinaus, statt gekuerzt zu werden —
   ein Flex-Kind schrumpft von sich aus nicht unter seinen Inhalt. */
.anhangname {
  overflow: hidden;
  font-size: 0.85rem;
  color: var(--text);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.anhanggroesse {
  font-size: 0.75rem;
  color: var(--text-leise);
}

/* Das Kuerzel steht an der Stelle, an der bei einem Bild die Vorschau sitzt —
   gleiche Groesse, damit der Streifen nicht springt. */
.anhangsymbol {
  display: flex;
  flex: none;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-leise);
  background: var(--hg);
  border: 1px solid var(--rand);
  border-radius: 8px;
}

/* Das Kreuz gehoert seit dem 2026-09-08 zur KARTE, nicht zum Streifen: eines
   fuer alles liesse bei fuenf Bildern nur "alles oder nichts" zu. */
.anhangweg {
  flex: none;
  width: 32px; height: 32px;
  font-size: 20px;
  line-height: 1;
  color: var(--text-leise);
  background: none;
  border: 0;
  cursor: pointer;
}

/* Der Anhaengeknopf sitzt links vom Feld und sieht aus wie die Videoknoepfe:
   Symbol, keine Beschriftung — auf 390 px ist dafuer kein Platz. */
#anhangknopf {
  flex: none;
  width: 40px; height: 40px;
  padding: 8px;
  color: var(--text-leise);
  background: none;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
}
#anhangknopf svg {
  width: 100%; height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
#anhangknopf:hover { color: var(--text); }
#anhangknopf:disabled { opacity: 0.4; cursor: default; }

/* Vollbildansicht eines Bildes. z-index ueber der Seitenleiste (2) und dem
   Abdunkeln, aber unter nichts anderem — der Videobereich liegt hoeher. */
.bildschau {
  position: fixed;
  z-index: 4;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--medien-schleier);
}

.bildschau img {
  max-width: 100%;
  /* dvh, nicht vh: sonst liegt der untere Rand am Handy hinter der
     Browserleiste. */
  max-height: 92dvh;
  object-fit: contain;
}

.bildschau button {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  right: 12px;
  width: 44px; height: 44px;
  font-size: 28px;
  line-height: 1;
  color: var(--auf-medien);
  background: var(--medien-flaeche);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.eisergebnis {
  margin: 0 0 0.5rem;
  padding: 0.5rem 0.6rem;
  font-size: 0.78rem;
  line-height: 1.35;
  /* Der Text kommt zeilenweise aus deutung(); umbrechen soll er trotzdem,
     sonst schiebt eine lange TURN-Adresse die Seitenleiste auf. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border-radius: 8px;
  border-left: 3px solid currentColor;
  background: var(--aufhell-leise);
}

.eisergebnis.gut { color: var(--gut-text); }
.eisergebnis.schlecht { color: var(--fehler); }

/* --- Vollbildknopf: rein und raus ------------------------------------- */
/*
   Der Knopf ist ein Umschalter. Solange er im Vollbild dasselbe Symbol zeigt,
   glaubt man, er sei kaputt — und am Handy gibt es keinen Browserrahmen mehr,
   der einen herausliesse. (Feldtest 2026-08-21.)
*/
.vollbildknopf .raus { display: none; }
.vollbildknopf.zurueck .rein { display: none; }
.vollbildknopf.zurueck .raus { display: inline; }

/* --- Waehrend eines Gespraechs: erst das Bild, auf Wunsch der Chat ------

   Bis zum 2026-09-08 stand hier eine feste Aufteilung: 60 % der Fensterhoehe
   fuers Video, der Rest fuer einen Verlauf, den waehrend eines Gespraechs
   kaum jemand liest. Jess am 2026-09-08:

     „Bei einem Videocall nimmt das Callfenster nur ca. 70 % der Hoehe ein.
      Darunter ist noch das Chatfenster zu sehen. Dies sorgt dafuer, dass die
      Videobilder auf dem Smartphone unnoetig klein sind."

   Jetzt gehoert das ganze Fenster dem Video. Der Chatknopf in der Videoleiste
   holt den Verlauf davor, der Balken darueber fuehrt zurueck — der Zustand
   steht als Klasse `chat-offen` an .anwendung (chat.js, chatWaehrendVideo).

   DIE ABFRAGE trifft genau die Faelle, in denen Video und Chat UEBEREINANDER
   liegen: hochkant unter 1200 px immer, quer nur solange das Fenster hoch
   genug ist. Die beiden anderen Faelle stellen beides NEBENEINANDER — ab
   1200 px und am Handy quer (max-height: 560px) — dort ist der Chat ohnehin
   zu sehen, es gibt nichts umzuschalten, und keine Regel von hier greift.
   Genau deshalb ist die zweite Zeile das Gegenstueck zu jener Abfrage und
   nicht etwa `max-width: 900px`: zwei Wege fuer dieselbe Sache brauchen zwei
   Ausnahmen, die sich decken.

   Dieser Block steht am ENDE der Datei, weil er die Zeilenaufteilung aus dem
   760-px-Block ueberschreibt und bei gleicher Spezifitaet die spaetere Regel
   gewinnt.
*/
@media (max-width: 1199px) and (orientation: portrait),
       (max-width: 1199px) and (min-height: 561px) {
  /* Das Video nimmt, was die Kopfzeile uebrig laesst. */
  .anwendung.video-offen .videobereich {
    flex: 1 1 auto;
    height: auto;
    border-bottom: none;
  }
  .anwendung.video-offen .verlauf,
  .anwendung.video-offen .eingabe { display: none; }

  /* Ab 760 px ist .anwendung ein Grid. Ohne diese Zeile blieben die Zeilen
     fuer Verlauf und Eingabe als leerer Platz stehen: `1fr` verteilt auch
     dann, wenn in der Zeile nichts liegt. Beide Faelle — mit und ohne
     Bildschirmteilen — bekommen dieselbe Aufteilung, denn mehr als das ganze
     Fenster gibt es beim Teilen auch nicht. */
  .anwendung.video-offen,
  .anwendung.video-offen.teilt { grid-template-rows: auto minmax(0, 1fr); }

  /* --- Der Chat davor ------------------------------------------------- */

  .anwendung.video-offen.chat-offen .verlauf,
  .anwendung.video-offen.chat-offen .eingabe { display: flex; }
  .anwendung.video-offen.chat-offen,
  .anwendung.video-offen.chat-offen.teilt {
    grid-template-rows: auto auto minmax(0, 1fr) auto;
  }

  /* Der Videobereich wird NICHT entfernt, sondern absolut ueber die
     Anwendung gelegt und unsichtbar gemacht.

     `display: none` haette zwei Folgen, und beide waeren im Feld schwer zu
     finden: die Kacheln messen sich dann zu 0 (chat.js, layoutAnwenden liest
     clientWidth/clientHeight), und ob ein <video> ohne Kasten seinen TON
     weiterspielt, entscheidet der Browser. Ein Gespraech, das beim Blick in
     den Chat verstummt, waere der schlimmere Fehler.

     So behaelt der Bereich seine Masse, das Bild laeuft weiter, und beim
     Zurueckschalten steht es sofort da statt neu aufzubauen. */
  .anwendung.video-offen.chat-offen .videobereich {
    position: absolute;
    inset: 0;
    visibility: hidden;
    pointer-events: none;
  }

  .anwendung.video-offen.chat-offen .zurueckbalken { display: flex; }

  /* Erst hier gibt es den Knopf: nur wo umgeschaltet werden kann. */
  #chatknopf { display: inline-flex; }
}

/* --- Verwaltung (SPECS.md § 4.15) -----------------------------------

   Der Bereich erbt .einstellungen und .einstellungskarte: dieselbe Bauweise,
   derselbe Deckel, dieselben drei Wege hinaus. Nur breiter — er traegt Listen
   mit Namen, Marken und Knoepfen nebeneinander, und 420 px reissen die
   Knopfreihe auf dem Ultrawide unnoetig um.

   z-index 6: EINE Stufe ueber dem Einstellungsdeckel (5), aus dem er
   aufgerufen wird. Ohne das laege er darunter und waere unsichtbar. */
#verwaltung { z-index: 6; }

.verwaltungskarte { max-width: 560px; }

/* --- Der Riegel --- */

.riegelform { margin-top: 14px; }

.riegelzeile { display: block; margin-top: 12px; }

.riegelbeschriftung {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  color: var(--text-leise);
}

.riegelzeile input {
  width: 100%;
  padding: 10px;
  font: inherit;
  color: var(--text);
  background: var(--hg-karte);
  border: 1px solid var(--rand);
  border-radius: 8px;
}

.riegelfehler { margin: 8px 0 0; font-size: 12px; color: var(--fehler); }

.riegelknopf {
  width: 100%;
  margin-top: 12px;
  padding: 11px;
  font: inherit;
  color: var(--text);
  background: var(--akzent-hg);
  border: 1px solid var(--rand);
  border-radius: 8px;
  cursor: pointer;
}
.riegelknopf:hover { border-color: var(--akzent); }

/* --- Reiter --- */

.reiterleiste {
  display: flex;
  gap: 4px;
  margin-top: 14px;
  border-bottom: 1px solid var(--rand);
}

.reiter {
  /* 12 px Polsterung machen aus der 13-px-Zeile ein Ziel von rund 40 px —
     dieselbe Rechnung wie bei .leistenlink. Die Verwaltung wird am Handy
     benutzt, das ist ihr ganzer Anlass. */
  padding: 12px 14px;
  font: inherit;
  font-size: 13px;
  color: var(--text-leise);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.reiter[aria-selected="true"] { color: var(--text); border-bottom-color: var(--akzent); }

/* --- Meldungen --- */

.verwaltungmeldung {
  margin: 12px 0 0;
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text);
  background: var(--hg-karte);
  border-left: 2px solid var(--akzent);
  border-radius: 6px;
}
.verwaltungmeldung.fehler { border-left-color: var(--fehler); color: var(--fehler); }

/* Einladung / Ruecksetzlink (§ 3.8). Der Code hoechstens 240 px breit: am
   390-px-Handy bleibt Luft, und eine Kamera am anderen Geraet trifft ihn
   aus einer Armlaenge. Seine Farben stehen im SVG selbst (verwaltung.js). */
.einmallink {
  margin: 8px 0 0;
  padding: 12px;
  background: var(--hg-karte);
  border-radius: 6px;
}
.einmallink .qrcode {
  display: block;
  width: 100%;
  max-width: 240px;
  height: auto;
  margin: 0 auto 4px;
  border-radius: 4px;
}
.einmallinkknoepfe {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

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

.verwaltungsliste { margin: 12px 0 0; padding: 0; list-style: none; }

/* Die Vorgabe der Loeschfrist ueber der Kanalliste (§ 3.6). */
.fristvorgabe {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
  margin-top: 12px;
  font-size: 12px;
  color: var(--text-leise);
}

/* Der Satz unter jedem KI-Schalter (ki/wege.js): die Folge der Wahl. Er steht
   dauerhaft da, nicht erst beim Umstellen — gedaempft, aber nicht gekuerzt. */
.kiwegehinweis {
  margin: 4px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-leise);
}

.verwaltungszeile {
  padding: 12px 0;
  border-bottom: 1px solid var(--rand);
}
.verwaltungszeile:last-child { border-bottom: 0; }

.zeilenkopf { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 8px; }
.zeilenname { font-size: 14px; }

.zeilenmarken { display: flex; flex-wrap: wrap; gap: 4px 6px; }

.marke {
  padding: 2px 7px;
  font-size: 11px;
  color: var(--text-leise);
  background: var(--hg-karte);
  border-radius: 999px;
}
.markeadmin { color: var(--akzent); background: var(--akzent-hg); }
/* Der Gast bekommt einen Rahmen statt einer Farbe: es ist kein Zustand, vor
   dem gewarnt werden muesste (wie „gesperrt"), und auch kein Rang (wie
   „Admina") — es ist eine ANDERE Art Konto. Ein Rahmen sagt genau das. */
.markegast { color: var(--text); box-shadow: inset 0 0 0 1px var(--rand); }
.markegesperrt { color: var(--fehler); }
/* Angelegt, Passwort noch nicht festgelegt (§ 3.8). */
.markeeingeladen { color: var(--akzent); box-shadow: inset 0 0 0 1px var(--akzent); }
.markeki { color: var(--akzent); }
/* Bittet um einen neuen Link (§ 3.7b): eine Aufgabe fuer die Admin, kein
   Fehler — dieselbe Farbe wie „eingeladen". */
.markelinkbitte { color: var(--akzent); box-shadow: inset 0 0 0 1px var(--akzent); }
/* „Passwort vergessen?" auf der Anmeldekarte. */
#vergessenknopf { align-self: center; margin-top: 10px; }
/* Der Weg ins Handbuch auf Anmeldung und Einladung: mittig wie „Passwort
   vergessen?“, aber leiser — er ist die Ausnahme, nicht der Weg. */
.hilfelink { align-self: center; text-align: center; }

/* Der Kurzweg neben dem ? im Fuss der Leiste. */
.handbuchkurz { margin-left: 6px; }

/* Das Handbuch als Deckel (chat.js, handbuchOeffnen). Ueber allem — auch
   ueber einer vollflaechigen Kachel (60) —, denn wer es oeffnet, will es
   lesen. Die App bleibt darunter verbunden. */
.handbuch {
  position: fixed;
  z-index: 70;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--hg);
}
.handbuchkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  padding: 4px 6px 4px 16px;
  background: var(--hg-erhoben);
  border-bottom: 1px solid var(--rand);
}
.handbuchkopf h2 { margin: 0; font-size: 16px; flex: 1; }
/* Kommt der Pfeil dazu, rueckt der Titel nach rechts statt zu springen. */
#handbuchzurueck, #handbuchzu {
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: 0;
  color: var(--text);
  font-size: 26px;
  cursor: pointer;
}
.handbuchrahmen { flex: 1; width: 100%; border: 0; background: var(--hg); }
.vergessen { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; font-size: 13px; color: var(--text-leise); }
.vergessen p { margin: 0; }
/* Unter einem Ruecksetzlink: wohin er gehoert. */
.linkhinweis { margin: 8px 0 0; font-size: 12px; color: var(--text-leise); }
.mitgliedlinkbitte { display: block; color: var(--akzent); font-size: 11px; }

.zeilenmitglieder {
  margin-top: 5px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-leise);
}

/* Umbrechend, nicht scrollend: auf 390 px stehen bis zu vier Knoepfe in
   einer Zeile, und keiner davon darf aus dem Bild laufen. */
.zeilenknoepfe { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

.zeilenknopf {
  /* 9 px auf 12-px-Schrift ergibt rund 34 px Hoehe. Bewusst kleiner als die
     40 px der Einstellungen: hier stehen bis zu vier nebeneinander, und mit
     vollem Mass passten sie auf 390 px nicht mehr in zwei Zeilen.

     Bis zum 2026-09-13 stand hier als Begruendung „das Loeschen ist gar
     nicht hier". Seitdem ist es hier (§ 3.5). Das kleine Mass bleibt
     trotzdem vertretbar, weil ein Fehlgriff auf „Löschen" noch nichts
     loescht: erst kommt die Rueckfrage mit den Zahlen, dann muss der
     Benutzername abgetippt werden. Wer das Mass hier vergroessert, tut es
     fuer die Bedienbarkeit — nicht, weil ein Fehlgriff sonst etwas zerstoert. */
  padding: 9px 12px;
  font: inherit;
  font-size: 12px;
  color: var(--text-leise);
  background: var(--hg-karte);
  border: 1px solid var(--rand);
  border-radius: 6px;
  cursor: pointer;
}
.zeilenknopf:hover { color: var(--text); border-color: var(--akzent); }
/* Unwiderruflich. Dieselben zwei Tokens wie beim Loeschen einer Nachricht
   (.eingriffaktion.warnung) — eine Farbe fuer „das geht nicht zurueck". */
.zeilenknopf.warnung { color: var(--fehler); border-color: var(--warn-rand); }
