﻿/* Build One — Prototype Design Tokens v2 "Elevation & Leben" */

:root {
  /* Sichere Bereiche (Notch, Home-Indikator) und Höhe der App im Gerätemodus
     (Block 3). Am Desktop sind beide Bereiche 0. --app-height setzt im
     Gerätemodus syncViewportHeight() in app.js (Tastatur). */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --app-height: 100vh;
  /* Kühles, blau-getöntes Off-White statt warmem Creme — knüpft an
     --color-primary an. Terracotta/Orange wurde vollständig aus der Palette
     entfernt (auch aus den zwei zuvor bewusst orange belassenen Ausnahmen,
     Meilenstein-Tag und Avatar-Farbverläufe g2/g4) — Blau/Weiss sind jetzt
     ausnahmslos die einzigen Hauptfarben. */
  --color-bg: #F4F6F9;
  --color-surface: #FFFFFF;
  --color-text-primary: #1A2433;
  --color-text-secondary: #56677B;
  --color-text-tertiary: #8D97A6;
  --color-primary: #2B4A6F;
  --color-primary-dark: #1E3450;
  --color-primary-bright: #4A7FB5;
  --color-primary-soft: #E8EEF4;
  /* Zweite, hellere Blau-Nuance für Premium-Hinweise & dezente
     Fortschritts-Nudges — damit Premium nie nach Warnung/Restriktion
     aussieht. Bleibt innerhalb der Blau-Familie (--color-primary-bright),
     keine dritte Hauptfarbe. */
  --color-primary-bright-soft: #E1EBF8;
  --color-border: #E2E7ED;
  --color-border-strong: #C7D0DA;
  --color-hairline: #EEF1F5;
  --color-danger: #B0503C;
  /* Erledigt-Signal (Ziel abgeschlossen / Meilenstein erreicht) — bewusst
     begrenzte, funktionale Ausnahme von Blau/Weiss, kein neuer Akzent für
     Dekoration. Gleicher Grünton wie der bestehende Online-Status-Punkt. */
  --color-success: #4C9A6E;
  --color-success-bright: #6FB78C;

  /* Beitragstyp-Farben — bewusst eng begrenzte Ausnahme von "alle Typen
     gleich neutral": nur das kleine Typ-Label bekommt Farbe, die Karte
     selbst bleibt unverändert (siehe .type-tag weiter unten). Kräftig/
     leuchtend statt gedeckt (Nutzerwunsch nach mehr "Pop") — trotzdem
     alle fünf in ähnlicher Sättigung/Helligkeit, damit keiner "besser"
     oder "schlechter" wirkt als ein anderer. Rückschlag ist absichtlich
     NICHT rot/warnend, sondern ein kräftiges Rose/Beere — ein Rückschlag
     soll nie wie ein Fehler aussehen. */
  --color-type-frage: #4550A8;
  --color-type-erfahrung: #0E8A76;
  --color-type-erfolg: #D9A61C;
  --color-type-rueckschlag: #C14B78;
  --color-type-diskussion: #7A4FA8;

  /* One consistent sharp radius for nearly everything. Circles (--radius-pill) are
     reserved for avatars, the status dot, the create button and the photo picker —
     things that represent a person or the primary action, not generic containers. */
  --radius: 6px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  /* Elevation — the primary way surfaces separate from the background now.
     Reserved for cards, sheets, dropdowns and buttons — not every list row. */
  --shadow-1: 0 1px 2px rgba(26,36,51,0.05), 0 1px 1px rgba(26,36,51,0.03);
  --shadow-2: 0 2px 6px rgba(26,36,51,0.06), 0 10px 24px rgba(26,36,51,0.08);
  --shadow-3: 0 4px 10px rgba(26,36,51,0.08), 0 16px 36px rgba(26,36,51,0.18);

  /* Derselbe Verlauf wie .intro-icon auf den drei Value-Proposition-Screens,
     zentral als Variable statt an jeder einzelnen Stelle wiederholt — greift
     auf alle primären Buttons (.btn-primary/.btn-premium), die eigene
     Chat-Bubble und die grossen, zuvor flach gefüllten Icon-Kacheln (Ziele,
     Aktueller Plan, Tagebuch, Neuer Eintrag, angepinnte Team-Nachricht). */
  --gradient-action: linear-gradient(150deg, var(--color-primary-dark) 0%, var(--color-primary) 55%, var(--color-primary-bright) 100%);

  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --ease: cubic-bezier(.4,0,.2,1);

  /* Typografie-Skala (31.08.2026, Design-Systematisierung) — ersetzt 17
     vorher fast beliebig verteilte font-size-Werte (viele nur 0,5px
     auseinander, z.B. 11/11.5/12/12.5/13px) durch 7 feste Stufen. Bewusst
     NICHT auf Avatar-Initialen (an die jeweilige Kreisgrösse kalibriert,
     z.B. .avatar.lg/.post-author .avatar), den Emoji-Reaktionspicker oder
     das Annotations-Panel (reine Prototyp-Notizen, kein App-UI) angewendet
     — diese behalten ihre eigenen, absichtlich separaten Werte. */
  --text-2xs: 10px;
  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-lg: 15px;
  --text-xl: 19px;
  --text-2xl: 23px;
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

/* Formularelemente erben font-family in manchen Browsern NICHT automatisch vom
   Body (UA-Stylesheet-Eigenheit) — ohne diese Regel könnten button/input/
   textarea/select je nach Browser auf die System-UI-Schrift zurückfallen statt
   auf --font-sans. Absicherung, unabhängig davon, ob einzelne Klassen (.btn,
   .input, …) font-family schon selbst setzen. */
button, input, textarea, select { font: inherit; }

html, body {
  height: 100%;
  background: #E6EBF1;
  font-family: var(--font-sans);
  color: var(--color-text-primary);
}

body {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 32px 16px;
  min-height: 100vh;
}

/* ---------- Phone frame ---------- */

.phone-frame {
  width: 390px;
  height: 844px;
  background: #0B0B0C;
  border-radius: 52px;
  padding: 14px;
  box-shadow: 0 40px 80px rgba(20,18,14,0.35), 0 4px 12px rgba(20,18,14,0.15);
  position: relative;
  flex-shrink: 0;
}

.phone-notch {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 120px;
  height: 26px;
  background: #0B0B0C;
  border-radius: 0 0 18px 18px;
  z-index: 50;
}

.phone-screen {
  width: 100%;
  height: 100%;
  background: var(--color-bg);
  border-radius: 38px;
  /* clip statt hidden: hidden erzeugt trotz unsichtbarer Scrollbar einen
     programmatisch/fokus-scrollbaren Container. Ein .focus() auf ein Feld
     innerhalb eines gerade erst translateX(100%)-positionierten Overlays
     (z. B. #search-input beim Öffnen der Suche) liess den Browser dieses
     Element "in den sichtbaren Bereich scrollen", noch bevor der Slide-
     Übergang fertig war — scrollLeft blieb danach dauerhaft auf einer vollen
     Overlay-Breite stehen und verschob jede künftige Overlay-Darstellung
     um genau diesen Betrag (Ursache des wiederholt aufgetretenen "Suche
     öffnet Löschen-Bestätigung"-Bugs, unabhängig vom Lupe-Icon selbst).
     clip verhält sich beim Rendern/Eckenradius identisch zu hidden, erzeugt
     aber keinen Scroll-Container — scrollLeft kann so gar nicht erst
     abdriften. */
  overflow: clip;
  position: relative;
}

/* ---------- Umgebung (Beta Block 1) ----------
   Band "Testumgebung" nur bei html[data-env="test"], mittig unter der Notch
   im freien Bereich der Statusleiste, über allem (auch über Toasts, z 60),
   ohne Klicks abzufangen. Sperrmeldung nur bei html[data-env="none"], deckt
   den ganzen Bildschirm ab (die Notch des Rahmens bleibt sichtbar). Beide setzt applyEnvironmentGate() in app.js.
   Im Gerätemodus (Block 3) liegt das Band als Leiste ganz oben, siehe unten. */
.env-band,
.env-blocked { display: none; }

html[data-env="test"] .env-band {
  display: block;
  position: absolute;
  top: 28px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 70;
  pointer-events: none;
  white-space: nowrap;
  padding: 2px 10px;
  border-radius: var(--radius);
  background: var(--color-primary-dark);
  color: #fff;
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 0.01em;
}

html[data-env="none"] .env-blocked {
  display: flex;
  position: absolute;
  inset: 0;
  /* über allen Ansichten und Overlays (bis 39), unter der Notch (50) */
  z-index: 45;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: var(--color-bg);
  color: var(--color-text-primary);
  font-size: var(--text-md);
  line-height: 1.5;
  text-align: center;
}

.status-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px 4px;
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--color-text-primary);
  position: relative;
  z-index: 10;
}

.status-bar .status-icons { display: flex; gap: 5px; align-items: center; }

/* ---------- Views ---------- */

.view {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  background: var(--color-bg);
}

.view.active { display: flex; }

.view-scroll {
  flex: 1;
  overflow-y: auto;
  -ms-overflow-style: none;
  scrollbar-width: none;
}
.view-scroll::-webkit-scrollbar { display: none; }

/* Knopfbereich unten in einer Ansicht (Block 3): ersetzt die früheren
   Inline-Polsterungen, Abstand nach unten inklusive sicherem Bereich.
   --actions-pb lässt sich je Ansicht inline anpassen (Intro 34, Willkommen 14). */
.view-actions { padding: 0 20px calc(var(--actions-pb, 26px) + var(--safe-bottom)); }
.view-actions-wide { padding-left: 24px; padding-right: 24px; --actions-pb: 28px; }

/* Overlay screens slide in from the right. .view.overlay ist spezifischer als
   .view (display:none) bzw. .view.active (display:flex) — display:flex gilt
   dadurch für JEDES Overlay, unabhängig von .active. Einziger Schutz vor
   Anzeige war bisher transform:translateX(100%) plus overflow:hidden am
   Elternelement (#phone-screen); versagt diese Verschiebung aus irgendeinem
   Grund (z. B. abgebrochener Übergang, Rendering-Eigenheit), bleibt ein
   eigentlich inaktives Overlay voll sichtbar und gewinnt gegen jedes andere
   per DOM-Reihenfolge — genau der wiederholt aufgetretene "Suche öffnet
   Löschen-Bestätigung"-Bug. visibility ist eine zweite, von transform
   unabhängige Absicherung: verzögert auf "hidden" beim Schliessen (erst nach
   Ende der Slide-Animation), aber sofort auf "visible" beim Öffnen — Optik
   bleibt unverändert, aber ein inaktives Overlay ist jetzt garantiert
   unsichtbar/nicht antastbar, ganz gleich was mit seiner Position passiert. */
.view.overlay {
  display: flex;
  transform: translateX(100%);
  visibility: hidden;
  transition: transform .32s var(--ease), visibility 0s linear .32s;
  z-index: 30;
  pointer-events: none;
}
.view.overlay.active {
  transform: translateX(0);
  visibility: visible;
  transition: transform .32s var(--ease), visibility 0s linear 0s;
  pointer-events: auto;
}

/* Sheet slides up from the bottom */
.sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20,18,14,0.32);
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s var(--ease);
  z-index: 39;
}
.sheet-backdrop.active { opacity: 1; pointer-events: auto; }

.sheet {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: var(--color-surface);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-3);
  transform: translateY(100%);
  transition: transform .32s var(--ease);
  z-index: 40;
  padding: 10px 20px calc(28px + var(--safe-bottom));
}
.sheet.active { transform: translateY(0); }

.sheet-handle {
  width: 36px; height: 4px;
  background: var(--color-border-strong);
  border-radius: var(--radius-pill);
  margin: 8px auto 16px;
}

/* ---------- Typography ---------- */

.headline {
  font-family: var(--font-sans);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
}

.eyebrow {
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.text-secondary { color: var(--color-text-secondary); }
.text-tertiary { color: var(--color-text-tertiary); }

/* ---------- Header bar (in-app screens) ---------- */

.screen-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 20px 14px;
}

.screen-title {
  font-family: var(--font-sans);
  font-weight: 800;
  letter-spacing: -0.01em;
  font-size: var(--text-xl);
  color: var(--color-text-primary);
}

.icon-btn {
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius);
  background: transparent;
  border: none;
  color: var(--color-text-primary);
  cursor: pointer;
  transition: background .15s var(--ease), transform .1s var(--ease);
}
.icon-btn:active { transform: scale(0.92); background: var(--color-primary-soft); }

.detail-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--color-border);
}
.detail-header .title { font-family: var(--font-sans); font-weight: 700; font-size: var(--text-lg); }
.detail-header-action {
  margin-left: auto;
  font-size: var(--text-sm); font-weight: 600; color: var(--color-primary);
  background: none; border: none; cursor: pointer; padding: 4px 0;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 600;
  border-radius: var(--radius);
  border: none;
  cursor: pointer;
  transition: transform .1s var(--ease), opacity .15s var(--ease), background .15s var(--ease), box-shadow .15s var(--ease);
  padding: 15px 20px;
}
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.45; cursor: default; pointer-events: none; }
.btn-block { width: 100%; }

/* Gleicher Verlauf wie .intro-icon (Value-Proposition-Screens) — bewusst
   dieselben, bereits etablierten --color-primary-dark/-primary/-bright-
   Stufen wiederverwendet statt eines neuen Farbtons. */
.btn-primary { background: var(--gradient-action); color: #FFFFFF; box-shadow: var(--shadow-1); }
.btn-primary:active { background: var(--color-primary-dark); box-shadow: none; }
.btn-primary:disabled { background: var(--color-border-strong); color: var(--color-text-tertiary); cursor: not-allowed; box-shadow: none; }

.btn-secondary { background: var(--color-surface); color: var(--color-text-primary); border: 1px solid var(--color-border-strong); }

.btn-ghost { background: transparent; color: var(--color-primary); padding: 10px 6px; }

/* Einzige Stelle, an der --color-danger als Button-Hintergrund dient — reserviert
   für echte, irreversible Aktionen (Account löschen), nicht für Abo-Kündigung
   oder andere reversible Schritte, die weiterhin btn-secondary nutzen. */
.btn-danger { background: var(--color-danger); color: #FFFFFF; }
.btn-danger:active { background: #8F4130; }

/* Premium-Button bewusst eine Nuance heller/lebendiger als .btn-primary
   (primary-bright statt #395C82 als Gradient-Start) statt orange — bleibt
   innerhalb der Blau-Familie, aber optisch als eigener Akzent erkennbar. */
.btn-premium { background: var(--gradient-action); color: #FFFFFF; box-shadow: var(--shadow-1); }
.btn-premium:active { background: var(--color-primary-dark); box-shadow: none; }

.btn-sm { padding: 9px 16px; font-size: var(--text-sm); }

/* ---------- Forms ---------- */

.field { margin-bottom: 18px; }
.field label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-primary);
  margin-bottom: 7px;
}
.field .hint { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 5px; }
.field .field-error { font-size: var(--text-xs); color: var(--color-danger); margin-top: 6px; }
/* Fehlermeldungen ausserhalb eines .field (z. B. über einem Speichern-Knopf:
   #setup-save-error, #post-form-error, #welcome-error) - gleiche Optik. */
.field-error { font-size: var(--text-xs); color: var(--color-danger); line-height: 1.45; }

/* Nur für Screenreader sichtbar (z. B. "Beiträge werden geladen"). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---------- EULA-Zustimmung (Registrierung, view-auth) ---------- */

.eula-consent-row { display: flex; align-items: flex-start; gap: 10px; margin-top: 16px; margin-bottom: 14px; }
.eula-consent-text { font-size: var(--text-sm); line-height: 1.5; color: var(--color-text-secondary); }
.eula-link { color: var(--color-primary); font-weight: 600; cursor: pointer; }
#eula-error { margin-top: 8px; margin-bottom: 4px; }

/* ---------- Rechtstext-Seiten (Einstellungen -> Rechtliches & Support) ---------- */

.legal-doc-meta { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-bottom: 18px; }
.legal-doc-heading { font-size: var(--text-md); font-weight: 700; color: var(--color-text-primary); margin: 22px 0 8px; }
.legal-doc p { font-size: var(--text-sm); line-height: 1.6; color: var(--color-text-secondary); margin-bottom: 10px; }
.legal-doc ul { padding-left: 18px; margin: 2px 0 10px; }
.legal-doc li { font-size: var(--text-sm); line-height: 1.6; color: var(--color-text-secondary); margin-bottom: 4px; }

.input, .textarea, .select {
  width: 100%;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  color: var(--color-text-primary);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 13px 14px;
  box-shadow: var(--shadow-1);
  transition: border-color .15s var(--ease);
}
/* Nur lesbares Feld (Block 3, A19: E-Mail-Adresse in den Einstellungen). */
/* Geburtsdatum als drei Felder Tag, Monat, Jahr (Block 5, F29). */
.birthdate-fields { display: grid; grid-template-columns: 1fr 1fr 1.6fr; gap: 10px; }
.birthdate-fields .input { text-align: center; }
.input:disabled { background: var(--color-bg); color: var(--color-text-secondary); cursor: default; -webkit-text-fill-color: var(--color-text-secondary); opacity: 1; }

.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--color-primary);
}
.textarea { resize: none; min-height: 84px; line-height: 1.5; }

.progress-steps {
  display: flex;
  gap: 6px;
  padding: 4px 20px 0;
}
.progress-steps .seg {
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: var(--color-border);
}
.progress-steps .seg.done { background: var(--color-primary); }

/* Chips */
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 9px 15px;
  border-radius: var(--radius);
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: all .15s var(--ease);
}
.chip.active {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
  color: var(--color-primary);
}
.chip:active { transform: scale(0.96); }

/* Ansichts-Umschalter zwischen 2-3 Listenansichten (Aktiv/Archiviert,
   Alle/Offen/Noch nicht eröffnet) — reiner Unterstrich statt Box. Bewusst
   eigene Klasse statt .chip: hier wird zwischen Ansichten gewechselt, nicht
   wie bei .chip ein Tag ausgewählt/mehrfach umgeschaltet — die ruhigere,
   rahmenlose Form passt besser zu einem simplen Umschalter (Nutzerwunsch:
   "cleaner, moderner, hochwertiger" als die bisherigen Chips). */
.tab-switch { display: flex; gap: 22px; border-bottom: 1px solid var(--color-border); padding: 0 20px; margin-bottom: 8px; }
.tab-switch-item {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-tertiary);
  padding: 0 0 12px;
  cursor: pointer;
  position: relative;
  transition: color .15s var(--ease);
}
.tab-switch-item.active { color: var(--color-primary); font-weight: 700; }
.tab-switch-item.active::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; background: var(--color-primary); border-radius: 2px;
}

/* Interests combobox: type to search a large suggestion list, or add custom text */
.combo-wrap { position: relative; }
.combo-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 9px; }
.combo-tags:empty { margin-bottom: 0; }
.combo-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-sm); font-weight: 600;
  background: var(--color-primary-soft); color: var(--color-primary);
  padding: 6px 8px 6px 12px; border-radius: var(--radius);
}
.combo-tag button {
  border: none; background: none; color: var(--color-primary); cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 2px;
  opacity: .65;
}
.combo-suggestions {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  max-height: 190px; overflow-y: auto;
  z-index: 5;
  display: none;
}
.combo-suggestions.open { display: block; }
.combo-suggestion {
  padding: 11px 14px; font-size: var(--text-sm); cursor: pointer;
  display: flex; align-items: center; gap: 8px;
}
.combo-suggestion:hover, .combo-suggestion.highlight { background: var(--color-primary-soft); }
.combo-suggestion.custom { color: var(--color-primary); font-weight: 600; border-top: 1px solid var(--color-border); }

/* Foto-Anhang im Beitrags-Formular — dezentes Icon + Text, wie die übrigen
   Formularaktionen (kein grosses Upload-Feld). */
.post-photos-row {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer;
  margin-bottom: 4px;
}
.post-photos-hint { font-size: var(--text-sm); font-weight: 600; color: var(--color-primary); }
.post-photos-preview {
  display: flex; gap: 8px; flex-wrap: wrap;
  margin-top: 10px;
}
.post-photos-preview:empty { margin-top: 0; }
.post-photo-thumb {
  position: relative;
  width: 64px; height: 64px;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.post-photo-thumb {
  background: var(--color-border);
  cursor: grab;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  transition: transform .18s ease;
}
.post-photo-thumb img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; }
/* Sortieren durch Gedrückthalten und Ziehen (D-2, Entscheid 3 neu): die
   gezogene Kachel liegt angehoben über den anderen und folgt dem Zeiger ohne
   Verzögerung, die anderen rücken mit einem kurzen Übergang nach. */
.post-photos-preview.sorting { cursor: grabbing; }
.post-photo-thumb.lifted {
  z-index: 2;
  cursor: grabbing;
  box-shadow: var(--shadow-3);
  transition: box-shadow .15s ease;
}
/* Scheitert der Upload eines Fotos, bleibt die Kachel stehen und bekommt
   einen roten Rand (D-2, alles oder nichts). */
.post-photo-thumb.failed { box-shadow: 0 0 0 2px var(--color-danger); }
.post-photo-thumb .thumb-remove {
  position: absolute; top: 3px; right: 3px;
  width: 18px; height: 18px; border-radius: 50%;
  background: rgba(26,36,51,0.65); color: #fff;
  border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}

/* Die Pfeile "Nach vorne"/"Nach hinten" sind nur noch für Screenreader und
   Tastatur da (Klasse .sr-only an .thumb-move-row, D-2, Entscheid 3 neu).
   Beim Fokus über die Tastatur werden sie unten in der Kachel sichtbar, für
   Maus und Touch bleiben sie unsichtbar. */
.thumb-move-row:has(:focus-visible) {
  position: absolute; left: 3px; right: 3px; bottom: 3px;
  width: auto; height: auto; margin: 0;
  overflow: visible; clip: auto; white-space: normal;
  display: flex; justify-content: space-between;
}
.thumb-move {
  width: 18px; height: 18px; border-radius: 50%; padding: 0;
  background: rgba(26,36,51,0.65); color: #fff;
  border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.thumb-move:disabled { opacity: .3; cursor: default; }
.thumb-move:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.post-photos-sort-hint { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 8px; line-height: 1.45; }
.post-photos-sub { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 2px; line-height: 1.45; }
.post-photos-notice { font-size: var(--text-xs); color: var(--color-text-secondary); margin-top: 8px; line-height: 1.45; }
.post-photos-field .post-photos-error { margin-top: 8px; }

/* Beitragsbilder: ein Foto füllt die Breite, mehrere scrollen horizontal.
   Jedes Foto liegt in einer grauen Fläche, bis die signierte Adresse da ist
   (Resolver, D-2); ein nicht ladbares Foto bleibt still grau. */
.post-photos {
  display: flex; gap: 6px;
  margin: 8px 0;
  border-radius: var(--radius);
  overflow: hidden;
}
.post-photo {
  position: relative;
  flex: 1 1 auto; height: 220px;
  background: var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
}
.post-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.post-photos.multi { overflow-x: auto; border-radius: 0; padding-bottom: 6px; }
/* Schmaler, dezenter Scrollbalken statt des Windows-Balkens mit Pfeilen;
   bleibt sichtbar, damit man am Desktop die weiteren Fotos bemerkt
   (Nutzerentscheid 27.09.2026). */
.post-photos.multi::-webkit-scrollbar { height: 4px; }
.post-photos.multi::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
.post-photos.multi::-webkit-scrollbar-track { background: transparent; }
.post-photos.multi::-webkit-scrollbar-thumb { background: var(--color-text-tertiary); border-radius: 2px; }
.post-photos.multi .post-photo { flex: 0 0 130px; width: 130px; height: 130px; }
.post-photos.compact { margin: 6px 0 2px; }
.post-photos.compact .post-photo { flex: 0 0 56px; width: 56px; height: 56px; }

/* Vollbild (T14): dunkle Fläche, Foto ganz, kein Zoom. */
.photo-viewer { background: #0E1520; }
.photo-viewer .status-bar { color: rgba(255,255,255,.85); }
.photo-viewer-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 12px;
}
.photo-viewer-header .icon-btn,
.photo-viewer-nav .icon-btn { color: #fff; background: rgba(255,255,255,.12); }
.photo-viewer-nav .icon-btn:disabled { opacity: .3; }
.photo-viewer-counter { color: #fff; font-size: var(--text-sm); font-weight: 600; }
.photo-viewer-spacer { width: 32px; }
.photo-viewer-stage {
  flex: 1; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 0 12px;
}
.photo-viewer-frame {
  width: 100%; min-height: 200px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.06);
  border-radius: var(--radius);
  overflow: hidden;
}
.photo-viewer-frame img { width: 100%; height: auto; max-height: 560px; object-fit: contain; display: block; }
.photo-viewer-nav { display: flex; justify-content: space-between; padding: 12px 20px calc(28px + var(--safe-bottom)); }

/* Angezeigt, sobald seit dem letzten Besuch neue Kommentare dazugekommen sind */
.new-comment-note {
  font-size: var(--text-xs); font-weight: 600; color: var(--color-primary);
  margin-top: 6px;
}

/* Photo upload — stays circular, it previews a face/photo */
.photo-upload {
  display: flex; align-items: center; gap: 14px;
  margin-bottom: 22px;
}
.photo-circle {
  width: 64px; height: 64px; border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-bright));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; flex-shrink: 0; cursor: pointer;
  box-shadow: 0 0 0 2px var(--color-surface), var(--shadow-1);
}
.photo-circle img { width: 100%; height: 100%; object-fit: cover; }
.photo-upload .txt { font-size: var(--text-sm); }
.photo-upload .link { color: var(--color-primary); font-weight: 600; cursor: pointer; }
.photo-upload .hint { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 2px; }
/* Profilbild (6b, D-1): Entfernen wirkt erst beim Speichern, deshalb kein
   Rot (das bleibt echten destruktiven Aktionen vorbehalten), sondern ein
   ruhiger Textlink wie die übrigen Nebenaktionen. */
.photo-remove-link {
  display: block; background: none; border: none; padding: 0; margin-top: 6px;
  font-family: var(--font-sans); font-size: var(--text-xs); font-weight: 600;
  color: var(--color-text-secondary); cursor: pointer; text-align: left;
}
.photo-remove-link:active { opacity: .7; }
/* Entwurf wiederhergestellt (Block 5, P6): ruhige Zeile oben im Formular,
   „Verwerfen“ als Textlink wie die übrigen Nebenaktionen. */
.post-draft-notice {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px;
  margin-bottom: 18px; padding: 10px 12px; border-radius: var(--radius);
  background: var(--color-primary-bright-soft);
  font-size: var(--text-sm); line-height: 1.5; color: var(--color-text-secondary);
}
.post-draft-discard {
  background: none; border: none; padding: 0;
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: 600;
  color: var(--color-primary); cursor: pointer;
}
.post-draft-discard:active { opacity: .7; }
.photo-upload .photo-error { margin-top: 6px; }

/* ---------- Cards ---------- */

.card {
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 18px;
}

.post-card { margin: 0 20px 10px; padding: 12px 14px; cursor: pointer; transition: transform .12s var(--ease), box-shadow .12s var(--ease); border-left: 3px solid transparent; }
.post-card:active { transform: scale(0.985); }

/* The left bar is reserved exclusively for unread status — it no longer
   doubles as a post-type marker, to avoid "new milestone" reading as
   "unread" or vice versa. Milestones stay identifiable via their tag only. */
.post-card.unread { border-left-color: var(--color-text-primary); }

/* Ladezustand des Feeds: Karten in Beitragsform mit grauen Platzhalter-
   Balken statt eines leeren Feeds (der kurz wie "Noch keine Beiträge"
   aussähe). Dezentes Pulsieren, bei reduzierter Bewegung statisch. */
.post-card.skeleton { cursor: default; display: flex; flex-direction: column; }
.post-card.skeleton:active { transform: none; }
.sk-line, .sk-avatar {
  display: block; background: var(--color-border);
  animation: pulse 1.4s ease-in-out infinite;
}
.sk-line { height: 10px; border-radius: var(--radius); }
.sk-avatar { width: 24px; height: 24px; border-radius: var(--radius-pill); flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) {
  .sk-line, .sk-avatar { animation: none; }
}

.post-author {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

/* Avatars: gradient fill (4 on-brand duotone variants, assigned per person)
   instead of one flat color for everyone — plus a thin halo ring so they
   read as a distinct layer against the page. No progress ring — that's
   the gamification line we don't cross. */
.avatar {
  width: 32px; height: 32px;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-bright));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 11.5px; font-weight: 700;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow: 0 0 0 2px var(--color-surface);
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar.lg { width: 64px; height: 64px; font-size: 20px; box-shadow: 0 0 0 3px var(--color-surface), var(--shadow-1); }
.post-author .avatar { width: 24px; height: 24px; font-size: 9px; }

/* 4 Avatar-Varianten rein aus der Blau-Familie (keine Orange-Ausnahme mehr):
   zwei lebendige Blau-Verläufe unterschiedlicher Tiefe (g1/g2) und zwei
   gedeckte Blaugrau-Verläufe (g3/g4) — bleiben trotzdem klar unterscheidbar. */
.avatar.g1 { background: linear-gradient(135deg, #2B4A6F, #4A7FB5); }
.avatar.g2 { background: linear-gradient(135deg, #1E3450, #4A7FB5); }
.avatar.g3 { background: linear-gradient(135deg, #3A4A5C, #6B7A8C); }
.avatar.g4 { background: linear-gradient(135deg, #2B4A6F, #6B7A8C); }

.post-author .name { font-size: var(--text-sm); font-weight: 700; line-height: 1.25; }

/* Jedes Avatar/Name-Element, das ein Profil repräsentiert, egal wo (Feed,
   Kommentare, Nachrichten, Neuigkeiten) — reine Cursor-Regel, die eigentliche
   Klick-Delegation läuft zentral über [data-open-profile] in app.js. */
[data-open-profile] { cursor: pointer; }
.post-author .meta { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; }

/* Post-type tag: alle sieben Beitragstypen (die fünf normalen plus Meilenstein/
   Vorstellung) teilen sich jetzt dieselbe flache Text+Icon-Familie, keine
   Fläche/Pille mehr — bewusst die konsistenteste von drei geprüften Varianten
   (nur Text/sanfte Chip-Fläche/flache Pille), auch wenn Meilenstein/Vorstellung
   dadurch etwas weniger "Pop" haben als die frühere Farbverlaufs-Pille. Jeder
   Typ bekommt seine eigene, gedeckte Farbe (--color-type-* im :root oben) —
   nur das Label selbst, die Karte drumherum bleibt in jedem Fall neutral. */
.type-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--color-text-tertiary);
  margin-bottom: 6px;
}
.type-tag.frage { color: var(--color-type-frage); }
.type-tag.erfahrung { color: var(--color-type-erfahrung); }
.type-tag.erfolg { color: var(--color-type-erfolg); }
.type-tag.rueckschlag { color: var(--color-type-rueckschlag); }
.type-tag.diskussion { color: var(--color-type-diskussion); }
/* Milestone bewusst dunkler/gedeckter als Intro (primary-dark statt
   primary-bright), damit beide trotz gemeinsamer Blau-Familie klar
   auseinanderzuhalten bleiben — dieselbe Unterscheidung wie zu Pillen-Zeiten,
   nur jetzt als Textfarbe statt Flächenfarbe. */
.type-tag.milestone { color: var(--color-primary-dark); }
.type-tag.intro { color: var(--color-primary-bright); }

.header-actions { display: flex; align-items: center; gap: 2px; }

/* Kompaktes Dropdown-Menü — trägt sowohl die Feed-Sortierung (Einfachauswahl,
   Icon je Option) als auch den Mitglieder-Filter (Mehrfachauswahl, Checkbox je Option).
   Scharfe Ecken statt der stärker gerundeten Vorlage, passend zu unserem Radius-System. */
.menu-trigger-wrap { position: relative; }
.menu-panel {
  position: absolute; top: calc(100% + 8px); right: 0;
  min-width: 172px;
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  padding: 6px;
  z-index: 30;
  display: none;
}
.menu-panel.open { display: block; }
.menu-panel.left { left: 0; right: auto; }
/* Am unteren Rand nach oben (placeMenuPanel() in app.js). */
.menu-panel.up { top: auto; bottom: calc(100% + 8px); }

/* Community-Titel als Dropdown-Trigger — sieht wie der normale Screen-Titel
   aus, ist aber klickbar und dreht den Chevron beim Öffnen. */
.screen-title-trigger {
  display: flex; align-items: center; gap: 5px;
  background: none; border: none;
  padding: 4px 6px 4px 2px; margin: -4px 0 -4px -2px;
  border-radius: var(--radius);
  cursor: pointer;
}
.screen-title-trigger:active { background: var(--color-primary-soft); }
.screen-title-trigger .screen-title { margin: 0; }
.switch-chevron { color: var(--color-text-tertiary); flex-shrink: 0; transition: transform .15s var(--ease); }
.menu-trigger-wrap.open .switch-chevron { transform: rotate(180deg); }
.menu-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 10px 10px;
  border-radius: var(--radius);
  border: none;
  background: none;
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: 600; color: var(--color-text-primary);
  text-align: left;
  cursor: pointer;
}
.menu-item:active { transform: scale(0.98); }
.menu-item.active { background: var(--color-primary-soft); color: var(--color-primary); }
.menu-item-icon { display: flex; align-items: center; justify-content: center; color: var(--color-text-tertiary); flex-shrink: 0; }
.menu-item.active .menu-item-icon { color: var(--color-primary); }
/* Einheitlicher, etwas grösserer Icon-Slot nur im Community-Wechsler (nicht global
   für .menu-item-icon), da hier das echte Logo (22px, siehe generalCommunityIcon in
   app.js) neben den 16px-SVG-Kategorien-Icons steht — ein fester Slot hält alle
   Zeilen gleich hoch, statt nur die Zeile "Allgemeine Community" zu vergrössern.
   Chip-Hintergrund (blassblau, wie .list-row .ic) als Basis für alle Zeilen dieses
   Dropdowns — Themen-Communitys überschreiben ihn per Inline-Style mit ihrer
   Kategorienfarbe (communityIconTintStyle() in app.js), "Allgemeine Community" und
   "Communitys entdecken" behalten den neutralen Chip. So bleibt die ganze Liste ein
   einheitliches Chip-System statt farbiger Quadrate neben nackten Icons. */
#community-switch-menu .menu-item-icon {
  width: 22px; height: 22px; border-radius: var(--radius);
  background: var(--color-primary-soft); color: var(--color-primary);
}
.menu-item-count { margin-left: auto; font-size: var(--text-2xs); font-weight: 500; color: var(--color-text-tertiary); }
.menu-item.active .menu-item-count { color: var(--color-primary); }
.menu-footer-link {
  display: block; width: 100%; text-align: center;
  padding: 9px; margin-top: 2px;
  font-size: var(--text-xs); font-weight: 600; color: var(--color-primary);
  background: none; border: none; cursor: pointer;
  border-top: 1px solid var(--color-border);
}

/* Kleiner Aktiv-Punkt auf dem Trigger-Icon, wenn ein Filter angewendet ist */
.menu-dot {
  position: absolute; top: 6px; right: 6px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--color-primary-bright);
  border: 1.5px solid var(--color-bg);
}

/* Angepinnte Admin-Nachricht — scrollt mit dem Feed, sitzt aber immer zuoberst.
   Blauer Rand statt Terracotta: das ist ein System-Hinweis, kein Meilenstein. */
.pinned-card {
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  margin: 14px 20px 10px;
  border-left: 3px solid var(--color-primary);
  overflow: hidden;
}
.pinned-header {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 14px;
  cursor: pointer;
}
/* Gefülltes statt umrissenes Icon (voller --color-primary-Fond, weisses
   Icon) plus farbiges statt graues Label — zusammen mit dem schon
   bestehenden blauen Rand links das zusätzliche Gewicht, das eine
   offizielle Team-Nachricht von einer normalen Liste unterscheidet. */
.pinned-icon {
  width: 32px; height: 32px; border-radius: var(--radius);
  background: var(--gradient-action); color: #fff;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.pinned-title-wrap { flex: 1; min-width: 0; }
.pinned-label {
  font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-primary);
}
.pinned-title { font-size: var(--text-sm); font-weight: 700; color: var(--color-text-primary); margin-top: 2px; }
.pinned-chevron { color: var(--color-text-tertiary); flex-shrink: 0; transition: transform .15s var(--ease); }
.pinned-card.open .pinned-chevron { transform: rotate(90deg); }
.pinned-body {
  display: none;
  padding: 0 14px 14px 56px;
  font-size: var(--text-sm); line-height: 1.55; color: var(--color-text-secondary);
}
.pinned-card.open .pinned-body { display: block; }

/* Composer bar — replaces the "+" nav button as the feed's entry point */
.composer-bar {
  display: flex; align-items: center; gap: 10px;
  margin: 2px 20px 16px;
  padding: 11px 14px;
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  cursor: pointer;
}
.composer-bar:active { box-shadow: none; }
.composer-placeholder { font-size: var(--text-sm); color: var(--color-text-tertiary); }

.post-title { font-size: var(--text-md); font-weight: 700; letter-spacing: -0.005em; line-height: 1.3; margin-bottom: 3px; color: var(--color-text-primary); }

.post-text {
  font-size: var(--text-sm); line-height: 1.48; color: var(--color-text-secondary); margin-bottom: 8px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.post-text.full { -webkit-line-clamp: unset; display: block; color: var(--color-text-primary); font-size: var(--text-md); line-height: 1.6; }

.post-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 1px;
}
.post-action {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--text-sm); font-weight: 600; color: var(--color-text-tertiary);
}
/* Unterstützen-Button: aktiver Zustand färbt Text und Herz-Icon in Primary statt
   eines eigenen Rot/Rosa — Unterstützung ist keine "Like"-Geste mit Signalfarbe,
   sondern bleibt in der bestehenden Blau-Palette. */
[data-support-toggle] { cursor: pointer; }
.post-action.supported { color: var(--color-primary); }
.post-action.supported svg { fill: currentColor; }

.avatar-stack { display: flex; align-items: center; margin-left: auto; }
.avatar-stack .mini {
  width: 20px; height: 20px; border-radius: 50%;
  color: #fff;
  font-size: 9px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--color-surface);
  margin-left: -6px;
  overflow: hidden;
}
.avatar-stack .mini img { width: 100%; height: 100%; object-fit: cover; }
.avatar-stack .mini:first-child { margin-left: 0; }
.avatar-stack .mini.g1 { background: linear-gradient(135deg, #2B4A6F, #4A7FB5); }
.avatar-stack .mini.g2 { background: linear-gradient(135deg, #1E3450, #4A7FB5); }
.avatar-stack .mini.g3 { background: linear-gradient(135deg, #3A4A5C, #6B7A8C); }
.avatar-stack .mini.g4 { background: linear-gradient(135deg, #2B4A6F, #6B7A8C); }

/* ---------- Bottom nav ---------- */

.bottom-nav {
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 10px 8px calc(var(--safe-bottom) + 10px);
  background: rgba(250,248,244,0.92);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--color-border);
  position: relative;
  z-index: 20;
}

.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  color: var(--color-text-tertiary);
  font-size: var(--text-2xs);
  font-weight: 600;
  cursor: pointer;
  padding: 6px 10px;
  transition: color .15s var(--ease), transform .1s var(--ease);
}
.nav-item:active { transform: scale(0.92); }
.nav-item.active { color: var(--color-primary); }
.nav-item svg { width: 24px; height: 24px; }

.nav-icon-wrap {
  position: relative;
  width: 36px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius);
  transition: background .15s var(--ease);
}
.nav-item.active .nav-icon-wrap { background: var(--color-primary-soft); }

.nav-avatar {
  width: 22px; height: 22px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  display: block;
}
.nav-avatar img { width: 100%; height: 100%; object-fit: cover; }

.nav-item.nav-create {
  color: var(--color-bg);
}
.nav-create-circle {
  width: 40px; height: 40px;
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, var(--color-primary-bright), var(--color-primary));
  box-shadow: var(--shadow-1);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 2px;
  transition: transform .12s var(--ease), background .15s var(--ease);
}
.nav-item.nav-create:active .nav-create-circle { transform: scale(0.9); background: var(--color-primary-dark); }

/* ---------- App shell tabs ---------- */

.tab-panel { display: none; flex: 1; flex-direction: column; overflow: hidden; }
.tab-panel.active { display: flex; }

/* ---------- Splash ---------- */

.splash {
  align-items: center;
  justify-content: center;
  background: var(--color-bg);
  cursor: pointer;
}
/* Eigener Statusbalken bleibt oben fixiert statt Teil des zentrierten
   Flex-Inhalts zu sein — sonst würde er das Logo aus der Bildschirmmitte
   verschieben. */
.splash .status-bar { position: absolute; top: 0; left: 0; right: 0; }
.splash-mark { display: flex; flex-direction: column; align-items: center; gap: 18px; }
.splash-mark img {
  width: 320px; height: 320px; object-fit: contain;
  animation: splash-zoom 1.9s cubic-bezier(.55,0,.85,.35) forwards;
}
.splash-tap-hint {
  position: absolute;
  bottom: calc(56px + var(--safe-bottom)); left: 0; right: 0;
  text-align: center;
  font-size: var(--text-xs);
  color: var(--color-text-tertiary);
  animation: pulse 2.2s ease-in-out infinite, splash-fade-out .5s ease-in .5s forwards;
}
@keyframes pulse { 0%,100% { opacity: .5; } 50% { opacity: 1; } }
/* Erst sichtbar, wenn feststeht, dass keine Sitzung besteht (Block 5, F15,
   restoreSessionOrShowSplash() setzt show-tap-hint). */
.splash:not(.show-tap-hint) .splash-tap-hint { display: none; }
/* Splash-Intro: das Logo hält kurz, dann "taucht" die Kamera hinein — Skalierung
   auf ein Vielfaches der Ausgangsgrösse bei gleichzeitigem Ausblenden, bevor
   automatisch zum nächsten Screen gewechselt wird (siehe splashTimer in app.js,
   Dauer dort synchron zu dieser Animation). .phone-screen hat overflow:hidden,
   die Vergrösserung bleibt also innerhalb des Telefonrahmens geclippt.
   Endskalierung bewusst auf 3.8 begrenzt statt höher: Logo.png ist 1254px
   breit, die Anzeigegrösse ist 320px (siehe .splash-mark img) — 320×3.8 ≈
   1216px bleibt knapp innerhalb der Quellauflösung, darüber würde das Bild
   sichtbar unscharf hochskaliert. */
@keyframes splash-zoom {
  0% { transform: scale(1); opacity: 1; }
  35% { transform: scale(1); opacity: 1; }
  100% { transform: scale(3.8); opacity: 0; }
}
@keyframes splash-fade-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ---------- Auth / Onboarding ---------- */

.auth-body, .setup-body, .welcome-body { padding: 8px 24px 28px; flex: 1; overflow-y: auto; }

.divider-row {
  display: flex; align-items: center; gap: 12px;
  margin: 22px 0;
  color: var(--color-text-tertiary);
  font-size: var(--text-xs);
}
.divider-row::before, .divider-row::after {
  content: ''; flex: 1; height: 1px; background: var(--color-border);
}

/* ---------- Value-Proposition-Screens (vor dem Login) ----------
   Bewusst eigene, von .progress-steps/.setup-body unabhängige Klassen
   (.intro-progress/.intro-body/.intro-title/.intro-text) statt die
   bestehenden Setup-Klassen zu verfeinern — sonst würde jede Anpassung
   hier automatisch den Profil-Erstellen-Screen mitverändern. */

.skip-link {
  background: none; border: none; padding: 6px 4px;
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: 500;
  color: var(--color-text-tertiary); cursor: pointer;
}
.skip-link:active { opacity: .7; }

.intro-progress { display: flex; gap: 6px; padding: 4px 20px 0; }
.intro-progress .seg {
  flex: 1; height: 4px; border-radius: 3px;
  background: var(--color-border);
  transition: background .35s var(--ease);
}
.intro-progress .seg.done { background: var(--color-primary); }

.intro-body { display: flex; flex-direction: column; justify-content: center; flex: 1; padding: 0 24px; }
.intro-step { text-align: center; }

/* Einzige Stelle in der App, an der ein Icon-Container einen eigenen,
   navy-getönten Schatten statt des neutralen --shadow-1/2/3 bekommt — die
   Kachel ist hier fast der ganze Bildschirminhalt, verdient dadurch mehr
   Bühne als die sonst üblichen 32-52px-Icon-Container. */
.intro-icon {
  width: 88px; height: 88px; border-radius: 22px;
  background: var(--gradient-action);
  box-shadow: 0 16px 32px -12px rgba(30,52,80,.45), 0 6px 14px rgba(30,52,80,.2);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 32px;
}

.intro-title {
  font-size: var(--text-2xl); font-weight: 800; letter-spacing: -.02em; line-height: 1.2;
  color: var(--color-text-primary);
  margin-bottom: 12px;
  text-wrap: balance;
}
.intro-text {
  font-size: var(--text-md); line-height: 1.65;
  color: var(--color-text-secondary);
  max-width: 230px; margin: 0 auto;
}

/* Gestaffeltes, leichtes Einblenden beim Schritt-Wechsel (goToIntroStep() in
   app.js setzt .enter auf den jeweils neu aktiven Schritt). Rein additiv zur
   bestehenden display:none/block-Logik, kein Überlappen zweier Schritte
   nötig. */
.intro-step.enter .intro-icon,
.intro-step.enter .intro-title,
.intro-step.enter .intro-text {
  opacity: 0; transform: translateY(8px);
  animation: introRise .5s var(--ease) forwards;
}
.intro-step.enter .intro-title { animation-delay: .08s; }
.intro-step.enter .intro-text { animation-delay: .14s; }
@keyframes introRise { to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .intro-step.enter .intro-icon,
  .intro-step.enter .intro-title,
  .intro-step.enter .intro-text { animation: none; opacity: 1; transform: none; }
}

.social-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%;
  background: var(--color-surface);
  border: none;
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 13px;
  font-size: var(--text-md); font-weight: 600;
  margin-bottom: 10px;
  cursor: pointer;
}
.social-btn:active { transform: scale(0.98); }

/* Apple/Google nebeneinander statt übereinander — gleiche Breite, gleiches
   Gewicht, keine Option optisch bevorzugt (Nutzervorgabe). */
.social-btn-row { display: flex; gap: 10px; margin-bottom: 10px; }
.social-btn-row .social-btn { width: auto; flex: 1; margin-bottom: 0; }

/* ---------- Welcome ---------- */

/* Eine einzige Karte für alle Frage/Antwort-Blöcke des Vorstellungsbeitrags,
   wie bei den übrigen Beitragstypen (.post-text.full) — vorher eine eigene
   Karte pro Frage, wirkte wie mehrere separate Beiträge statt einem. */
.template-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 18px;
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 16px 18px;
  position: relative;
}
/* Veröffentlichter Vorstellungsbeitrag (Beitrag-Detailansicht): kein eigener
   Kasten mehr, der Inhalt liegt direkt auf dem weissen Seitenhintergrund wie
   bei jedem anderen Beitragstyp (.post-text.full) — .template-card bleibt nur
   für das noch auszufüllende Formular (#welcome-template) reserviert. */
.intro-post-qa { display: flex; flex-direction: column; gap: 14px; margin-top: 14px; margin-bottom: 14px; }
.template-q {}
.template-q .label { display: flex; align-items: center; gap: 7px; font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-tertiary); margin-bottom: 6px; }
.template-q .from-profile { color: var(--color-primary); }
.template-q .val { font-size: var(--text-sm); color: var(--color-text-primary); line-height: 1.45; }
.template-q .val.empty { color: var(--color-text-tertiary); font-style: italic; }
/* Dezent von den reinen Vorschau-Werten (.val, kein Hintergrund, padding:0)
   abgesetzt — ein leiser Flächenton statt eines Rahmens, damit klar wird,
   dass dies das einzige tatsächlich editierbare Feld ist, ohne die
   rahmenlose Optik der Karte aufzugeben. */
.template-q textarea {
  width: 100%; border: none; outline: none; resize: none;
  font-family: var(--font-sans); font-size: var(--text-sm); line-height: 1.5;
  color: var(--color-text-primary); background: var(--color-bg); min-height: 44px;
  padding: 10px 12px; border-radius: var(--radius);
}

/* Einziges Feld im Vorstellungsbeitrag, das noch aktiv ausgefüllt werden
   muss (die anderen drei zeigen nur vorbefüllte Profildaten an) — nur das
   fett gesetzte Label hebt das hervor, kein zusätzlicher Rahmen (auf
   Nutzerwunsch entfernt, wirkte neben den rahmenlosen Feldern unruhig). */
.template-q .label.label-support { color: var(--color-text-primary); font-weight: 800; }

/* ---------- Search / filters ---------- */

.search-bar {
  display: flex; align-items: center; gap: 8px;
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 11px 14px;
  margin: 4px 20px 14px;
  color: var(--color-text-tertiary);
}
.search-bar input {
  border: none; outline: none; background: none;
  font-family: var(--font-sans); font-size: var(--text-md); color: var(--color-text-primary);
  flex: 1;
}

/* ---------- Avatar status dot ---------- */

.avatar-wrap { position: relative; flex-shrink: 0; }
.status-dot {
  position: absolute; right: -1px; bottom: -1px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--color-success); border: 2px solid var(--color-bg);
}
.status-dot.offline { background: var(--color-border-strong); }
.avatar-wrap.lg .status-dot { width: 14px; height: 14px; border-width: 3px; }

/* ---------- Member list (clean divided rows, not cards) ---------- */

.member-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 20px;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer;
}
.member-row:active { background: var(--color-primary-soft); }
.member-row .info { flex: 1; min-width: 0; }
.member-row .name-row { display: flex; align-items: center; gap: 7px; }
.member-row .name { font-size: var(--text-sm); font-weight: 700; }
.member-row .sub { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 1px; font-weight: 500; }
.member-row .tag {
  font-size: var(--text-2xs); font-weight: 600;
  color: var(--color-primary);
  background: none;
  padding: 0;
  flex-shrink: 0;
  align-self: center;
  text-align: right;
}

/* ---------- Conversation list (Nachrichten-Übersicht) ---------- */

.conversation-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 20px;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer;
}
.conversation-row:active { background: var(--color-primary-soft); }
.conversation-row .info { flex: 1; min-width: 0; }
.conversation-row .top-row { display: flex; align-items: center; gap: 7px; }
.conversation-row .name { font-size: var(--text-sm); font-weight: 700; flex: 1; min-width: 0; }
.conversation-row .time { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; flex-shrink: 0; }
.conversation-row .preview {
  font-size: var(--text-sm); color: var(--color-text-secondary); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.conversation-row.unread .name, .conversation-row.unread .preview { color: var(--color-text-primary); font-weight: 700; }
.conversation-row .unread-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--color-primary); flex-shrink: 0; margin-top: 4px; }
/* Etwas kompakter als der normale .btn-sm — sonst wirkt die Anfragen-Zeile
   deutlich höher als eine gewöhnliche Konversationszeile ohne Aktionen. */
.request-row-actions { display: flex; gap: 8px; margin-top: 8px; }
/* Eingehende Anfrage: der Text ist vorab lesbar (M1), höchstens zwei Zeilen. */
.conversation-row .preview.request-text {
  white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.conversation-row.skeleton { cursor: default; }
/* Zahl am Nachrichten-Symbol (gleiche Optik wie bei den Neuigkeiten). */
#btn-open-messages { position: relative; }
#btn-open-messages .nav-count-badge { top: -3px; right: -4px; }
.request-row-actions .btn { flex: 1; padding: 7px 14px; font-size: var(--text-xs); }

/* ---------- Profile screen ---------- */

.profile-hero {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 20px 20px 18px;
  border-bottom: 1px solid var(--color-border);
}
.profile-hero .info { flex: 1; min-width: 0; padding-top: 2px; }
.profile-hero .name { font-family: var(--font-sans); font-weight: 800; letter-spacing: -0.01em; font-size: var(--text-lg); line-height: 1.2; }
.profile-hero .handle { font-size: var(--text-sm); color: var(--color-text-tertiary); margin-top: 1px; font-weight: 500; }
.profile-hero .region { font-size: var(--text-sm); color: var(--color-text-tertiary); margin-top: 2px; font-weight: 500; }
.profile-hero .status-row { display: flex; align-items: center; gap: 6px; font-size: var(--text-xs); color: var(--color-text-secondary); margin-top: 8px; font-weight: 500; }
.profile-hero .status-row .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-success); }
.profile-hero .status-row .dot.offline { background: var(--color-border-strong); }
.profile-hero .joined { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 3px; font-weight: 500; }
.profile-hero .bio { font-size: var(--text-sm); color: var(--color-text-secondary); margin-top: 10px; line-height: 1.55; }

/* Eigenes Profil: vertikaler, zentrierter Header wie bei Skool — anders als
   die horizontale Zeile für fremde Profile (profileBodyHtml, unverändert). */
.profile-hero.compact {
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 22px 20px 16px;
  gap: 0;
}
.profile-hero.compact .avatar.lg { width: 60px; height: 60px; font-size: 17px; }
.profile-hero.compact .info { width: 100%; padding-top: 0; }
.profile-hero.compact .name { font-size: var(--text-lg); margin-top: 12px; }
.profile-hero.compact .bio { text-align: center; }
.profile-hero.compact .btn { width: 100%; margin-top: 10px; }
.profile-hero.compact .stat-row { justify-content: center; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--color-border); }
.profile-hero.compact .link-row { justify-content: center; }

/* Zwei separate Statuszeilen (Online / Beigetreten) statt der zusammengefassten
   Ein-Zeilen-Fassung — bewusst wieder wie bei Skool getrennt. */
.status-lines { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-top: 12px; }
.status-line { display: flex; align-items: center; gap: 6px; font-size: var(--text-xs); color: var(--color-text-secondary); font-weight: 500; }
.status-line .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-success); flex-shrink: 0; }
.status-line svg { color: var(--color-text-tertiary); flex-shrink: 0; }

/* Goal-progress ring around the avatar — replaces Skool's level ring.
   Thin, single-color arc; no ring at all when there are no active goals.
   Rendered first in the DOM so the avatar paints over its center, leaving
   only the ring's edge visible around the photo. */
/* width/height:100% zusätzlich zu inset nötig — <svg> ist ein Replaced
   Element, dessen Grösse ohne explizite width/height browserabhängig
   (Chrome Desktop vs. mobile WebKit) leicht von der durch inset vorgegebenen
   Box abweichen kann. Eine dadurch nicht exakt quadratische Box verschiebt
   sich durch transform:rotate(-90deg) sichtbar — bei einem echten Quadrat
   hätte die Drehung keinen sichtbaren Effekt. Bug trat nur auf kleineren/
   mobilen Viewports auf, genau dieses Muster bestätigt die Ursache. */
.goal-ring { position: absolute; inset: -7px; width: calc(100% + 14px); height: calc(100% + 14px); transform: rotate(-90deg); }
.goal-ring .track { fill: none; stroke: var(--color-border); stroke-width: 3; }
.goal-ring .fill { fill: none; stroke: var(--color-primary); stroke-width: 3; stroke-linecap: round; transition: stroke-dashoffset .3s var(--ease); }

.stat-row { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
.stat-row .stat { font-size: var(--text-sm); color: var(--color-text-secondary); font-weight: 500; }
.stat-row .stat .n { font-weight: 800; color: var(--color-text-primary); }
.stat-row .stat-divider { width: 1px; height: 12px; background: var(--color-border); }
.stat-row .stat.tappable { cursor: pointer; }
.stat-row .stat.tappable:active { opacity: 0.6; }

.link-row { display: flex; align-items: center; gap: 14px; margin-top: 10px; }
.link-row a, .link-row span { display: flex; align-items: center; gap: 5px; font-size: var(--text-xs); font-weight: 600; color: var(--color-text-secondary); text-decoration: none; }

.section-label {
  font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--color-text-tertiary);
  padding: 20px 20px 10px;
}

/* Section-Label mit Aktion rechts (z. B. Sortierung "Beiträge") — gleicher
   Trigger-Mechanismus wie die Feed-Sortierung, nur innerhalb einer Sektion. */
.section-label-row { display: flex; align-items: center; justify-content: space-between; padding-right: 12px; }
.section-label-row .icon-btn { width: 32px; height: 32px; }

.goal-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 20px; font-size: var(--text-sm); color: var(--color-text-primary); }
.goal-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-primary); margin-top: 6px; flex-shrink: 0; }

/* Gemeinsame Basis für die vier "abgerundete Quadrat"-Status-/Auswahl-Marker
   der App (Ziel-Checkbox, Premium-Wahl-Häkchen, Feature-Punkt, Freischalt-
   Punkt in "Eröffnung unterstützen") — vorher vier separate, wortgleich
   duplizierte Deklarationsblöcke mit demselben Farbverlauf/derselben Optik,
   nur unterschiedlicher Pixelgrösse. Klassennamen und Zustandslogik in
   app.js bleiben unverändert, nur die gemeinsamen Eigenschaften sind jetzt
   an einer Stelle gepflegt (31.08.2026, Design-Systematisierung). */
.goal-check-box, .premium-choice-check, .feature-item .dot, .interest-unlock-dot {
  border-radius: 5px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: #fff;
}
/* Shared small checkbox — used for each Ziel inside a goal-theme. */
.goal-check-box {
  width: 19px; height: 19px;
  border: 1.5px solid var(--color-border-strong);
  transition: all .15s var(--ease);
}
.goal-check-box.done { background: linear-gradient(135deg, var(--color-primary-bright), var(--color-primary)); border-color: var(--color-primary); }
.goal-check-text { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--color-text-primary); }
.goal-check-text.done { color: var(--color-text-tertiary); text-decoration: line-through; }
.goal-check-text.editable { cursor: text; }

.goal-group-label-row { display: flex; align-items: center; gap: 8px; padding: 20px 20px 10px; }
.goal-group-label {
  flex: 1; min-width: 0;
  font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--color-text-tertiary);
}
.goal-group-label.editable { cursor: text; }
.goal-theme-count { font-size: var(--text-2xs); font-weight: 700; color: var(--color-text-tertiary); flex-shrink: 0; }

/* Kleines ×, um ein Ziel oder ein ganzes Thema wieder zu entfernen — dezent,
   erst beim Hovern/Tappen der Zeile stärker sichtbar. */
.goal-row-delete {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 4px; flex-shrink: 0;
  background: none; border: none; color: var(--color-text-tertiary);
  cursor: pointer; padding: 0; opacity: .6;
}
.goal-row-delete:hover, .goal-row-delete:active { opacity: 1; background: var(--color-bg); }

/* Pfeile zum Verschieben, gleiche Masse und Optik wie das Lösch-×. Am oberen
   bzw. unteren Rand bleibt der Pfeil stehen und wird nur blass, damit die
   Zeile beim Verschieben nicht ihre Breite ändert. */
.goal-move-btn {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 4px; flex-shrink: 0;
  background: none; border: none; color: var(--color-text-tertiary);
  cursor: pointer; padding: 0; opacity: .6;
}
.goal-move-btn:hover:not(:disabled), .goal-move-btn:active:not(:disabled) { opacity: 1; background: var(--color-bg); }
.goal-move-btn:disabled { opacity: .2; cursor: default; }

/* Ziele-Themen: flache, abhakbare Liste pro Thema. Die Reihenfolge ist fest
   (sort_order in der Datenbank), Abhaken sortiert bewusst nicht um — das
   zerstörte bei gestuften Zielen die Logik der Liste. Text umbenennen per
   Klick, Checkbox hakt ab, Pfeile verschieben, × löscht — je eigenes
   Klickziel. */
.goal-check-row { display: flex; align-items: center; gap: 10px; padding: 9px 20px; border-bottom: 1px solid var(--color-border); }
.goal-check-box { cursor: pointer; }
.goal-theme-add-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 20px;
  cursor: pointer;
}
.goal-theme-add-row .goal-add-label, .goal-theme-add-row .goal-add-icon { opacity: .85; }
/* Innerhalb der Themen-Karte dieselben 16px wie .list-card .list-row, statt
   der 20px, die für den flachen Vollbild-Hintergrund gedacht waren. */
.list-card .goal-check-row, .list-card .goal-theme-add-row { padding-left: 16px; padding-right: 16px; }

/* Inline "add goal" — expands in place, no separate screen */
.goal-add-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 20px;
  cursor: pointer;
}
.goal-add-icon {
  width: 20px; height: 20px; border-radius: 5px; flex-shrink: 0;
  border: 1.5px dashed var(--color-border-strong);
  color: var(--color-text-tertiary);
  display: flex; align-items: center; justify-content: center;
}
.goal-add-label { font-size: var(--text-sm); color: var(--color-text-tertiary); }

/* "Thema hinzufügen" legt eine ganze neue Themen-Gruppe an (grössere Wirkung
   als ein einzelnes Ziel) und sitzt bewusst ausserhalb jeder Themen-Karte,
   als eigene Aktion am Ende der Liste — Trennlinie plus Primärfarbe statt
   der dezenten grauen .goal-add-row-Optik von "Ziel hinzufügen" innerhalb
   einer Karte, damit beide klar unterscheidbar bleiben. */
.theme-add-row {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px 12px;
  margin-top: 6px;
  border-top: 1px solid var(--color-border);
  cursor: pointer;
}
.theme-add-row .goal-add-icon { border: none; background: var(--gradient-action); color: #fff; }
.theme-add-row .goal-add-label { color: var(--color-primary); font-weight: 700; }

/* "Reise" — echte Zeitleiste für Meilensteine + abgeschlossene Ziele
   (journeyItemsHtml() in app.js). Marker sind bewusst abgerundete Quadrate,
   keine Kreise, und nutzen denselben Farbverlauf wie .goal-check-box.done —
   visuelles Geschwister der Ziele-Checkliste statt eigener Kreis-Sprache. */
.journey-timeline { padding: 2px 20px 4px 46px; position: relative; }
.journey-item { position: relative; padding-bottom: 20px; }
.journey-item:last-child { padding-bottom: 2px; }
.journey-item::before {
  content: ''; position: absolute;
  left: -28px; top: 21px; bottom: -1px;
  width: 2px; background: var(--color-border-strong);
}
.journey-item:last-child::before { display: none; }
.journey-marker {
  position: absolute; left: -37px; top: 1px;
  width: 19px; height: 19px; border-radius: 5px;
  background: linear-gradient(135deg, var(--color-success-bright), var(--color-success));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 3px var(--color-surface);
}
.journey-item .t { font-size: var(--text-sm); font-weight: 700; color: var(--color-text-primary); line-height: 1.35; }
.journey-item .d { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 2px; font-weight: 500; }

/* ---------- Tagebuch: Übersicht & Detail ("Editorial"-Richtung) ---------- */
/* Jeder Eintrag eine eigene, ruhige Karte statt einer dichten Liste — mehr
   Weissraum, natürliche Textvorschau statt harter Kürzung. Das Erreichte
   bekommt einen schmalen grünen Rand statt einer Fläche oder eines Icons,
   damit es als Highlight auffällt, ohne die Navy/Weiss-Ruhe zu stören. */
.diary-card { background: var(--color-surface); border-radius: var(--radius); box-shadow: var(--shadow-2); padding: 15px 16px; margin: 0 20px 11px; cursor: pointer; }
.diary-card-top { display: flex; align-items: center; gap: 7px; margin-bottom: 7px; }
.diary-card-top .flag { width: 14px; height: 14px; color: var(--color-success); flex-shrink: 0; }
.diary-card-date { font-size: var(--text-md); font-weight: 700; color: var(--color-text-primary); }
.diary-card-activity { font-size: var(--text-sm); color: var(--color-text-secondary); line-height: 1.55; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.diary-card-achievement { font-size: var(--text-sm); color: var(--color-text-primary); font-weight: 600; line-height: 1.5; margin-top: 8px; padding-left: 11px; border-left: 2px solid var(--color-success); }

.diary-detail-date { font-size: var(--text-2xl); font-weight: 700; color: var(--color-text-primary); margin: 8px 20px 10px; }
.diary-detail-tags { display: flex; gap: 12px; margin: 0 20px 24px; }
.diary-detail-tag { font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-tertiary); }
.diary-detail-tag.on { color: var(--color-success); }
.diary-section { margin: 0 20px 22px; }
.diary-section-label { font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-tertiary); margin-bottom: 6px; }
.diary-section-text { font-size: var(--text-md); color: var(--color-text-primary); line-height: 1.65; }

/* Gruppierte Card um mehrere .list-row (oder verwandte Inhalte wie
   .feature-list/.empty-state) — gleicher Elevation-Stil wie .post-card
   (margin 0 20px, shadow-2, kein Rahmen), damit im Profil auf den ersten
   Blick klar ist: das ist eine strukturierte, teils klickbare Einheit statt
   loser Text auf dem Hintergrund. */
.list-card {
  margin: 0 20px 10px;
  background: var(--color-surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.list-card .list-row { padding-left: 16px; padding-right: 16px; }

/* Generic list row — reused for Communitys, Discover, Tagebuch-Vorschau */
.list-row {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 20px;
  border-bottom: 1px solid var(--color-border);
}
.list-row:last-child { border-bottom: none; }
.list-row.tappable { cursor: pointer; }
.list-row.tappable:active { background: var(--color-primary-soft); }
.list-row .ic {
  width: 32px; height: 32px; border-radius: var(--radius); flex-shrink: 0;
  background: var(--color-primary-soft); color: var(--color-primary);
  display: flex; align-items: center; justify-content: center;
}
.list-row .ic.warm { background: var(--color-primary-bright-soft); color: var(--color-primary-dark); }
.list-row .ic.muted { background: var(--color-bg); color: var(--color-text-tertiary); }
.list-row .ic.danger { background: rgba(176,80,60,0.12); color: var(--color-danger); }
.list-row .body { flex: 1; min-width: 0; }
.list-row .body .t { font-size: var(--text-sm); font-weight: 700; color: var(--color-text-primary); display: flex; align-items: center; gap: 7px; }
.list-row .body .d { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 1px; font-weight: 500; }
.list-row .body .d.clamp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Tagebuch-Vorschau im Profil: Zitat darf 2 Zeilen zeigen statt einer
   abgeschnittenen Zeile — wirkt mehr wie ein gelesenes Zitat. Eigene Regel
   statt die geteilte .d.clamp-Definition zu ändern, die sonst überall
   einzeilig bleibt. */
.diary-preview-row .body .d.clamp {
  font-size: var(--text-sm); line-height: 1.5; color: var(--color-text-secondary);
  white-space: normal; text-overflow: clip;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.list-row .trail { flex-shrink: 0; color: var(--color-text-tertiary); display: flex; align-items: center; }

.empty-row { padding: 10px 20px 6px; font-size: var(--text-sm); color: var(--color-text-tertiary); line-height: 1.5; }

/* ---------- Community-Mitgliederzahl & -Fortschritt (Discover-Screen) ---------- */

/* Offene Community: Personen-Icon statt reinem Text als visueller Anker,
   monochrom (erbt die dezente Textfarbe), kein Farb-Icon. */
.community-member-count {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500;
  margin-top: 2px;
}

/* Noch nicht eröffnete Community: derselbe Farb-/Formstil wie der Ziele-Ring
   (.goal-ring) — Navy-Füllung auf heller, neutraler Spur, keine Gamification-
   Optik. Kompaktes "3/5"-Label über dem Balken statt eines ganzen Satzes,
   die genaue Zahl bleibt damit trotzdem ablesbar. */
.community-progress { margin-top: 8px; }
.community-progress-count { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; margin-bottom: 5px; }
.community-progress-track { height: 6px; border-radius: var(--radius); background: var(--color-border); overflow: hidden; }
.community-progress-fill { height: 100%; background: var(--color-primary); border-radius: var(--radius); }
/* Reiner Stand-Text, neutral statt komplett blau/unterstrichen — die
   eigentliche Handlung sitzt jetzt in einem eigenen .btn-premium-Button
   direkt darunter statt als Link mitten im Satz (Nutzerwunsch: "wie der
   Premium-freischalten-Button" auf dem Plan-verwalten-Screen). */
.community-progress-nudge { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; margin-top: 5px; line-height: 1.4; }

/* Dezenter Text-Link statt eines zweiten vollflächigen Buttons unter
   "Eröffnung unterstützen" — zwei Buttons gleichzeitig machten die Zeile
   deutlich höher als andere Discover-Zeilen, für eine Aktion, die ohnehin
   nur eine Abkürzung zur ersten ist. */
.nudge-link {
  display: block; background: none; border: none; padding: 0; margin-top: 8px;
  font-family: var(--font-sans); font-size: var(--text-xs); font-weight: 600;
  color: var(--color-primary-bright); cursor: pointer; text-align: left;
}
.nudge-link:active { opacity: .7; }
.interest-confirmed { display: flex; align-items: flex-start; gap: 6px; margin-top: 9px; font-size: var(--text-sm); font-weight: 600; color: var(--color-primary); line-height: 1.4; }
.interest-confirmed svg { flex-shrink: 0; margin-top: 3px; }
.interest-confirmed + .nudge-link { margin-top: 4px; }
/* Kurze Erklärung ganz oben im Entdecken-Screen (Unterstützen, Eröffnung, Premium). */
.discover-intro { margin: 4px 20px 14px; }

/* "Eröffnung unterstützen"-Ansicht: derselbe Balken wie im Discover-Screen
   (.community-progress-track/-fill), nur mit der Zahl daneben statt einer
   kleinen Caption darüber — hier der einzige Inhalt der Ansicht, verdient
   etwas mehr Gewicht. Premium-Hinweis bewusst als normaler Fliesstext statt
   .premium-note-Fläche, damit er integriert statt wie ein Hinweiskasten wirkt. */
.interest-progress-row { display: flex; align-items: center; gap: 12px; margin-top: 4px; }
.interest-progress-stat { font-size: var(--text-xl); font-weight: 700; color: var(--color-text-primary); white-space: nowrap; }
.interest-progress-stat span { font-size: var(--text-sm); font-weight: 600; color: var(--color-text-tertiary); }
.interest-progress-label { font-size: var(--text-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-tertiary); margin-top: 7px; }

/* "Eröffnung unterstützen"-Ansicht als Karten-Stapel statt loser Elemente auf
   dem Hintergrund (Nutzerwunsch: mehr Struktur/weniger Leerraum, "wie Apple/
   Linear/Notion"). Eigene, schlanke Kartenklasse statt .list-card, da
   .list-card eigene 20px-Aussenränder mitbringt (dort für den vollen Screen
   gedacht) — hier sitzen die Karten bereits im #community-interest-content-
   Container mit padding:20px, ein zweiter Aussenrand würde sich addieren. */
.interest-stack { display: flex; flex-direction: column; gap: 12px; }
.interest-card { background: var(--color-surface); border-radius: var(--radius); box-shadow: var(--shadow-1); padding: 14px; }
.interest-hero { display: flex; align-items: center; gap: 12px; }
.interest-hero .ic {
  width: 44px; height: 44px; border-radius: var(--radius-lg); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
}
.interest-hero-name { font-size: var(--text-lg); font-weight: 700; color: var(--color-text-primary); }
.interest-hero-desc { font-size: var(--text-sm); color: var(--color-text-tertiary); margin-top: 1px; }
.interest-stat-line { display: flex; align-items: baseline; gap: 6px; }
.interest-stat-num { font-size: var(--text-2xl); font-weight: 800; color: var(--color-text-primary); }
.interest-stat-of { font-size: var(--text-sm); font-weight: 600; color: var(--color-text-tertiary); }
.interest-unlock-label { font-size: var(--text-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-tertiary); margin: 14px 0 6px; }
.interest-unlock-item { display: flex; align-items: flex-start; gap: 9px; font-size: var(--text-sm); color: var(--color-text-secondary); padding: 5px 0; line-height: 1.45; }
/* Nummerierte Schritte in "So funktioniert es" (Eröffnung unterstützen). */
.interest-unlock-dot { flex-shrink: 0; margin-top: 1px; color: #fff; font-size: var(--text-2xs); font-weight: 700; }
.interest-unlock-dot { width: 16px; height: 16px; background: linear-gradient(135deg, var(--color-primary-bright), var(--color-primary)); }

/* Auswahl-Karten für "Wann startet dein Premium" — Häkchen-Quadrat statt
   Kreis-Radio, dieselbe Formsprache wie .goal-check-box/.feature-item .dot
   (Status-/Auswahl-Marker sind in dieser App abgerundete Quadrate, echte
   Kreise bleiben Personen/Status/Hauptaktion vorbehalten). */
.premium-choice-label { font-size: var(--text-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-tertiary); margin-bottom: 8px; }
.premium-choice-option { display: flex; align-items: flex-start; gap: 11px; padding: 12px; border-radius: var(--radius); border: 1.5px solid var(--color-border); cursor: pointer; transition: all .15s var(--ease); }
.premium-choice-option + .premium-choice-option { margin-top: 8px; }
.premium-choice-option.selected { border-color: var(--color-primary); background: var(--color-primary-soft); }
.premium-choice-check { width: 19px; height: 19px; border: 1.5px solid var(--color-border-strong); margin-top: 1px; transition: all .15s var(--ease); }
.premium-choice-option.selected .premium-choice-check { background: linear-gradient(135deg, var(--color-primary-bright), var(--color-primary)); border-color: var(--color-primary); }
.premium-choice-title { font-size: var(--text-sm); font-weight: 700; color: var(--color-text-primary); }
.premium-choice-desc { font-size: var(--text-xs); color: var(--color-text-secondary); margin-top: 2px; line-height: 1.45; }
/* Generischer integrierter Hinweistext (Icon + Fliesstext, keine Fläche) —
   ursprünglich nur für die Eröffnung-unterstützen-Ansicht gebaut, jetzt
   verallgemeinert, da dasselbe Muster im Tagebuch-Formular wiederverwendet wird. */
.info-text { display: flex; align-items: flex-start; gap: 9px; margin-top: 22px; font-size: var(--text-sm); line-height: 1.55; color: var(--color-text-secondary); }
.info-text svg { flex-shrink: 0; margin-top: 1px; color: var(--color-text-tertiary); }

/* Designed empty states — for the larger, more visible gaps (comments,
   diary, search, member list) rather than every small hint. */
.empty-state {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 36px 24px;
}
.empty-state .ic {
  width: 52px; height: 52px; border-radius: var(--radius-lg);
  background: var(--color-primary-soft); color: var(--color-primary);
  display: flex; align-items: center; justify-content: center; margin-bottom: 14px;
}
.empty-state .t { font-size: var(--text-sm); font-weight: 700; color: var(--color-text-primary); }
.empty-state .d { font-size: var(--text-sm); color: var(--color-text-tertiary); margin-top: 5px; line-height: 1.5; max-width: 230px; }

/* Mitgliedschaft / Premium — informational, not promotional */
.plan-tag {
  display: inline-flex; align-items: center;
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .03em;
  padding: 3px 8px; border-radius: var(--radius);
  background: var(--color-primary-soft); color: var(--color-primary);
}
.plan-tag.premium { background: var(--color-primary-bright-soft); color: var(--color-primary-dark); }
.feature-list { padding: 4px 20px 16px; }
.feature-item { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: var(--text-sm); color: var(--color-text-secondary); }
/* Gleiche abgerundete Quadrat-Optik wie .goal-check-box, statt eigener runder
   Punkte — an das neue Ziele-Design angeglichen (siehe CLAUDE.md: Kreise nur
   für Avatare/Status/Haupt-Aktionen, nicht für Status-Icons wie hier). */
.feature-item .dot { width: 18px; height: 18px; background: linear-gradient(135deg, var(--color-primary-bright), var(--color-primary)); }
.feature-item.locked .dot { background: var(--color-bg); border: 1.5px solid var(--color-border-strong); color: var(--color-text-tertiary); }
.feature-item.locked { color: var(--color-text-tertiary); }

/* Beitrag-Detail ist die einzige Ausnahme von der sonst app-weiten
   .view{background:var(--color-bg)}-Regel — Nutzerwunsch (Skool-Referenz):
   die Seite selbst weiss, dafür bekommen einzelne Kommentare/Antworten unten
   eine eigene gefüllte Fläche in genau diesem Grauton (siehe .comment-bubble). */
#view-post-detail { background: var(--color-surface); }

/* ---------- Comments ---------- */

.comment-row { display: flex; align-items: flex-start; gap: 10px; margin: 0 16px 10px; position: relative; }
.comment-row.reply { margin: 0 16px 8px 0; }
.comment-content { flex: 1; min-width: 0; }

/* Kebab-Menü liegt bei Kommentaren bewusst über der grauen Fläche statt
   daneben (eigener Flex-Platz würde die Fläche vorzeitig abschneiden,
   Nutzerwunsch: die Fläche soll fast bis zum Rand gehen, das Menü sitzt
   sichtbar darüber, oben rechts in der Ecke). Nur für Kommentare so
   überschrieben — .post-author nutzt dasselbe Markup unverändert daneben. */
.comment-row .menu-trigger-wrap { position: absolute; top: 6px; right: 0; margin-left: 0 !important; }

/* Gefüllte Fläche im Skool-Stil (Nutzerreferenz) — die Seite selbst ist
   weiss (#view-post-detail), diese Fläche nutzt stattdessen genau den
   Grauton, der vorher als Seitenhintergrund diente. Padding-right macht
   Platz für das darüberliegende Kebab-Menü, ohne dass Name/Zeit/Text
   darunter verschwinden. */
.comment-bubble { background: var(--color-bg); border-radius: var(--radius); padding: 10px 40px 10px 14px; }
/* Lange Namen (auch ohne Leerzeichen) brechen sauber um, die Zeitangabe rutscht bei Bedarf in die nächste Zeile, bleibt aber immer in der Fläche. */
.comment-head { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; row-gap: 1px; min-width: 0; }
.comment-head .name { font-size: var(--text-sm); font-weight: 700; min-width: 0; overflow-wrap: anywhere; }
.comment-head .time { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; white-space: nowrap; }
.comment-bubble .txt { font-size: var(--text-sm); line-height: 1.55; color: var(--color-text-primary); margin-top: 3px; overflow-wrap: anywhere; }

/* Liken/Antworten liegen bewusst ausserhalb der Kommentarfläche, direkt auf
   dem weissen Seitenhintergrund — genau wie im Skool-Referenzbild. */
.comment-meta-row { display: flex; align-items: center; gap: 14px; margin-top: 6px; padding-left: 14px; }
.comment-action {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); font-weight: 600; color: var(--color-text-tertiary);
  cursor: pointer;
}
/* Gleiches Prinzip wie .post-action.supported: aktiver Zustand färbt Text und
   Herz in Primary statt einer eigenen Signalfarbe. */
.comment-action.liked { color: var(--color-primary); }
.comment-action.liked svg { fill: currentColor; }

/* Antworten sitzen eingerückt unter ihrem Kommentar, mit einer dünnen
   Trennlinie als Faden zum übergeordneten Kommentar — Einrückung entspricht
   Avatarbreite (32px) plus Zeilenabstand (10px). */
.comment-replies {
  margin-left: 42px;
  padding-left: 12px;
  border-left: 1px solid var(--color-border);
}
.comment-reply-composer { margin: 6px 16px 10px 42px; }

/* Soft-gelöschter Kommentar mit Antworten (delete_comment()): klar als
   Platzhalter erkennbar, ohne Name/Text/Aktionen - gestrichelter Rand statt
   Fläche, damit er sich deutlich von echten Kommentaren abhebt. */
.comment-bubble.deleted {
  background: transparent;
  border: 1px dashed var(--color-border);
  padding: 9px 14px;
}
.comment-bubble.deleted .txt { margin-top: 0; color: var(--color-text-tertiary); font-style: italic; }
.comment-avatar-deleted { background: var(--color-bg); border: 1px dashed var(--color-border); flex-shrink: 0; }

/* Ladezustand der Kommentare, gleiche Balken wie im Feed. */
.comment-row.skeleton .sk-line { margin-top: 0; }
.comment-row.skeleton .comment-bubble { display: flex; flex-direction: column; }

/* Senden-Knopf/Feld während des Speicherns gesperrt. */
.reply-bar input:disabled { opacity: .6; }
#btn-send-post-reply:disabled, .comment-send-btn:disabled { opacity: .5; cursor: default; }

.reply-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px calc(var(--safe-bottom) + 12px);
  border-top: 1px solid var(--color-border);
  background: var(--color-bg);
}
.reply-bar input {
  flex: 1; border: none; border-radius: var(--radius-pill);
  padding: 11px 16px; font-family: var(--font-sans); font-size: var(--text-sm); outline: none;
  background: var(--color-surface); box-shadow: var(--shadow-1);
}

/* Antwort- oder Bearbeitungsfeld unter einem Kommentar hat den Fokus: die
   untere Kommentarzeile tritt zurück (Block 5, F27, bindInlineCommentFocus()). */
#view-post-detail.inline-comment-editing > .reply-bar { display: none; }

/* ---------- Chat-Thread (Konversation) ---------- */

.chat-thread { padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.chat-bubble-row { display: flex; flex-direction: column; max-width: 78%; }
.chat-bubble-row.me { align-self: flex-end; align-items: flex-end; }
.chat-bubble-row.them { align-self: flex-start; align-items: flex-start; }
.chat-bubble {
  padding: 10px 13px; border-radius: var(--radius);
  font-size: var(--text-sm); line-height: 1.5;
  cursor: pointer;
}
/* Nur der reine Nachrichtentext behält Zeilenumbrüche (abgedeckte und
   entfernte Bubbles enthalten Markup mit Einrückung). */
.chat-bubble { overflow-wrap: anywhere; }
.chat-bubble[data-bubble] { white-space: pre-wrap; }
.chat-bubble-row.me .chat-bubble { background: var(--gradient-action); color: #fff; }
.chat-bubble-row.them .chat-bubble { background: var(--color-surface); color: var(--color-text-primary); box-shadow: var(--shadow-1); }
.chat-bubble-time { font-size: var(--text-2xs); color: var(--color-text-tertiary); font-weight: 500; padding: 0 2px; }
.chat-bubble-meta { display: flex; align-items: center; gap: 2px; margin-top: 4px; }

/* Von der Moderation entfernte Nachricht: gleiche Sprache wie ein gelöschter
   Kommentar (gestrichelt, kursiv), für beide Seiten gleich, ohne Aktionen. */
.chat-bubble.removed,
.chat-bubble-row.me .chat-bubble.removed,
.chat-bubble-row.them .chat-bubble.removed {
  background: transparent; box-shadow: none; color: var(--color-text-tertiary);
  border: 1px dashed var(--color-border); font-style: italic; cursor: default;
}
/* Wortfilter beim Empfang (M9): abgedeckt, bis man sie aufdeckt. */
.chat-bubble-row.them .chat-bubble.masked { background: var(--color-bg); box-shadow: none; border: 1px solid var(--color-border); color: var(--color-text-secondary); cursor: default; }
.chat-masked-actions { display: flex; gap: 6px; margin-top: 8px; }
.chat-masked-actions .btn { padding: 6px 12px; font-size: var(--text-xs); }
.chat-older-btn { align-self: center; }

/* Jugendschutz in Direktnachrichten (Z1/Z2): sehen nur Minderjährige.
   Bewusst ruhig gehalten, kein Warnrot, damit es hilft statt zu ängstigen. */
.dm-safety-hint {
  background: var(--color-primary-bright-soft); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 4px;
}
.dm-safety-hint .t { font-size: var(--text-sm); font-weight: 700; color: var(--color-primary-dark); }
.dm-safety-hint .d { font-size: var(--text-xs); line-height: 1.5; color: var(--color-text-secondary); margin-top: 4px; }
.dm-safety-hint .btn { margin-top: 10px; }
.dm-youth-warning {
  display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap;
  margin-top: 6px; padding: 8px 10px;
  background: var(--color-bg); border-radius: var(--radius);
  font-size: var(--text-xs); line-height: 1.5; color: var(--color-text-secondary);
}
.dm-youth-warning .btn { padding: 4px 10px; font-size: var(--text-2xs); }

/* Beweiskopie in der Moderationsübersicht. */
.report-evidence {
  margin: 8px 0 6px; padding: 10px 12px;
  background: var(--color-bg); border-radius: var(--radius);
  font-size: var(--text-sm); line-height: 1.5; color: var(--color-text-primary);
  overflow-wrap: anywhere; white-space: pre-wrap;
}
/* Spezifischer als .list-row .body .d, sonst wird die Z3-Kennzeichnung grau
   (war seit Schritt 4 so, gefunden in B-3). */
.report-minor,
.list-row .body .report-minor { color: var(--color-danger); font-weight: 600; margin-top: 4px; }
/* Rückfallebene, wenn die Zwischenablage nicht verfügbar ist. */
.report-evidence-fallback {
  width: 100%; min-height: 120px; margin-top: 10px; padding: 10px 12px;
  border: 1px solid var(--color-border); border-radius: var(--radius);
  font-family: inherit; font-size: var(--text-xs); line-height: 1.5;
  background: var(--color-surface); color: var(--color-text-primary); resize: vertical;
}
/* Anhänge gemeldeter Nachrichten (E-3): Bild erst auf Tipp, dann Vorschau
   120 x 120 px mit Vollbild; Sprachnachricht wie in der Unterhaltung, aber
   ohne Reaktionen; "Anhang nicht mehr verfügbar" gestrichelt wie ein
   Platzhalter. */
.mod-media { margin: 8px 0 4px; }
.mod-image-thumb {
  display: block; width: 120px; height: 120px; padding: 0; border: none;
  border-radius: var(--radius); overflow: hidden; background: var(--color-border); cursor: pointer;
}
.mod-image-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }
.mod-image-thumb.loading { cursor: default; }
.mod-voice { max-width: 100%; }
.mod-voice .chat-bubble.voice { background: var(--color-bg); box-shadow: none; cursor: default; }
.mod-media-unavailable {
  display: inline-block; background: none; padding: 8px 10px; font: inherit;
  font-size: var(--text-xs); font-weight: 500; color: var(--color-text-tertiary); text-align: left;
  border: 1px dashed var(--color-border); border-radius: var(--radius);
}
button.mod-media-unavailable { cursor: pointer; }
/* Sprachnachricht in der Moderation nicht ladbar oder nicht abspielbar:
   derselbe gestrichelte Rahmen wie beim Bild statt einer leeren Bubble. */
.mod-voice .chat-bubble.voice[data-state="failed"],
.mod-voice .chat-bubble.voice[data-state="unplayable"] { width: auto; padding: 0; background: none; }
.mod-voice .chat-bubble.voice[data-state="failed"] .voice-unavailable,
.mod-voice .chat-bubble.voice[data-state="unplayable"] .voice-unavailable {
  padding: 8px 10px; opacity: 1; color: var(--color-text-tertiary);
  border: 1px dashed var(--color-border); border-radius: var(--radius);
}
.reaction-pill.mine { box-shadow: inset 0 0 0 1.5px var(--color-primary-bright), var(--shadow-1); }
.moderation-actions { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.moderation-actions .btn { padding: 8px 12px; }

/* Bild-Bubble (6b, E-1, E1-3): fester Rahmen 200 x 200 px, damit das
   Nachladen die Liste nicht verschiebt; die Bubble-Fläche bleibt als schmaler
   Rand. Grau bis zum Laden, ganz im Vollbild. */
.chat-bubble.image { padding: 4px; cursor: default; }
.chat-image-frame {
  display: flex; align-items: center; justify-content: center;
  width: 200px; height: 200px; padding: 0; border: none;
  border-radius: calc(var(--radius) - 2px); overflow: hidden;
  background: var(--color-border); cursor: pointer;
}
.chat-image-frame img { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }
.chat-image-frame.loading { cursor: default; }
.chat-image-failed { font-size: var(--text-xs); color: var(--color-text-tertiary); font-weight: 500; }
/* Vorschau im Composer: dieselbe Kachel wie im Beitragsformular, aber ohne
   Sortieren (nur ein Bild). */
.post-photo-thumb.chat-photo-thumb { cursor: default; }
#chat-photo-preview:not(:empty) { margin: 8px 0; }
/* Bild-Knopf gedämpft, wenn Medien in dieser Unterhaltung nicht möglich sind
   (E1-1 a): bleibt antippbar, ein Tipp erklärt es. */
#btn-chat-attach-image.is-muted { opacity: .4; }
/* Reaktionen im Vollbild eines Nachrichtenbildes (E1-2). */
.photo-viewer-reactions {
  display: flex; justify-content: center; gap: 4px;
  margin: 12px auto 28px; padding: 5px 6px;
  background: rgba(255,255,255,.12); border-radius: var(--radius-pill);
  width: max-content;
}
.photo-viewer-reactions .reaction-picker-btn { font-size: 20px; padding: 6px; }
.photo-viewer-reactions .reaction-picker-btn.active { background: rgba(255,255,255,.28); }

/* Sprachnachricht-Bubble (E2-7): Abspielen/Pause, schmale Fortschrittsleiste
   und Dauer in einer Reihe. Keine Schein-Wellenform, die Leiste zeigt nur
   echten Fortschritt. Tippen neben den Knopf öffnet die Reaktionen (E2-8). */
.chat-bubble.voice { display: flex; align-items: center; gap: 10px; width: 200px; max-width: 100%; padding: 8px 12px 8px 8px; }
.voice-play-btn {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; cursor: pointer; color: inherit;
  background: rgba(255,255,255,0.22);
}
.chat-bubble-row.them .voice-play-btn { background: var(--color-primary-soft); color: var(--color-primary); }
.voice-play-btn:disabled { opacity: .5; cursor: default; }
.voice-track { flex: 1; min-width: 0; height: 4px; border-radius: 2px; background: rgba(255,255,255,0.32); overflow: hidden; }
.chat-bubble-row.them .voice-track { background: var(--color-primary-soft); }
.voice-progress { display: block; height: 100%; width: 0; background: #fff; border-radius: 2px; transition: width .25s linear; }
.chat-bubble-row.them .voice-progress { background: var(--color-primary); }
.voice-duration { font-size: var(--text-xs); font-weight: 600; opacity: .85; flex-shrink: 0; min-width: 28px; text-align: right; font-variant-numeric: tabular-nums; }
.voice-unavailable {
  background: none; border: none; padding: 4px 4px; font: inherit;
  font-size: var(--text-xs); font-weight: 500; color: inherit; opacity: .8; text-align: left;
}
button.voice-unavailable { cursor: pointer; }
@media (prefers-reduced-motion: reduce) { .voice-progress { transition: none; } }

/* Reaktionen: kleine, freistehende Pills unter der Bubble statt Zahlen-Badge
   — es gibt hier nur einen Nutzer, ein Emoji pro Reaktion reicht als Signal. */
.chat-reactions { display: flex; gap: 4px; margin-top: 4px; }
.reaction-pill {
  background: var(--color-surface); box-shadow: var(--shadow-1);
  border-radius: var(--radius-pill); padding: 2px 7px; font-size: var(--text-sm); line-height: 1.4;
}

/* Reaktions-Auswahl öffnet sich beim Klick auf eine Bubble, direkt darunter
   statt als absolut positioniertes Popover — einfacher und robust im
   begrenzten Phone-Frame. */
.chat-reaction-picker {
  display: flex; gap: 2px; margin-top: 6px;
  background: var(--color-surface); box-shadow: var(--shadow-2);
  border-radius: var(--radius-pill); padding: 5px 6px;
}
.reaction-picker-btn {
  border: none; background: none; cursor: pointer;
  font-size: 16px; line-height: 1; padding: 4px;
  border-radius: 50%; transition: transform .1s var(--ease), background .15s var(--ease);
}
.reaction-picker-btn:active { transform: scale(1.2); background: var(--color-primary-soft); }

/* Aufnahme-Zeile (E2-11): Punkt, Text und Zeit ersetzen Feld, Bild- und
   Mikrofon-Knopf. Rot ist ein funktionales Aufnahme-Signal wie in jeder
   Kamera-/Chat-App, keine neue Akzentfarbe. Nach dem Beenden ("Aufnahme
   beendet", "Nicht gesendet") verschwindet der Punkt. */
.chat-recording-indicator {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary);
  padding: 0 4px;
}
#chat-recording-timer { font-variant-numeric: tabular-nums; color: var(--color-text-primary); }
.chat-recording-dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--color-danger);
  animation: pulse 1.1s ease-in-out infinite;
}
.chat-recording-indicator.stopped .chat-recording-dot { display: none; }
.chat-recording-indicator.failed #chat-recording-label { color: var(--color-danger); }
#btn-chat-voice.is-muted { opacity: .4; }
#btn-send-conversation-message:disabled, #btn-chat-voice-cancel:disabled { opacity: .5; cursor: default; }

.premium-note {
  display: flex; align-items: center; gap: 8px;
  background: var(--color-primary-bright-soft);
  border-radius: var(--radius);
  padding: 11px 13px;
  font-size: var(--text-sm); color: var(--color-primary-dark);
  margin-bottom: 18px;
  line-height: 1.4;
}

/* Gleiches Muster wie .premium-note, aber für echte Warnungen vor
   irreversiblen Aktionen (Account löschen) — einzige Stelle ausserhalb des
   Aufnahme-Indikators, an der --color-danger als Fläche/Text auftaucht. */
.danger-note {
  display: flex; align-items: center; gap: 8px;
  background: rgba(176,80,60,0.1);
  border-radius: var(--radius);
  padding: 11px 13px;
  font-size: var(--text-sm); color: var(--color-danger);
  line-height: 1.4;
}

/* Toggle-Switch für Benachrichtigungen — bewusste Erweiterung von
   --radius-pill über die bisherige Liste (Avatare, Statuspunkt, +-Button)
   hinaus: ein Schalter ist universell rund, alles andere wirkt hier falsch. */
.switch {
  width: 42px; height: 24px; border-radius: var(--radius-pill);
  background: var(--color-border-strong);
  position: relative; cursor: pointer; flex-shrink: 0;
  border: none; padding: 0;
  transition: background .15s var(--ease);
}
.switch.on { background: var(--color-primary); }
.switch-knob {
  position: absolute; top: 2px; left: 2px;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-1);
  transition: transform .15s var(--ease);
}
.switch.on .switch-knob { transform: translateX(18px); }

/* ---------- Notifications ---------- */

.notif-row { display: flex; align-items: center; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--color-border); cursor: pointer; }
.notif-row.unread { background: var(--color-primary-soft); }
.notif-icon {
  width: 32px; height: 32px; border-radius: var(--radius);
  background: var(--color-surface); box-shadow: var(--shadow-1);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--color-primary);
}
.notif-body { flex: 1; min-width: 0; }
.notif-text { font-size: var(--text-sm); line-height: 1.5; color: var(--color-text-primary); }
.notif-text b { font-weight: 700; }
.notif-time { font-size: var(--text-xs); color: var(--color-text-tertiary); margin-top: 3px; font-weight: 500; }
/* Ungelesen-Punkt wie bei Skool — zeigt den Status auf einen Blick, statt nur
   über den dezenten Hintergrund-Tint der ganzen Zeile. */
.notif-unread-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--color-primary); flex-shrink: 0; }
/* Auszug des Kommentars unter dem Satz, höchstens zwei Zeilen. */
.notif-excerpt {
  font-size: var(--text-sm); line-height: 1.45; color: var(--color-text-secondary); margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere;
}
.notif-text { overflow-wrap: anywhere; }
.notif-actions { display: flex; justify-content: flex-end; padding: 10px 20px 6px; border-bottom: 1px solid var(--color-border); }
.notif-row.skeleton { cursor: default; }
.notif-row.skeleton .sk-line { margin-top: 0; }

/* Zahl ungelesener Neuigkeiten am Icon in der Bottom-Nav (gebündelte
   Unterstützung zählt als 1, siehe updateNotificationsBadge()). */
.nav-count-badge {
  position: absolute; top: -5px; right: -1px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-bright); color: #fff;
  border: 1.5px solid var(--color-surface);
  font-size: var(--text-2xs); font-weight: 700; line-height: 14px; text-align: center;
  box-sizing: border-box;
}

/* Kurze Hervorhebung des Kommentars, zu dem eine Benachrichtigung führt. */
.comment-bubble { transition: background-color .6s var(--ease); }
.comment-bubble.comment-focus { background: var(--color-primary-bright-soft); }

/* ---------- Toast ---------- */

.toast {
  position: absolute;
  left: 24px; right: 24px; bottom: calc(108px + var(--safe-bottom));
  background: var(--color-text-primary);
  color: #fff;
  padding: 13px 16px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  opacity: 0;
  transform: translateY(8px);
  transition: all .25s var(--ease);
  pointer-events: none;
  z-index: 60;
}
.toast.show { opacity: 1; transform: translateY(0); }

/* ---------- Gerätemodus (Block 3, Handy-Tauglichkeit) ----------
   Handys, Tablets und schmale Fenster (bis 600 px oder Touch ohne Maus):
   kein Handy-Rahmen, keine Notch, keine Status-Leiste als Attrappe; die App
   füllt den Bildschirm, auf Tablets als mittige Spalte bis 520 px. Am
   Desktop mit Maus bleibt der Rahmen als Vorschau unverändert. Dieselbe
   Abfrage steht in app.js als DEVICE_MODE_QUERY. */
@supports (height: 100dvh) {
  :root { --app-height: 100dvh; }
}

@media (max-width: 600px), (hover: none) and (pointer: coarse) {
  html, body {
    height: 100%;
    margin: 0;
    background: var(--color-bg);
    overflow: hidden;
    overscroll-behavior: none;
  }
  body {
    display: block;
    padding: 0;
    min-height: 0;
  }
  .phone-frame {
    width: 100%;
    max-width: 520px;
    height: var(--app-height);
    margin: 0 auto;
    padding: 0;
    background: var(--color-bg);
    border-radius: 0;
    box-shadow: none;
  }
  .phone-notch { display: none; }
  .phone-screen { border-radius: 0; }

  /* Die echte Status-Leiste des Geräts ersetzt die Attrappe; der obere
     Abstand kommt aus dem sicheren Bereich plus 8 px Luft. */
  .status-bar { display: none; }
  html { --view-pad-top: calc(var(--safe-top) + 8px); }
  .view { padding-top: var(--view-pad-top); }

  /* Band "Testumgebung" als schmale Leiste ganz oben unter dem sicheren
     Bereich; die Ansichten rücken um seine Höhe nach unten. */
  html[data-env="test"] .env-band {
    top: var(--safe-top);
    left: 0;
    right: 0;
    transform: none;
    height: 18px;
    line-height: 14px;
    border-radius: 0;
    text-align: center;
  }
  html[data-env="test"] { --view-pad-top: calc(var(--safe-top) + 18px + 8px); }

  button, a, [role="button"] {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }

  /* Mindestens 16 px in allen Eingabefeldern, sonst zoomt iOS beim Antippen
     in die Seite hinein (Block 3, 3-3). Gilt nur im Gerätemodus; am Desktop
     bleibt die bisherige Grösse. !important, weil die Felder ihre Grösse
     über Klassen setzen (.input, .textarea, .search-bar input, .reply-bar
     input, .template-q textarea); die Regel ist eine reine Untergrenze. */
  input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):not([type=button]):not([type=submit]):not([type=color]),
  textarea,
  select { font-size: 16px !important; }

  /* Bei offener Bildschirmtastatur (syncViewportHeight() in app.js) braucht
     die untere Navigation keinen Platz über der Tastatur. */
  html.keyboard-open .bottom-nav { display: none; }

  /* Bei offener Tastatur verdeckt sie den Home-Balken: der sichere Bereich
     unten fällt auf 0, Eingabezeile, Knopfbereiche, Toast und Auswahlfenster
     sitzen direkt über der Tastatur (Block 5, F1). */
  html.keyboard-open { --safe-bottom: 0px; }

  /* Formulare mit Knopfbereich (Block 5, F1): Bei offener Tastatur scrollt
     die Ansicht selbst, der bisherige Scrollbereich fliesst mit, der
     Knopfbereich steht am Ende des Inhalts und ist durch Scrollen erreichbar.
     Die Eingabezeile (.reply-bar) ist bewusst nicht betroffen. Die
     Scroll-Position überträgt setKeyboardState() in app.js. */
  html.keyboard-open .view.active:has(> .view-actions) {
    overflow-y: auto;
    -ms-overflow-style: none;
    scrollbar-width: none;
  }
  html.keyboard-open .view.active:has(> .view-actions)::-webkit-scrollbar { display: none; }
  html.keyboard-open .view.active:has(> .view-actions) > .view-scroll,
  html.keyboard-open .view.active:has(> .view-actions) > .auth-body,
  html.keyboard-open .view.active:has(> .view-actions) > .setup-body,
  html.keyboard-open .view.active:has(> .view-actions) > .welcome-body {
    flex: none;
    min-height: 0;
    overflow: visible;
  }
  html.keyboard-open .view.active:has(> .view-actions) > .view-actions { flex: none; }

  /* Die Kopfzeile bleibt dabei oben kleben, damit Zurück und Schliessen
     erreichbar bleiben. Der obere Abstand der Ansicht (sicherer Bereich,
     Band) wandert dafür als Rahmen in Hintergrundfarbe an die Kopfzeile:
     gleiche Geometrie, aber top: 0 rechnet in jeder Engine gleich (Chromium
     misst den Klebe-Abstand ab dem Inhaltsrand des Scrollbereichs). Bei sehr
     geringer Höhe (quer mit Tastatur, kb-short aus app.js) scrollt sie mit
     (F9). */
  html.keyboard-open:not(.kb-short) .view.active:has(> .view-actions):has(> .detail-header, > .screen-header, > .progress-steps) {
    padding-top: 0;
  }
  html.keyboard-open:not(.kb-short) .view.active:has(> .view-actions) > .detail-header,
  html.keyboard-open:not(.kb-short) .view.active:has(> .view-actions) > .screen-header,
  html.keyboard-open:not(.kb-short) .view.active:has(> .view-actions) > .progress-steps {
    position: sticky;
    top: 0;
    z-index: 4;
    background: var(--color-bg);
    border-top: var(--view-pad-top) solid var(--color-bg);
  }

  /* Abstand beim Scrollen zum Feld: nicht unter der klebenden Kopfzeile,
     nicht direkt an der Tastaturkante. */
  html.keyboard-open input,
  html.keyboard-open textarea,
  html.keyboard-open select { scroll-margin-top: 72px; scroll-margin-bottom: 16px; }

  /* Kein Zoomen durch Doppeltippen, Zoomen mit zwei Fingern bleibt (Block 5,
     F4). touch-action wirkt nur bis zum nächsten Scrollbereich, deshalb auf
     allen Elementen; Fotokacheln überschreiben es unten. */
  html, body, html * { touch-action: manipulation; }

  /* Fotokacheln im Beitragsformular: kein Mitscrollen beim Ziehen (F21);
     gescrollt wird neben den Kacheln. */
  .post-photo-thumb { touch-action: none; }

  /* Bedienelemente nicht markierbar, kein iOS-Menü bei langem Drücken
     (F13 Teil 1). Markierbar bleiben Beitrags- und Kommentartexte,
     Nachrichten, Bio, Rechtstexte und alle Eingabefelder. */
  .env-band, .status-bar, .screen-header, .detail-header, .photo-viewer-header,
  .bottom-nav, .nav-item, button, .btn, .icon-btn, .chip, .tab-switch,
  .tab-switch-item, .menu-panel, .menu-item, .list-row, .conversation-row,
  .member-row, .notif-row, .post-author, .post-action, .comment-meta-row,
  .progress-steps, .toast, .splash, #view-intro, .nudge-link, .skip-link {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }

  /* Ausnahme: Zeilen der Moderation bleiben markierbar (Auszüge gemeldeter
     Inhalte, Sperrgründe; Durchsicht C6). Knöpfe darin bleiben es nicht. */
  #view-moderation .list-row { -webkit-user-select: text; user-select: text; }

  /* Bilder in Beiträgen, Unterhaltungen und im Vollbild behalten das
     iOS-Bildmenü ("Foto sichern"): ausdrücklich, weil das Bild in der
     Unterhaltung in einem button steckt und von ihm callout: none erbte
     (Korrekturrunde 2, N12). */
  .post-photo img, .chat-image-frame img, .chat-photo-thumb img, .photo-viewer img { -webkit-touch-callout: default; }
  /* Bekannte Grenze (Korrekturrunde 4, N12): In der installierten Web-App
     bietet iOS das Bildmenü nicht an, in Safari schon. touch-action: auto
     auf Bildern aus Runde 3 ist deshalb wieder entfernt. */
  /* Bilder aus der Moderation ohne iOS-Bildmenü (F13 Teil 2, passend zu
     E3-3); die Regel steht nach der Ausnahme oben und ist stärker. */
  #view-moderation img, .photo-viewer.from-moderation img { -webkit-touch-callout: none; }

  /* Vorschlagsliste bei den Interessen im Textfluss statt schwebend (F7):
     verlängert den Inhalt, lässt sich so immer ganz sichtbar scrollen. */
  .combo-suggestions.open { position: static; margin-top: 6px; }
}

/* Tablet: die Spalte grenzt sich mit einer feinen Linie vom Rand ab. */
@media (hover: none) and (pointer: coarse) and (min-width: 521px),
       (min-width: 521px) and (max-width: 600px) {
  .phone-frame { box-shadow: 0 0 0 1px var(--color-border); }
}
