﻿/* Kribo Design-Theme – Tokens + Bausteine. Spezifikation: docs/design/ */

/* ---- Schriften (gebündelt, offline) ---- */
@font-face{font-family:'Bricolage Grotesque';font-weight:600;font-display:swap;src:url('fonts/bricolage-600.ttf') format('truetype')}
@font-face{font-family:'Bricolage Grotesque';font-weight:700;font-display:swap;src:url('fonts/bricolage-700.ttf') format('truetype')}
@font-face{font-family:'Bricolage Grotesque';font-weight:800;font-display:swap;src:url('fonts/bricolage-800.ttf') format('truetype')}
@font-face{font-family:'Hanken Grotesk';font-weight:400;font-display:swap;src:url('fonts/hanken-400.ttf') format('truetype')}
@font-face{font-family:'Hanken Grotesk';font-weight:500;font-display:swap;src:url('fonts/hanken-500.ttf') format('truetype')}
@font-face{font-family:'Hanken Grotesk';font-weight:600;font-display:swap;src:url('fonts/hanken-600.ttf') format('truetype')}
@font-face{font-family:'Hanken Grotesk';font-weight:700;font-display:swap;src:url('fonts/hanken-700.ttf') format('truetype')}

/* ---- Tokens (Light) ---- */
:root{
  color-scheme:light;
  --kri-paper:#F6EEDD; --kri-surface:#FFFBF3; --kri-tint:#F3E4CC;
  /* --kri-primary (hell #123A46 / dunkel #8FD3CC weiter unten) ist auf Android zusätzlich hartcodiert
     in Platforms/Android/MainActivity.cs (native Polsterung der Statusleiste, siehe Kommentar dort) —
     bei einer Änderung hier auch dort nachziehen. */
  --kri-ink:#23201A; --kri-ink2:#6E655A; --kri-primary:#123A46; --kri-line:#E4D8C2; --kri-card-border:transparent;
  /* Sekundärer Text/Icon-Ton AUF --kri-primary (Sidebar, Drawer, mobile App-Bar). Muss ein eigener
     Token sein: --kri-primary wechselt zwischen dunklem Petrol (hell) und hellem Mint (dunkel), ein
     festes Creme-rgba() wäre im Dunkelmodus Creme auf Mint (~1.3:1) und praktisch unlesbar. */
  --kri-on-primary-soft:#B1B8B0; --kri-on-primary-veil:rgba(255,255,255,.08); --kri-on-primary-line:rgba(255,255,255,.14);
  --kri-accent:#FA6A1E; --kri-accent-soft:#FCE0C6; --kri-accent-text:#AE480A;
  --kri-teal:#12A594; --kri-yellow:#F6B60B; --kri-pink:#EC5A86;
  --kri-ok:#2FA05E; --kri-warn:#D99400; --kri-danger:#E0492F; --kri-danger-text:#B83A22; --kri-ok-text:#1B6E3D;
  --kri-display:'Bricolage Grotesque', system-ui, sans-serif;
  --kri-sans:'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --kri-mono:ui-monospace, Consolas, monospace;
  --kri-shadow:drop-shadow(0 6px 15px rgba(60,40,20,.14));
  /* Kantige Neigung: EINE zentrale Stellschraube. --kri-skew = Basis-Eckversatz in px.
     Die drei Stufen leiten sich davon ab und geben den Flächen etwas Unregelmässigkeit.
     Neigung global ändern? Nur --kri-skew anpassen. Ganz gerade? --kri-skew:0. */
  --kri-skew:5px;
  --kri-skew-s:calc(var(--kri-skew) * .4);
  --kri-skew-m:var(--kri-skew);
  --kri-skew-l:calc(var(--kri-skew) * 1.3);
  /* Farbstrich der Hauptkomponente in der Tagesleiste der Wochenübersicht (Menüplanung F, A37): nur Dekoration neben Text,
     Kontrast >= 3:1 zu surface und paper (scripts/check_contrast.py). */
  --kri-mc-pasta:#9A6B00; --kri-mc-kartoffeln:#8B5A2B; --kri-mc-reis:#4F7595; --kri-mc-brot:#A0522D;
  --kri-mc-huelsenfruechte:#3E7D3A; --kri-mc-getreide:#7F7216; --kri-mc-teig:#B0476A; --kri-mc-keine:#7A7468;
}
/* ---- Tokens (Dark) ---- */
/* bewusst dupliziert (Media-Query + data-theme), synchron halten: der Media-Query-Block greift für
   System-Dunkelmodus ohne explizite Nutzerwahl, der data-theme-Block für die manuelle Umschaltung
   (kriboTheme.toggle setzt data-theme direkt) — beide müssen dieselben Werte tragen. */
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  color-scheme:dark;
  --kri-paper:#14100A; --kri-surface:#241D14; --kri-tint:#342A1D;
  --kri-ink:#F8F1E6; --kri-ink2:#C8BBA6; --kri-primary:#8FD3CC; --kri-line:#4A3E2D; --kri-card-border:#4A3E2D;
  /* --kri-primary ist hier ein HELLES Mint → der Ton darauf muss dunkel sein, ebenso die Schleier. */
  --kri-on-primary-soft:#2F3B35; --kri-on-primary-veil:rgba(0,0,0,.08); --kri-on-primary-line:rgba(0,0,0,.16);
  --kri-accent:#FF864B; --kri-accent-soft:#43301A; --kri-accent-text:#FF864B;
  --kri-teal:#3CC6B4; --kri-yellow:#F7C948; --kri-pink:#F58AAB; --kri-danger-text:#FF8A70; --kri-ok-text:#6FD39A;
  --kri-mc-pasta:#E8B84A; --kri-mc-kartoffeln:#C99A6B; --kri-mc-reis:#8FB2D0; --kri-mc-brot:#E08A5E;
  --kri-mc-huelsenfruechte:#7FC77A; --kri-mc-getreide:#CDC25A; --kri-mc-teig:#E58AAA; --kri-mc-keine:#A39C90;
  --kri-shadow:drop-shadow(0 8px 20px rgba(0,0,0,.6));
}}
/* bewusst dupliziert (Media-Query + data-theme), synchron halten — siehe Kommentar am Media-Query-Block
   oben. */
:root[data-theme="dark"]{
  color-scheme:dark;
  --kri-paper:#14100A; --kri-surface:#241D14; --kri-tint:#342A1D;
  --kri-ink:#F8F1E6; --kri-ink2:#C8BBA6; --kri-primary:#8FD3CC; --kri-line:#4A3E2D; --kri-card-border:#4A3E2D;
  /* --kri-primary ist hier ein HELLES Mint → der Ton darauf muss dunkel sein, ebenso die Schleier. */
  --kri-on-primary-soft:#2F3B35; --kri-on-primary-veil:rgba(0,0,0,.08); --kri-on-primary-line:rgba(0,0,0,.16);
  --kri-accent:#FF864B; --kri-accent-soft:#43301A; --kri-accent-text:#FF864B;
  --kri-teal:#3CC6B4; --kri-yellow:#F7C948; --kri-pink:#F58AAB; --kri-danger-text:#FF8A70; --kri-ok-text:#6FD39A;
  --kri-mc-pasta:#E8B84A; --kri-mc-kartoffeln:#C99A6B; --kri-mc-reis:#8FB2D0; --kri-mc-brot:#E08A5E;
  --kri-mc-huelsenfruechte:#7FC77A; --kri-mc-getreide:#CDC25A; --kri-mc-teig:#E58AAA; --kri-mc-keine:#A39C90;
  --kri-shadow:drop-shadow(0 8px 20px rgba(0,0,0,.6));
}

/* ---- Basis ---- */
body{font-family:var(--kri-sans);background:var(--kri-paper);color:var(--kri-ink)}
/* Barrierefreier Standard-Link (überschreibt Template-Blau aus app.css) */
a{color:var(--kri-accent-text)}
.kri-display{font-family:var(--kri-display)}
.kri-center{min-height:100dvh;display:flex;align-items:center;justify-content:center;padding:24px}
.kri-stack{display:flex;flex-direction:column;gap:16px}

/* ---- Öffentliche Seiten (/login, /legal, /welcome/name; PublicLayout) ---- */
.kri-public{min-height:100dvh;display:flex;flex-direction:column}
.kri-public-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 20px}
.kri-public-brand{display:inline-flex;align-items:center;gap:10px;font-family:var(--kri-display);font-weight:800;font-size:20px;color:var(--kri-ink);text-decoration:none}
.kri-public-brand:hover,.kri-public-brand:focus{color:var(--kri-ink)}
.kri-public-lang{display:flex;gap:14px}
.kri-public-lang a,.kri-public-foot a{display:inline-flex;align-items:center;min-height:44px;font-size:14px}
.kri-public-main{flex:1;display:flex;align-items:flex-start;justify-content:center;padding:16px 20px 32px}
.kri-public-foot{padding:8px 20px 20px;text-align:center}
.kri-public-card{width:100%;max-width:400px}
.kri-public-card--wide{max-width:720px}
.kri-public-note{margin:14px 0 0;font-size:13px}
.kri-prose h2{font-family:var(--kri-display);font-weight:700;font-size:17px;margin:22px 0 6px;color:var(--kri-ink)}
.kri-prose p,.kri-prose li{line-height:1.55;margin:0 0 10px}
.kri-prose ol,.kri-prose ul{padding-left:22px;margin:0 0 10px}

/* ---- Anmelde-Knöpfe der Anbieter (D18, docs/specs/auth.md) ----
   BEWUSSTE AUSNAHME vom kantigen Design, nur für diese Knöpfe: Google und Apple schreiben Form, Farben und Abstände ihrer Knöpfe vor
   (Google Identity Branding Guidelines, Apple HIG "Sign in with Apple"). scripts/check_design.py nimmt ausschließlich Zeilen aus,
   die mit ".kri-provider-btn{" beginnen. Mindesthöhe 48px (große Touch-Ziele). */
.kri-provider-list{display:flex;flex-direction:column;gap:12px;margin-top:18px}
.kri-provider-btn{display:flex;align-items:center;justify-content:center;gap:12px;min-height:48px;padding:0 16px;box-sizing:border-box;width:100%;border-radius:4px;border:1px solid transparent;font-family:Roboto,"Helvetica Neue",Arial,system-ui,sans-serif;font-size:14px;font-weight:500;line-height:1.2;text-decoration:none;cursor:pointer}
.kri-provider-btn:focus-visible{outline:3px solid var(--kri-accent-text);outline-offset:2px}
.kri-provider-logo{width:20px;height:20px;flex:none}
.kri-provider-logo--apple{width:18px;height:22px}
/* Google: helle Variante (Standard); im Dunkelmodus die dunkle Variante der Richtlinie. */
.kri-provider-btn--google{background:#FFFFFF;color:#1F1F1F;border-color:#747775}
.kri-provider-btn--google:hover,.kri-provider-btn--google:focus,.kri-provider-btn--google:active{background:#F2F2F2;color:#1F1F1F}
/* Apple: schwarz mit weißer Schrift; auf dunklem Grund die weiße Variante mit Rand. */
.kri-provider-btn--apple{background:#000000;color:#FFFFFF;border-color:#000000}
.kri-provider-btn--apple:hover,.kri-provider-btn--apple:focus,.kri-provider-btn--apple:active{background:#262626;color:#FFFFFF}
/* Apple HIG: Systemschrift (San Francisco), Titelgröße etwa 43 % der Knopfhöhe (48px -> 19-20px). */
.kri-provider-btn--apple{font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text",system-ui,sans-serif;font-size:19px}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .kri-provider-btn--google{background:#131314;color:#E3E3E3;border-color:#8E918F}
  :root:not([data-theme="light"]) .kri-provider-btn--google:hover{background:#2A2A2C}
  :root:not([data-theme="light"]) .kri-provider-btn--apple{background:#FFFFFF;color:#000000;border-color:#FFFFFF}
  :root:not([data-theme="light"]) .kri-provider-btn--apple:hover{background:#E6E6E6}
}
:root[data-theme="dark"] .kri-provider-btn--google{background:#131314;color:#E3E3E3;border-color:#8E918F}
:root[data-theme="dark"] .kri-provider-btn--google:hover{background:#2A2A2C}
:root[data-theme="dark"] .kri-provider-btn--apple{background:#FFFFFF;color:#000000;border-color:#FFFFFF}
:root[data-theme="dark"] .kri-provider-btn--apple:hover{background:#E6E6E6}


/* ---- Eigener Datepicker (Kribo-Stil) ---- */
.kri-dp-field{display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:pointer;text-align:left}
.kri-dp-field .kri-dp-placeholder{color:var(--kri-ink2)}
.kri-dp-backdrop{position:fixed;inset:0;z-index:40}
.kri-dp-pop{position:absolute;z-index:41;top:calc(100% + 6px);left:0;min-width:300px;background:var(--kri-surface);border:1px solid var(--kri-line);padding:14px;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-dp-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.kri-dp-title{font-family:var(--kri-display);font-weight:700;font-size:15px;text-transform:capitalize}
.kri-dp-nav{background:var(--kri-tint);color:var(--kri-primary);border:none;cursor:pointer;width:32px;height:32px;font-size:18px;line-height:1;display:flex;align-items:center;justify-content:center;clip-path:polygon(0 8%,100% 0,100% 92%,0 100%)}
.kri-dp-nav:hover{filter:brightness(.96)}
.kri-dp-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.kri-dp-wd{text-align:center;font-family:var(--kri-mono);font-size:11px;color:var(--kri-ink2);padding:4px 0}
.kri-dp-day{aspect-ratio:1;border:none;background:transparent;color:var(--kri-ink);font-family:var(--kri-sans);font-size:14px;cursor:pointer;clip-path:polygon(0 8%,100% 0,100% 92%,0 100%)}
.kri-dp-day:hover{background:var(--kri-tint)}
.kri-dp-day--today{box-shadow:inset 0 0 0 1.5px var(--kri-primary)}
.kri-dp-day--sel{background:var(--kri-accent);color:#241206;font-weight:700}
.kri-dp-foot{display:flex;justify-content:space-between;gap:8px;margin-top:10px}

/* ---- Ausleih-Belegungskalender (LendingAvailabilityCalendar) ---- */
.kri-lending-day--booked{box-shadow:inset 0 0 0 1.5px var(--kri-danger-text);color:var(--kri-danger-text)}
.kri-lending-legend{display:inline-block;width:12px;height:12px;box-shadow:inset 0 0 0 1.5px var(--kri-danger-text)}
.kri-lending-legend--selected{background:var(--kri-accent);box-shadow:none}

/* ---- Eigentums-Report (druckbar) ---- */
.kri-report-onlyprint{display:none}
.kri-report-owner{font-family:var(--kri-display);font-weight:700;font-size:16px;margin:14px 0 4px}
.kri-report-list{margin:0 0 8px;padding-left:20px}
.kri-report-list li{margin:3px 0}
@media print{
  .kri-sidebar,.kri-top,.kri-footer,.kri-no-print,.kri-dash-aside{display:none !important}
  .kri-shell,.kri-main-col,.kri-workspace{display:block;height:auto;overflow:visible}
  .kri-report-onlyprint{display:block;font-family:var(--kri-display);font-weight:800;font-size:22px;margin:0 0 14px}
  .kri-card{clip-path:none;border:1px solid #ccc} /*ok*/
  body{background:#fff}
}

/* ---- Termin-Datum-Badge ---- */
.kri-event-date{flex:none;width:52px;text-align:center;background:var(--kri-accent-soft);color:var(--kri-accent-text);padding:6px 0;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-event-day{display:block;font-family:var(--kri-display);font-weight:800;font-size:20px;line-height:1}
.kri-event-mon{display:block;font-family:var(--kri-mono);font-size:11px;text-transform:uppercase;margin-top:2px}

/* ---- Laden-Schnellauswahl (Chips) ---- */
.kri-store-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.kri-chip{font-family:var(--kri-sans);font-size:13px;font-weight:600;padding:5px 12px;border:none;cursor:pointer;background:var(--kri-tint);color:var(--kri-primary);clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-chip:hover{filter:brightness(.97)}
.kri-chip--on{background:var(--kri-accent);color:#241206}
.kri-chip:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
/* Tage der Wochenleiste: Rahmen (heute) und Balken (gewählt) sind schon --kri-primary, ein Fokusring in derselben Farbe fiele nicht auf.
   Darum ein doppelter Ring in Tinte und Fläche. */
.kri-weektab:focus-visible{outline:none;box-shadow:inset 0 0 0 3px var(--kri-ink),inset 0 0 0 5px var(--kri-surface)}

/* ---- Kompakte Vorschau-Karten (Übersicht: Einkaufsliste/Ausgaben) ---- */
.kri-card--compact{padding:16px 18px 8px}
.kri-card--compact .kri-member{padding:5px 0;line-height:1.25}
/* letzter Listeneintrag: kein zusätzlicher Abstand nach unten */
.kri-card--compact .kri-member:last-child{padding-bottom:0}

/* ---- Filter-Leiste über Listen ---- */
.kri-filters{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:16px}
.kri-filter{font-family:var(--kri-sans);font-size:14px;padding:8px 12px;border:none;background:var(--kri-surface);color:var(--kri-ink);clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-filter:focus{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-filter-search{flex:1;min-width:160px}

/* ---- Karte ---- */
.kri-card{background:var(--kri-surface);color:var(--kri-ink);padding:26px;border:1px solid var(--kri-card-border);clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%)}
/* Form-Varianten (seeded via Tilt.Quad) – MÜSSEN nach .kri-card stehen, sonst überschreibt
   .kri-cards clip-path die Varianz und alle Karten sehen gleich aus. */
.kri-q1{clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%)}
.kri-q2{clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)))}
.kri-q3{clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-l)),0 100%)}
.kri-q4{clip-path:polygon(0 0,100% var(--kri-skew-l),100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-q5{clip-path:polygon(0 var(--kri-skew-l),100% var(--kri-skew-s),100% 100%,0 calc(100% - var(--kri-skew-m)))}
/* Karten mit schwebendem Kontextmenü (z. B. Mitglieder-Kebab): clip-path erzeugt einen
   Clipping-Kontext, der position:absolute-Kindelemente (.kri-menu) am Kartenrand abschneidet.
   Diese Variante verzichtet auf den Eck-Anschnitt, damit das Menü vollständig sichtbar bleibt;
   die Karte bleibt eckig/rechteckig (kein border-radius, weiterhin kantig). Muss nach den
   .kri-qN-Regeln stehen, um deren clip-path zuverlässig zu überschreiben. */
.kri-card--overflow-visible{clip-path:none;overflow:visible} /*ok*/
.kri-title{font-family:var(--kri-display);font-weight:800;font-size:22px;letter-spacing:-.01em;margin:0 0 4px;color:var(--kri-ink)}
.kri-muted{color:var(--kri-ink2);font-size:14px;margin:0 0 18px}
/* Karten-Untertitel (SectionCard-Komponente) — bislang an 48 Stellen als
   <h2 class="kri-title" style="font-size:18px"> von Hand nachgebaut. */
.kri-card-title{font-family:var(--kri-display);font-weight:800;letter-spacing:-.01em;margin:0 0 4px;color:var(--kri-ink);font-size:18px}

/* ---- Formularfeld ---- */
.kri-field{margin-bottom:14px}
.kri-field > label{display:block;font-size:13px;font-weight:600;margin-bottom:6px;color:var(--kri-ink)}
.kri-input{width:100%;font-family:var(--kri-sans);font-size:15px;padding:14px 16px;border:none;background:var(--kri-tint);color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-field:nth-of-type(even) .kri-input{clip-path:polygon(0 0,100% var(--kri-skew-s),100% 100%,0 calc(100% - var(--kri-skew-s)))}
.kri-input::placeholder{color:var(--kri-ink2)}
/* Datums-/Zeitfelder ins Theme ziehen: Höhe wie Textfelder, Picker-Icon dezent (folgt via color-scheme) */
.kri-input[type="date"],.kri-input[type="datetime-local"],.kri-input[type="time"]{min-height:49px;font-family:var(--kri-sans)}
.kri-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.6;transition:opacity .15s}
.kri-input::-webkit-calendar-picker-indicator:hover{opacity:1}
/* barrierefreier Fokus trotz clip-path: inset-Ring bleibt sichtbar */
.kri-input:focus{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}

/* ---- Buttons ---- */
.kri-btn{font-family:var(--kri-sans);font-weight:600;font-size:15px;padding:13px 28px;border:none;cursor:pointer;
  color:#241206;background:var(--kri-accent);clip-path:polygon(0 0,100% var(--kri-skew-l),100% 100%,0 calc(100% - var(--kri-skew-l)));transition:filter .15s}
.kri-btn:hover{filter:brightness(.96)}
.kri-btn:disabled{opacity:.6;cursor:not-allowed}
.kri-btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px rgba(0,0,0,.55)}
.kri-btn--full{width:100%}
/* bewusster Alias: Primär-Stil ist der kri-btn-Default (siehe .kri-btn oben); die Klasse existiert nur,
   damit Markup, das eine Aktion ausdrücklich als primär kennzeichnen will, das lesbar tun kann. */
.kri-btn--primary{}
/* Sekundär: gefüllte Fläche (kein Rahmen) – clip-path würde einen Rahmen kappen.
   Tint-Hintergrund + dunkler Text = kantig UND kontraststark (WCAG AA). */
.kri-btn--secondary{background:var(--kri-tint);color:var(--kri-primary);border:none;clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%)}
.kri-btn--secondary:hover{filter:brightness(.97)}
.kri-btn--secondary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
/* Ghost: reiner Text-Button – keine Fläche, daher kein clip-path nötig. */
.kri-btn--ghost{background:transparent;color:var(--kri-accent-text);border:none;clip-path:none;padding:8px 12px} /*ok*/
/* Danger: gefüllte Signalfläche (z. B. destruktive Primäraktion in einem Dialog) — exakt dieselbe
   Farbkombination wie .kri-toast--danger (dort bereits WCAG-AA-geprüft, ~4.8:1). */
.kri-btn--danger{background:var(--kri-danger);color:#1a0603}
/* Icon-only Modifikator: EIN Symbol statt einer Beschriftung (z. B. die Anruf-Knöpfe im Gesprächskopf,
   MessagesThread.razor) — ohne ihn machte das feste padding:13px 28px von .kri-btn aus einem einzelnen
   Zeichen einen textbreiten Knopf. Ändert NUR Innenabstand/Fläche auf die Mindestgrösse für
   Berührungsziele (44x44px), die das Repo bereits an fünf Stellen einhält (.kri-icon-btn/.kri-menu-trigger/
   .kri-dp-nav/.kri-detail-modal-close/.kri-mtb-btn) — Fläche, Farbe und der schräge clip-path der
   jeweiligen Variante (z. B. --secondary) bleiben unverändert, das ist Kribos kantiger Charakter
   (scripts/check_design.py prüft ihn). Muss NACH .kri-btn stehen (gleiche Spezifität, sonst gewinnt
   dessen padding). */
.kri-btn--icon{width:44px;height:44px;padding:0;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}
/* Button-Varianz: benachbarte Buttons leicht unterschiedlich schräg (nth-of-type hat höhere
   Spezifität und überschreibt daher die Variant-clip-paths oben). Ghost bleibt ohne Fläche. */
.kri-btn:nth-of-type(even):not(.kri-btn--ghost){clip-path:polygon(0 0,100% var(--kri-skew-l),100% 100%,0 calc(100% - var(--kri-skew-l)))}
.kri-btn:nth-of-type(3n):not(.kri-btn--ghost){clip-path:polygon(0 var(--kri-skew-l),100% var(--kri-skew-s),100% calc(100% - var(--kri-skew-s)),0 100%)}

/* Grosser Knopf für die eine wichtigste Aktion einer Seite (z. B. "Zuteilung ansehen" beim Wichteln).
   Auch als <a class="kri-btn"> brauchbar: inline-block + zentriert, damit min-height/padding greifen.
   Mindesthöhe 52px = grosses Touch-Ziel. Fläche/Farbe/clip-path bleiben die von .kri-btn. */
.kri-btn--lg{display:inline-block;box-sizing:border-box;min-height:52px;padding:15px 36px;font-size:17px;text-align:center;text-decoration:none}
/* Hervorgehobene Karte ("Spotlight"): trägt die wichtigste Info einer Seite ganz oben. Fläche ist
   --kri-accent-soft (hell Pfirsich / dunkel Braun), Text --kri-ink bzw. --kri-ink2 -- beide Modi mit
   gutem Kontrast; der Knopf darin ist --kri-accent und hebt sich von der weichen Fläche ab. */
.kri-spotlight{background:var(--kri-accent-soft);margin-bottom:16px}
.kri-spotlight .kri-title{font-size:22px;margin-bottom:6px}
.kri-spotlight-text{color:var(--kri-ink);font-size:15px;margin:0 0 16px}
.kri-spotlight-note{color:var(--kri-ink2);font-size:13px;margin:10px 0 0}
/* Klickbare Kachel ohne <a> um den ganzen Inhalt: der Titel-Link (.kri-card-link) spannt per ::after
   die gesamte Karte auf; weitere Knöpfe in der Karte liegen per z-index darüber und bleiben einzeln
   anklickbar (ein <a> darf nicht in einem <a> stecken). */
.kri-card--link{position:relative}
.kri-card--link:hover{filter:brightness(.98)}
.kri-card-link::after{content:"";position:absolute;inset:0}
.kri-card--link .kri-btn{position:relative;z-index:1}

/* ---- Einladungscode-Chip ---- */
/* flex-wrap: Code (Mono 22px, letter-spacing) + Owner-Button "Neu generieren" passen auf 360px
   nach Karten-Padding nicht mehr nebeneinander (Button lief über den clip-path-Rand hinaus /
   erzeugte horizontales Scrollen). Der Button rutscht auf schmalen Screens in eine eigene Zeile. */
.kri-invite{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;background:var(--kri-accent-soft);
  padding:15px 17px;margin-top:8px;clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)))}
.kri-cap{font-family:var(--kri-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--kri-ink2)}
.kri-code{font-family:var(--kri-mono);font-size:22px;letter-spacing:.16em;color:var(--kri-primary);font-weight:700}

/* ---- Mitglieder / Avatar / Badge ---- */
.kri-member{display:flex;align-items:center;gap:12px;padding:8px 0;border-top:1px solid var(--kri-line)}
/* Kontextmenü (3-Punkte) für Zeilen-Aktionen, z. B. Mitglieder */
.kri-menu-wrap{position:relative;flex:none}
.kri-menu-trigger{padding:6px;min-width:0}
.kri-menu-scrim{position:fixed;inset:0;z-index:40}
.kri-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:41;min-width:170px;background:var(--kri-surface);border:1px solid var(--kri-line);filter:var(--kri-shadow);display:flex;flex-direction:column;padding:4px}
.kri-menu-item{background:transparent;border:none;cursor:pointer;text-align:left;padding:9px 12px;font-size:14px;color:var(--kri-ink)}
.kri-menu-item:hover:not(:disabled){background:var(--kri-tint)}
.kri-menu-item:disabled{opacity:.5;cursor:default}
.kri-menu-item--danger{color:var(--kri-danger-text)}
/* Linksbündige Variante: .kri-menu haengt normal am RECHTEN Rand seines Wrappers, weil es fast immer
   am Ende einer Zeile ausgeloest wird. Sitzt der Ausloeser dagegen am linken Rand einer breiten Zeile
   (Anhang-Plus im Messenger, Plus im Community-Chat), schob right:0 das 170px breite Panel nach links
   aus dem Inhaltsbereich heraus -- unter die Navigationsleiste UND unter den eigenen .kri-menu-scrim,
   der damit oberstes Element ueber den Eintraegen war: ein Klick schloss das Menue, statt den Eintrag
   auszuloesen. Im Browser gemessen 2026-09-16. */
.kri-menu--start{right:auto;left:0}
/* Nach OBEN aufklappende Variante: .kri-menu haengt normal UNTER seinem Ausloeser (top:100%+4px),
   weil der Ausloeser fast immer am oberen Rand eines Inhalts sitzt. Sitzt er dagegen in einer fest
   unten verankerten Eingabezeile (.kri-composer, siehe dort) -- Anhang-Plus im Messenger, Plus im
   Community-Chat --, wuerde das Panel nach unten aus dem sichtbaren Bereich hinaus- oder unter eine
   mobile Tab-Leiste aufklappen. Beide Stellen kombinieren dies bereits mit AlignStart (derselbe
   Ausloeser sitzt zugleich am LINKEN Rand seiner Zeile). */
.kri-menu--up{top:auto;bottom:calc(100% + 4px)}
/* Multiselect-Panel (z. B. Community-Auswahl je Widget): durchsuchbare Checkbox-Liste im
   selben schwebenden .kri-menu-Muster, nur breiter/scrollbar für viele Einträge. */
.kri-menu--list{width:240px;max-width:calc(100vw - 32px);max-height:260px;overflow-y:auto}
.kri-menu-search{width:100%;box-sizing:border-box}
.kri-menu-check{display:flex;align-items:center;gap:8px;padding:9px 8px;font-size:13.5px;cursor:pointer;min-height:36px}
.kri-menu-check:hover{background:var(--kri-tint)}
.kri-menu-check input{flex:none;width:18px;height:18px}
.kri-avatar{width:40px;height:40px;background:var(--kri-tint);color:var(--kri-primary);display:flex;align-items:center;justify-content:center;
  font-family:var(--kri-display);font-weight:800;clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%)}
.kri-avatar--teal{background:var(--kri-teal);color:#0c211d} .kri-avatar--pink{background:var(--kri-pink);color:#2a0d16} .kri-avatar--yellow{background:var(--kri-yellow);color:#3a2a00}
.kri-badge{font-family:var(--kri-mono);font-size:11.5px;font-weight:600;padding:4px 11px;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-badge--owner{background:var(--kri-accent);color:#241206} .kri-badge--score{background:var(--kri-tint);color:var(--kri-primary)}
/* Signalbadge (z. B. "gesperrt") — dieselbe Farbkombination wie .kri-btn--danger/.kri-toast--danger. */
.kri-badge--danger{background:var(--kri-danger);color:#1a0603}

/* ---- Kategorie-Farben (Feature-Kennzeichnung: Einkaufsliste/Aufgaben/Ausgaben/Pinnwand) ---- */
:root{
  --kri-cat-shopping:var(--kri-teal); --kri-cat-chores:var(--kri-yellow);
  --kri-cat-expenses:var(--kri-pink); --kri-cat-pinboard:var(--kri-primary);
}
.kri-cat--shopping{--kri-cat:var(--kri-cat-shopping)} .kri-cat--chores{--kri-cat:var(--kri-cat-chores)}
.kri-cat--expenses{--kri-cat:var(--kri-cat-expenses)} .kri-cat--pinboard{--kri-cat:var(--kri-cat-pinboard)}
[class*="kri-cat--"]:not(.kri-dashboard){border-left:4px solid var(--kri-cat)}

/* ---- Overlay / Dialog ---- */
.kri-overlay{position:fixed;inset:0;background:rgba(20,16,10,.55);display:flex;align-items:center;justify-content:center;
  padding:20px;z-index:1000}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .kri-overlay{background:rgba(0,0,0,.65)}}
:root[data-theme="dark"] .kri-overlay{background:rgba(0,0,0,.65)}
.kri-dialog{background:var(--kri-surface);color:var(--kri-ink);padding:24px;filter:var(--kri-shadow);border:1px solid var(--kri-card-border);
  clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%);max-width:420px;width:100%;max-height:90vh;overflow:auto}
.kri-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px;flex-wrap:wrap}

/* ---- Toast ---- */
.kri-toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);padding:13px 20px;font-family:var(--kri-sans);
  font-size:14px;font-weight:600;max-width:90vw;filter:var(--kri-shadow);z-index:1100;background:var(--kri-surface);color:var(--kri-ink);clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)));
  display:flex;align-items:center;gap:14px}
.kri-toast--ok{background:var(--kri-ok);color:#0f2916}
.kri-toast--warn{background:var(--kri-warn);color:#3a2600}
.kri-toast--danger{background:var(--kri-danger);color:#1a0603}
.kri-toast-close{background:none;border:0;padding:0;margin:0;color:inherit;font:inherit;font-weight:700;line-height:1;cursor:pointer;flex-shrink:0}

/* ---- Detail-Modal (Kleinanzeigen-artiges Item-Detail, siehe DetailModal.razor) ---- */
.kri-detail-modal{max-width:560px;padding:0;overflow:hidden}
.kri-detail-modal-head{position:relative}
.kri-detail-modal-photo{width:100%;height:260px;object-fit:cover;display:block;background:var(--kri-tint)}
.kri-detail-modal-photo--empty{display:flex;align-items:center;justify-content:center}
.kri-detail-modal-close{position:absolute;top:10px;right:10px;background:var(--kri-surface);padding:6px 12px;filter:var(--kri-shadow)}
.kri-detail-modal-body{padding:20px 24px}
.kri-detail-modal-titlerow{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:8px}
.kri-detail-modal-price{font-family:var(--kri-mono);font-weight:700;font-size:17px;color:var(--kri-primary);white-space:nowrap}
.kri-detail-modal-badges{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.kri-detail-modal-description{white-space:pre-wrap;margin:0 0 14px}
.kri-detail-modal-meta{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px;font-size:13px;color:var(--kri-ink2)}
.kri-detail-modal-actions{padding:0 24px 20px}

/* ---- Leerer Zustand / Ladezustand / Fehler ---- */
.kri-empty{text-align:center;color:var(--kri-ink2);padding:36px 16px}
/* Einzige Ladeanzeige der App (das gedrehte Rad .kri-spinner ist entfallen -- zwei Anzeigen
   fuer dieselbe Aussage waren eine zu viel). Unterschieden wird nur ueber die Groesse des
   Zeichens, siehe LoadingIndicator.Size. */
.kri-loading{display:flex;align-items:center;justify-content:center;gap:12px;padding:24px;color:var(--kri-ink2)}

/* ---- Ladeanzeige mit dem Markenzeichen ----
   Das Zeichen sind DREI ineinandergreifende Teile: „verschiedene Menschen, ein Ganzes"
   (docs/brand.md). Genau das animiert hier — die Teile treten nacheinander hervor und fuegen sich
   zusammen. Die Bewegung erzaehlt also, was die Marke bedeutet, statt ein Rad zu drehen.

   Die Regeln aus brand.md sind eingehalten: nur GLEICHMAESSIGE Skalierung des ganzen Zeichens
   (nie x/y getrennt), keine Rundungen, keine Verlaeufe, keine Schatten -- und die Fugen zwischen
   den drei Teilen werden weder geschlossen noch ueberlappt, weil sich die Teile gar nicht bewegen:
   animiert wird ausschliesslich ihre Deckkraft. */
.kri-brandload{display:inline-block;line-height:0;animation:kri-brand-breathe 2.8s ease-in-out infinite}
.kri-brandload svg{display:block}
/* Der Versatz muss GROESSER sein als die Ein-/Ausblendzeit, sonst verblassen alle drei Teile
   gleichzeitig und es sieht aus, als wuerde das ganze Zeichen flackern -- genau das war der erste
   Versuch (im Emulator gesehen). Mit 0,22 s Versatz auf 2,2 s Dauer treten sie nacheinander hervor:
   erst eines, dann zwei, dann das Ganze. */
.kri-brandload polygon{animation:kri-brand-assemble 2.2s ease-in-out infinite}
.kri-brandload polygon:nth-of-type(1){animation-delay:0s}
.kri-brandload polygon:nth-of-type(2){animation-delay:.22s}
.kri-brandload polygon:nth-of-type(3){animation-delay:.44s}
@keyframes kri-brand-assemble{0%{opacity:.14}22%,78%{opacity:1}100%{opacity:.14}} /*ok*/
@keyframes kri-brand-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.03)}} /*ok*/

/* Ganzseitige Ladeanzeige: das Zeichen mittig, Text darunter. */
.kri-loadpage{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:18px;padding:64px 24px;color:var(--kri-ink2);text-align:center}
.kri-loadpage-text{font-family:var(--kri-display);font-weight:800;font-size:16px;color:var(--kri-ink)}

/* Bei reduzierter Bewegung faellt das ATMEN weg -- das ist die einzige echte Bewegung hier
   (transform:scale). Das Zusammensetzen bleibt, denn es bewegt nichts: es aendert ausschliesslich
   die Deckkraft, und genau darauf zielt prefers-reduced-motion nicht.

   Vorher stand hier `animation:none` fuer BEIDES und opacity zurueck auf 1. Das Ergebnis war ein
   vollstaendiges, voellig regloses Zeichen -- fuer den Betrachter nicht von einem Logo zu
   unterscheiden. Wo <LoadingIndicator> ohne Text steht, fehlte damit JEDES Signal, dass ueberhaupt
   etwas laedt. Die Zusage 'keine Bewegung' zu halten heisst nicht, gar keine Rueckmeldung mehr zu
   geben.

   Die ruhige Fassung ist bewusst langsamer (3,2 s statt 2,2 s), hat weniger Kontrastsprung
   (.45 statt .14) und laeuft ohne Versatz gleichmaessig ueber alle drei Teile: kein Nacheinander,
   kein Flackern. Mit rund 0,3 Hz liegt sie weit unter der Blitzgrenze von 3 Hz (WCAG 2.3.1). */
@media (prefers-reduced-motion:reduce){
  .kri-brandload{animation:none}
  .kri-brandload polygon{animation:kri-brand-calm 3.2s ease-in-out infinite;animation-delay:0s}
}
@keyframes kri-brand-calm{0%,100%{opacity:.45}50%{opacity:1}} /*ok*/

/* Die Startanzeige in src/Kribo/wwwroot/index.html ist statisches HTML -- sie kann die Farben des
   Zeichens nicht wie BrandMark.razor ueber einen Parameter waehlen. Im Dunkelmodus laege das dunkle
   Petrol (#123A46) sonst auf fast schwarzem Grund (#14100A). Deshalb hier dieselben Werte, die
   BrandMark mit OnDark verwendet -- keine erfundenen Farben, siehe docs/brand.md. */
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  .kri-brandload .kri-bm-a{fill:#F6EEDD}
  .kri-brandload .kri-bm-c{fill:#8FD3CC}
}}
:root[data-theme="dark"] .kri-brandload .kri-bm-a{fill:#F6EEDD}
:root[data-theme="dark"] .kri-brandload .kri-bm-c{fill:#8FD3CC}
.kri-error{color:var(--kri-danger-text);font-size:13.5px;margin:8px 0}

/* ---- Grosse Touch-Checkbox (Einkaufsliste) ---- */
.kri-check{appearance:none;width:26px;height:26px;min-width:26px;border:1.8px solid var(--kri-ink2);background:var(--kri-surface);
  cursor:pointer;position:relative;flex-shrink:0}
.kri-check:checked{background:var(--kri-primary);border-color:var(--kri-primary)}
/* Häkchenfarbe bewusst var(--kri-surface) statt hartcodiertem #fff: --kri-primary kippt im
   Dunkelmodus auf helles Mint (#8FD3CC) — weiss darauf wäre nur noch ~1.7:1. --kri-surface ist
   in beiden Modi das Gegenstück zu --kri-primary (hell auf dunklem Petrol, dunkel auf hellem Mint)
   und bleibt so in beiden Modi kontrastreich. Analog zum --kri-on-primary-*-Muster oben. */
.kri-check:checked::after{content:"";position:absolute;left:7px;top:2px;width:6px;height:12px;
  border:solid var(--kri-surface);border-width:0 2.5px 2.5px 0;transform:rotate(45deg)} /*ok*/
.kri-check:focus-visible{outline:none;box-shadow:0 0 0 3px var(--kri-accent-soft)}

/* ---- Checkbox-Trefferfläche: Kästchen bleibt visuell 26px, das umschliessende <label>
   vergrössert die tatsächliche Tippfläche auf 44px, ohne die Optik zu verändern (Einkaufsliste). ---- */
.kri-check-wrap{display:flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;flex-shrink:0}
.kri-check-wrap label{display:flex;align-items:center;justify-content:center;cursor:pointer}

/* ---- Beschriftetes Kontrollkästchen (CheckboxField-Komponente) — ersetzt das bislang an jeder
   Einsatzstelle wiederholte style="display:flex;align-items:center;gap:10px;cursor:pointer". ---- */
.kri-check-label{display:flex;align-items:center;gap:10px;cursor:pointer}

/* ---- Echtes Disclosure ("Mehr Optionen") — button mit aria-expanded/aria-controls, kein reines
   CSS-Aufklappen (Einkaufsliste: Menge/Aktion/Foto). ---- */
.kri-more-options{margin:10px 0}
.kri-more-toggle{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 0;
  color:var(--kri-accent-text);font:inherit;font-weight:600;cursor:pointer}
.kri-more-chevron{flex-shrink:0;transition:transform .15s ease}
.kri-more-body{padding:4px 0 0}

/* ---- Kompakte, zusammenhängende Liste (Einkaufsliste) — Trennlinien statt Karten+Abstand,
   das entfernt den Zwischenraum zwischen Einträgen vollständig. ---- */
.kri-shop-list{padding:0}
.kri-shop-row{display:flex;align-items:center;gap:14px;padding:14px 26px;border-bottom:1px solid var(--kri-line)}
.kri-shop-row:last-child{border-bottom:none}
.kri-shop-main{flex:1;min-width:0;cursor:pointer}
.kri-shop-meta{margin:2px 0 0;font-size:12.5px}
.kri-shop-thumb{object-fit:cover;flex-shrink:0;border:1px solid var(--kri-line)}
.kri-shop-sale-badge{display:inline-flex;margin-left:8px;padding:2px 8px;font-size:11.5px;font-weight:600;
  min-height:0;vertical-align:middle}
.kri-shop-edit{flex:1;min-width:0}

/* ---- Icon-Knöpfe (Bearbeiten/Löschen): 44px Trefferfläche bei kompakter Optik. ---- */
.kri-icon-btn{display:flex;align-items:center;justify-content:center;width:44px;height:44px;flex-shrink:0;
  background:none;border:none;color:var(--kri-ink2);cursor:pointer;padding:0}
.kri-icon-btn:hover{color:var(--kri-accent-text)}
.kri-icon-btn--danger:hover{color:var(--kri-danger-text)}
.kri-icon-btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--kri-accent-soft)}

/* ---- Schnellerfassung: nur auf Mobile sichtbar (siehe Media-Query unten), auf Desktop bleibt
   das bestehende Formular in .kri-dash-aside die einzige Erfassung. ---- */
.kri-shop-quickadd-wrap{display:none}

/* ---- Formularvalidierung ---- */
.kri-invalid{border-color:var(--kri-danger)!important;color:var(--kri-danger-text)}
.kri-input.kri-invalid{box-shadow:inset 0 0 0 1.6px var(--kri-danger)}
.kri-validation{color:var(--kri-danger-text);font-size:12.5px;margin:4px 0 0}

@media (prefers-reduced-motion:reduce){*{transition:none!important}}

/* ---- Mobile: Schräglagen dämpfen ---- */
@media (max-width:480px){
  /* Datepicker-Popup an die Feldbreite binden: min-width:300px ist breiter als der Inhaltsbereich
     einer Karte auf einem 360px-Display (~260px). Das Popup lief rechts heraus und wurde vom
     clip-path der umgebenden .kri-card abgeschnitten (letzte Wochenspalte fehlte). */
  .kri-dp-pop{min-width:0;left:0;right:0;padding:12px}
}

/* ---- Touch-Ziele sichern (docs/design/index.md: ≥44px) ----
   Breakpoint bewusst 900px statt 480px: das mobile Chrome (App-Bar/Bottom-Bar/Drawer, siehe unten)
   ist bis 900px aktiv — zwischen 481 und 900px (Phone quer, kleine Tablets) griff diese Regel
   bislang gar nicht. .kri-check ist bewusst ausgenommen (eigene, bereits 26px grosse Fläche). */
@media (max-width:900px){
  .kri-btn{min-height:44px}
  .kri-dp-nav{min-width:44px;min-height:44px}
  .kri-chip{min-height:44px;box-sizing:border-box;display:inline-flex;align-items:center}
  .kri-seg-btn{min-height:44px}
  .kri-menu-trigger{min-width:44px;min-height:44px}
  .kri-detail-modal-close{min-width:44px;min-height:44px;display:flex;align-items:center;justify-content:center}
}

/* === Dashboard-Layout (Desktop: Maske+Diagramm neben Liste; Mobile: gestapelt) === */
.kri-dashboard{max-width:1180px;margin:0 auto;padding:24px}
.kri-dash-head{display:flex;align-items:center;gap:10px;margin:0 0 18px}
.kri-dash-grid{display:grid;gap:20px;grid-template-columns:1fr}
.kri-dash-aside{display:flex;flex-direction:column;gap:20px;min-width:0}
.kri-dash-main{min-width:0}
@media (min-width:960px){
  .kri-dash-grid{grid-template-columns:minmax(300px,360px) 1fr;align-items:start}
}
/* Einspaltig (Mobile/schmales Tablet): IMMER der Hauptinhalt zuerst, die Nebenspalte
   (Hinzufügen-Formular, Filter, Diagramm) danach. Verhindert, dass die Desktop-Linke
   auf Mobile den eigentlichen Inhalt nach unten drückt. Seiten mit main-first im DOM
   bleiben unverändert (order 0 vs 1 ist dort deckungsgleich). */
@media (max-width:959.98px){
  .kri-dash-main{order:0}
  .kri-dash-aside{order:1}
}

/* === Einstellungsmenü im Inhalt (docs/specs/settings-ia.md, Abschnitt „Wo das Einstellungsmenü
   lebt"): Das Menü der Sektionen lebt in Account.razor selbst, nicht mehr in NavMenu — die
   Hauptnavigation bleibt beim Betreten von /settings/* unverändert stehen. Mobile-first: Standard
   ist die horizontal scrollbare Leiste (Telefon), ab 900px (derselbe Bruchpunkt wie die App-Chrome
   .kri-sidebar/.kri-mtopbar) wird daraus die linke Spalte neben der Sektion. */
.kri-settings-layout{display:flex;flex-direction:column;gap:16px}
.kri-settings-nav{display:flex;flex-direction:row;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;
  padding:2px 2px 10px;margin:0 -2px}
.kri-settings-nav-item{display:flex;align-items:center;gap:8px;flex:none;box-sizing:border-box;
  padding:10px 14px;min-height:44px;white-space:nowrap;text-decoration:none;font-weight:600;font-size:13.5px;
  background:var(--kri-tint);color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-settings-nav-item:hover{filter:brightness(.97)}
.kri-settings-nav-item.is-active{background:var(--kri-accent);color:#241206}
.kri-settings-nav-item svg{flex:none;width:18px;height:18px}
.kri-settings-content{min-width:0}
@media (min-width:900px){
  .kri-settings-layout{flex-direction:row;align-items:flex-start;gap:24px}
  .kri-settings-nav{flex:none;width:220px;flex-direction:column;overflow:visible;padding:0;margin:0}
  .kri-settings-nav-item{white-space:normal}
  .kri-settings-content{flex:1}
}

/* === Bespoke Balken-Diagramm (kri-Design, hell/dunkel-fähig) === */
.kri-chart-rows{display:flex;flex-direction:column;gap:10px}
.kri-chart-row{display:grid;grid-template-columns:96px 1fr auto;align-items:center;gap:10px}
.kri-chart-label{font-size:13px;color:var(--kri-ink2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kri-chart-track{height:16px;background:var(--kri-tint);clip-path:polygon(0 12%,100% 0,100% 88%,0 100%)}
.kri-chart-bar{height:100%;min-width:3px;clip-path:polygon(0 12%,100% 0,100% 88%,0 100%);transition:width .35s ease}
.kri-chart-value{font-size:13px;font-weight:700;color:var(--kri-ink);white-space:nowrap}
.kri-chart-empty{color:var(--kri-ink2);font-size:13.5px;margin:0}

/* === Footer (global) === */
/* Obere Kante bewusst schräg (Ausnahme) – analog zur Topbar. */
.kri-footer{position:relative;background:var(--kri-surface);color:var(--kri-ink2);font-size:13px;padding:28px 24px 16px;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;clip-path:polygon(0 12px,100% 0,100% 100%,0 100%)}
.kri-footer a{color:var(--kri-accent-text);text-decoration:none;font-weight:600}
.kri-footer a:hover{text-decoration:underline}
.kri-footer-brand{display:inline-flex;align-items:center;gap:6px;font-family:var(--kri-display);font-weight:800;color:var(--kri-ink)}
.kri-footer-brand svg{flex:none}
.kri-footer-brand .kri-dot{color:var(--kri-yellow)}
.kri-footer-links{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.kri-footer-muted{color:var(--kri-ink2)}

/* Fusszeilen-Inhalt (Markenzeichen/Tagline/Copyright) im mobilen App-Menue, ganz unten (siehe
   NavMenu.razor) -- ruhig gesetzt statt der breiten Desktop-Fusszeilenzeile, die dort keinen Platz hat
   und mobil ohnehin ausgeblendet ist. Wiederverwendet .kri-footer-brand/-muted, nur gestapelt. */
/* Polsterung links/rechts wie bei .kri-menu-item (9px 12px) -- ohne sie klebte die Fusszeile
   als einziger Block im Panel am Rand. */
.kri-appmenu-footer{display:flex;flex-direction:column;gap:4px;margin-top:16px;
  padding:10px 12px 12px;border-top:1px solid var(--kri-line);font-size:12.5px}

/* === Landingpage (ausgeloggt) === */
.kri-landing{max-width:1100px;margin:0 auto;padding:24px}
.kri-hero{text-align:center;padding:56px 16px 40px}
.kri-hero-title{font-family:var(--kri-display);font-weight:800;font-size:clamp(34px,6vw,60px);line-height:1.05;color:var(--kri-ink);margin:0 0 14px}
.kri-hero-accent{color:var(--kri-accent)}
.kri-hero-sub{font-size:clamp(16px,2.2vw,20px);color:var(--kri-ink2);max-width:640px;margin:0 auto 26px}
.kri-hero-cta{display:inline-flex;gap:12px;flex-wrap:wrap;justify-content:center}
.kri-features{display:grid;gap:18px;grid-template-columns:1fr;margin:24px 0 8px}
@media (min-width:760px){.kri-features{grid-template-columns:repeat(3,1fr)}}
.kri-feature{padding:22px;background:var(--kri-surface);clip-path:polygon(0 var(--kri-skew-l),100% 0,100% calc(100% - var(--kri-skew-l)),0 100%)}
.kri-feature-ico{width:40px;height:40px;display:flex;align-items:center;justify-content:center;background:var(--kri-accent-soft);color:var(--kri-accent-text);margin-bottom:12px;clip-path:polygon(0 0,100% var(--kri-skew-s),100% 100%,0 calc(100% - var(--kri-skew-s)))}
.kri-feature-title{font-family:var(--kri-display);font-weight:700;font-size:18px;color:var(--kri-ink);margin:0 0 6px}
.kri-feature-text{color:var(--kri-ink2);font-size:14px;margin:0}

/* === Übersichts-Dashboard (eingeloggt) === */
.kri-kpis{display:flex;gap:18px;flex-wrap:wrap}
.kri-kpi{display:flex;flex-direction:column}
.kri-kpi-num{font-family:var(--kri-display);font-weight:800;font-size:22px;color:var(--kri-ink);line-height:1}
.kri-kpi-num--credit{color:var(--kri-teal)}
.kri-kpi-num--debt{color:var(--kri-danger-text)}
.kri-kpi-label{font-size:12px;color:var(--kri-ink2);margin-top:3px}

/* === Startseiten-Widgets (Dashboard) === */
/* min(280px,100%) statt 280px: sonst ist die Spur auf sehr schmalen Geräten (320px Viewport →
   ~272px Inhaltsbreite) breiter als der Container und die Seite scrollt waagrecht. */
.kri-dash-widgets{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(280px,100%),1fr));gap:16px}
.kri-widget{padding:18px 20px;display:flex;flex-direction:column}
.kri-widget--lg{grid-column:span 2}
@media (max-width:640px){.kri-widget--lg{grid-column:span 1}}
.kri-widget-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:12px}
.kri-widget-title{font-family:var(--kri-display);font-weight:800;font-size:16px;color:var(--kri-ink)}
.kri-widget-open{font-size:12.5px;font-weight:600;color:var(--kri-accent-text);text-decoration:none;white-space:nowrap}
.kri-widget-kpi{margin:2px 0 4px}
.kri-widget-kpi .kri-kpi-num{font-size:34px}
.kri-widget-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.kri-widget-list--cols2{display:grid;grid-template-columns:1fr 1fr;gap:0 24px}
@media (max-width:640px){.kri-widget-list--cols2{grid-template-columns:1fr}}
.kri-widget-item{border-bottom:1px solid var(--kri-line)}
.kri-widget-item:last-child{border-bottom:none}
.kri-widget-list--cols2 .kri-widget-item{border-bottom:1px solid var(--kri-line)}
.kri-widget-item-link{display:flex;align-items:center;gap:10px;text-decoration:none;color:inherit;padding:8px 0;min-height:40px}
.kri-widget-item-link:hover .kri-widget-item-title,.kri-widget-item-link:hover .kri-widget-item-sub{color:var(--kri-accent-text)}
.kri-widget-item-link input[type="checkbox"]{flex:none;margin:0}
.kri-widget-item-main{display:flex;flex-direction:column;min-width:0;flex:1 1 auto;gap:1px}
.kri-widget-item-title{font-weight:600;color:var(--kri-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-widget-item-sub{font-size:12px;color:var(--kri-ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-widget-item-meta{font-family:var(--kri-mono);font-size:12px;color:var(--kri-ink2);white-space:nowrap;margin-left:auto;flex:none}
.kri-widget-kpi-wrap{display:flex;flex-direction:column;gap:12px}
.kri-widget-kpi-rows{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:0}
.kri-widget-kpi-row-link{display:flex;align-items:center;justify-content:space-between;gap:10px;text-decoration:none;color:inherit;font-size:13px;padding:6px 0;border-bottom:1px solid var(--kri-line)}
.kri-widget-kpi-rows li:last-child .kri-widget-kpi-row-link{border-bottom:none}
.kri-widget-kpi-row-link:hover .kri-widget-kpi-row-name{color:var(--kri-accent-text)}
.kri-widget-kpi-row-name{font-weight:600;color:var(--kri-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-widget-kpi-row-val{font-family:var(--kri-mono);font-size:12.5px;color:var(--kri-ink2);white-space:nowrap}
.kri-widget-kpi-row-val--credit{color:var(--kri-teal)}
.kri-widget-kpi-row-val--debt{color:var(--kri-danger-text)}
.kri-empty-hero{text-align:center;padding:56px 20px;max-width:520px;margin:0 auto}
.kri-empty-hero .kri-title{margin-bottom:8px}
.kri-empty-hero .kri-btn{margin-top:18px}

/* Segment-Umschalter (Widget-Größe klein/groß) */
.kri-seg{display:inline-flex;border:1px solid var(--kri-line)}
.kri-seg-btn{background:transparent;color:var(--kri-ink2);border:none;padding:5px 12px;font-size:12.5px;font-weight:600;cursor:pointer}
.kri-seg-btn.is-active{background:var(--kri-accent);color:#241206}

/* ============================================================
   NEUE APP-SHELL (Sidebar links · Topbar+Workspace rechts)
   ============================================================ */
/* Aus dem früheren Shell-Layout übernommen (siehe docs/decisions.md): der Theme-Umschalter erscheint
   in .kri-top-actions (MainLayout.razor) sowie im ausgeloggten Zustand, die Fehlerleiste ist global
   verankert (MainLayout.razor, id="blazor-error-ui"). */
.kri-theme-toggle{display:inline-flex;align-items:center;gap:6px;color:var(--kri-paper);font-size:14px;padding:8px 12px}
/* Textfarbe fest (nicht var(--kri-paper)): --kri-warn (#D99400) ändert sich zwischen den Modi nicht,
   --kri-paper aber schon — im Hellmodus wäre das Creme-auf-Amber nur ~2.2:1 (AA-Fail). Der feste
   dunkle Ton trägt in beiden Modi, analog zu .kri-toast--warn (dort korrekt #3a2600). */
#blazor-error-ui{background:var(--kri-warn);color:#3a2600;bottom:0;box-shadow:0 -1px 2px rgba(0,0,0,0.2);box-sizing:border-box;display:none;left:0;padding:0.6rem 1.25rem 0.7rem 1.25rem;position:fixed;width:100%;z-index:1000}
#blazor-error-ui .dismiss{cursor:pointer;position:absolute;right:0.75rem;top:0.5rem;color:#3a2600}
/* Desktop: Shell füllt genau den Viewport, nur der Workspace scrollt → Sidebar bleibt fix. */
.kri-shell{display:flex;height:100dvh;overflow:hidden;background:var(--kri-paper)}

/* Sidebar (dunkel, kontextuell) */
.kri-sidebar{width:288px;flex:none;height:100dvh;overflow-y:auto;background:var(--kri-primary);color:var(--kri-paper);display:flex;flex-direction:column;padding:16px 12px;gap:14px;clip-path:polygon(0 0,100% 0,calc(100% - 20px) 100%,0 100%)}
.kri-side-brand{display:flex;align-items:center;gap:8px;font-family:var(--kri-display);font-weight:800;font-size:20px;color:var(--kri-paper);text-decoration:none;padding:6px 8px}
.kri-side-brand svg{flex:none}
.kri-side-brand .kri-dot{color:var(--kri-yellow)}
.kri-side-cap{font-size:11px;letter-spacing:.5px;text-transform:uppercase;color:var(--kri-on-primary-soft)}

/* Haushalt-Umschalter */
.kri-switcher{position:relative}
.kri-switcher-summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;border:none;font-family:inherit;background:var(--kri-on-primary-veil);padding:10px 12px;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-switcher-name{font-size:15px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--kri-paper)}
.kri-switcher-menu{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:20;background:var(--kri-surface);border:1px solid var(--kri-line);clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)));padding:6px;max-height:320px;overflow:auto}
.kri-switcher-item{display:block;padding:9px 10px;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%);color:var(--kri-ink);text-decoration:none;font-size:14px}
.kri-switcher-item:hover{background:var(--kri-tint)}
.kri-switcher-item--active{background:var(--kri-accent-soft);color:var(--kri-accent-text);font-weight:600}
.kri-switcher-sep{height:1px;background:var(--kri-line);margin:6px 4px}

/* Sidebar-Navigation */
.kri-side-nav{display:flex;flex-direction:column;gap:2px}
.kri-side-link{display:flex;align-items:center;gap:12px;padding:10px 12px;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%);color:var(--kri-paper);text-decoration:none;font-family:var(--kri-sans);font-weight:600;font-size:14px}
.kri-side-nav .kri-side-link:nth-child(2){clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)))}
.kri-side-nav .kri-side-link:nth-child(3){clip-path:polygon(0 var(--kri-skew-m),100% 0,100% calc(100% - var(--kri-skew-m)),0 100%)}
.kri-side-nav .kri-side-link:nth-child(4){clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)))}
.kri-side-nav .kri-side-link:nth-child(5){clip-path:polygon(0 var(--kri-skew-l),100% 0,100% calc(100% - var(--kri-skew-l)),0 100%)}
.kri-side-link:hover{background:var(--kri-on-primary-veil)}
.kri-side-link.active{background:var(--kri-accent);color:#241206}
.kri-side-link svg{flex:none}

/* Nutzer unten */
.kri-side-foot{margin-top:auto;border-top:1px solid var(--kri-on-primary-line);padding-top:12px}
.kri-side-user{display:flex;align-items:center;gap:10px;padding-right:22px}
.kri-side-user .kri-avatar{width:32px;height:32px;font-size:13px}
.kri-side-uname{flex:1;min-width:0;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--kri-paper)}
.kri-side-logout{background:none;border:none;color:var(--kri-on-primary-soft);cursor:pointer;padding:6px;display:flex;align-items:center}
.kri-side-logout:hover{color:var(--kri-paper)}

/* Rechte Spalte: Topbar (hell) + Workspace + Footer */
.kri-main-col{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}
/* Untere Kante bewusst schräg (Ausnahme, unabhängig vom --kri-skew-Standard) – die Diagonale
   entsteht aus dem Farbunterschied Surface/Paper, ohne separate Linie. */
.kri-top{position:relative;height:60px;flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px;background:transparent}
.kri-top::before{content:"";position:absolute;inset:0;background:var(--kri-surface);clip-path:polygon(0 0,100% 0,100% 100%,0 calc(100% - 12px));pointer-events:none}
.kri-crumb{font-size:13px;color:var(--kri-ink2);min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kri-crumb-cur{color:var(--kri-ink);font-weight:600}
.kri-crumb-sep{color:var(--kri-line);margin:0 4px}
.kri-top-actions{display:flex;align-items:center;gap:8px}
/* LanguageSwitcher.razor rendert ein <select class="kri-input kri-lang-select"> — die Feldoptik kommt
   bereits von .kri-input, diese Regel verschmälert nur die Breite in der schmalen Filterleiste/Karte
   (an .kri-filter orientiert statt einer vollbreiten .kri-input-Spalte). */
.kri-lang-select{width:auto;min-width:150px;padding:8px 12px}
.kri-top .kri-theme-toggle{color:var(--kri-ink2)}
.kri-workspace{flex:1;min-width:0;min-height:0;overflow-y:auto}

/* Icon-Größen zentral (die SVGs tragen selbst keine width/height mehr). */
.kri-side-link svg,.kri-side-logout svg{width:18px;height:18px;flex:none}

/* Benachrichtigungsportal (Glocke) */
.kri-bell{position:relative}
.kri-bell-btn{position:relative;padding:8px;display:flex;align-items:center;justify-content:center}
.kri-bell-btn svg{width:22px;height:22px} /* war 20px (Ausreisser) — jetzt IconSize.Xl wie die Nachbar-Icons in der Kopfzeile */
/* Textfarbe fest dunkel statt #fff: weiss auf --kri-danger (#E0492F) liegt nur bei ~4.07:1 (AA-Fail
   bei so kleiner Schrift), verschärft durch die 10px-Schrift. #1a0603 ist der bereits etablierte
   dunkle Ton der Danger-Toasts (.kri-toast--danger, ~4.8:1) — Design verbietet weissen Text auf
   Signalflächen (docs/design/index.md). Schrift zusätzlich auf 11px angehoben. */
.kri-bell-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 3px;background:var(--kri-danger,#d33);color:#1a0603;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1}
.kri-highlight{outline:2px solid var(--kri-accent);outline-offset:2px;transition:outline-color 2s ease}
.kri-bell-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;width:320px;max-width:calc(100vw - 24px);background:var(--kri-surface);border:1px solid var(--kri-line);clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)));max-height:400px;overflow-y:auto}
.kri-bell-item{display:block;padding:10px 14px;border-bottom:1px solid var(--kri-line);color:var(--kri-ink);text-decoration:none}
.kri-bell-item:last-child{border-bottom:none}
.kri-bell-item:hover{background:var(--kri-tint)}
.kri-bell-item-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-weight:600;font-size:14px}

/* Mobile-Chrome ist auf dem Desktop ausgeblendet; die Media-Query unten aktiviert es. */
.kri-mtopbar,.kri-btabs,.kri-drawer,.kri-drawer-scrim{display:none}

/* ============================================================
   MOBILE (<900px): Sidebar → schlanke App-Bar oben +
   Bottom-Tab-Bar (native App-Muster) + Drawer für den Rest.
   Desktop bleibt unverändert.
   ============================================================ */
@media (max-width:900px){
  /* Gesamthoehe von App-Bar und Tab-Leiste, EINMAL definiert. Alles, was um die beiden Leisten herum
     Platz freihalten oder sich an sie anlehnen muss (.kri-main-col, .kri-thread-page, frueher auch
     .kri-composer/.kri-thread-head), rechnet ab jetzt mit diesen zwei Variablen statt mit eigenen
     56px/64px-Literalen, die sonst leicht auseinanderlaufen. Die Werte selbst bleiben unveraendert
     (56px/64px Inhalt + jeweilige Safe-Area, siehe .kri-mtopbar/.kri-btabs weiter unten). */
  :root{
    --kri-mtopbar-h:calc(56px + env(safe-area-inset-top));
    --kri-btabs-h:calc(64px + env(safe-area-inset-bottom));
  }
  /* Shell wird zum normalen Dokument-Fluss; nur der Body scrollt. */
  .kri-shell{display:block;height:auto;overflow:visible}
  .kri-sidebar{display:none}
  .kri-top{display:none}                 /* Desktop-Breadcrumb-Leiste aus */
  /* Der Seiten-Footer stand mobil unter JEDER Seite, direkt über der fixen Bottom-Bar (~60px nur für
     Marke/Slogan) — sein Inhalt zieht ins App-Menü (siehe .kri-appmenu-footer), hier nur ausgeblendet. */
  .kri-footer{display:none}
  /* Platz für die fixe Bottom-Bar (inkl. Home-Indicator) gehört an die GANZE Spalte, nicht an den
     Workspace: der Footer steht im Fluss NACH dem Workspace und lag sonst hinter der Tab-Bar. */
  .kri-main-col{min-height:100dvh;padding-bottom:var(--kri-btabs-h)}
  .kri-workspace{overflow:visible}

  /* App-Bar oben (sticky, dunkel): Community-Wechsler links · App-Menü rechts (docs/specs/
     navigation-ia.md, Abschnitt „Mobile Navigation: zwei Menüs, zwei Aufgaben"). Kein Hamburger mehr —
     der Platz gehört jetzt dem Wechsler.
     Höhe MUSS den oberen Safe-Area-Inset dazurechnen: box-sizing ist global border-box, die Höhe wäre
     sonst die Gesamthöhe INKLUSIVE Polsterung. Unter der Statusleiste/Notch blieben dann nur wenige
     Pixel für den Inhalt — der Text sitzt korrekt, aber der Hintergrund endet mitten darin. */
  .kri-mtopbar{display:flex;position:sticky;top:0;z-index:30;align-items:center;gap:8px;
    height:var(--kri-mtopbar-h);
    padding:0 8px;padding-top:env(safe-area-inset-top);background:var(--kri-primary);color:var(--kri-paper)}
  .kri-mtb-btn{display:flex;align-items:center;justify-content:center;width:44px;height:44px;flex:none;
    background:none;border:none;color:var(--kri-paper);cursor:pointer;padding:0}
  .kri-mtb-btn svg{width:22px;height:22px}
  .kri-mtb-title{flex:1;min-width:0;font-family:var(--kri-display);font-weight:800;font-size:17px;
    color:var(--kri-paper);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:center}

  /* Markenzeichen ganz links (Link auf "/") — fester 44x44-Ziel wie die übrigen Kopfzeilen-Knöpfe,
     das Zeichen selbst bleibt kleiner (Size=22) und zentriert. */
  .kri-mtb-brandlink{display:flex;align-items:center;justify-content:center;width:44px;height:44px;
    flex:none;color:var(--kri-paper)}

  /* Community-Wechsler (Kopfzeile, neben dem Markenzeichen): nimmt den restlichen Platz links vom
     App-Menü ein — Name + Pfeil, öffnet das Community-Blatt (kein Link auf die Übersicht, siehe
     navigation-ia.md). */
  .kri-mtb-switcher{flex:1;min-width:0;display:flex;align-items:center;gap:6px;min-height:44px;
    background:none;border:none;padding:8px 4px;text-align:left;cursor:pointer;
    font-family:var(--kri-display);font-weight:800;font-size:17px;color:var(--kri-paper)}
  .kri-mtb-switcher-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .kri-mtb-switcher svg{flex:none;width:16px;height:16px}
  /* Ohne Community: schlichter, nicht antippbarer Markenname an derselben Stelle. */
  .kri-mtb-brand{flex:1;min-width:0;font-family:var(--kri-display);font-weight:800;font-size:17px;
    padding:8px 4px}
  /* App-Menü (Kopfzeile rechts): senkrechte Drei-Punkte als Ausloeser (bewusst anders als die
     waagrechten Punkte von "Mehr" unten, siehe navigation-ia.md) + Dropdown-Panel darunter. Der
     Wrapper (nicht der Knopf) ist der Positionierungs-Anker fuer das Panel, der Knopf selbst der
     Anker fuer den Zaehler-Blubbel der gefalteten NotificationBell. */
  .kri-appmenu{position:relative;flex:none}
  .kri-mtb-appmenu{position:relative}
  /* Dropdown-Panel: deutlich grosszuegiger als das generische .kri-menu (170-240px/260px) --
     der Inhalt (Person, vier Einstellungs-Links, gefaltete Glocke, Fusszeile) braucht mehr davon,
     siehe Kommentar in NavMenu.razor. Analog .kri-bell-menu (gleicher Ansatz, gleiches Vorbild). */
  /* color GEHOERT hierher: Das Panel liegt im <header class="kri-mtopbar">, der color:--kri-paper
     setzt. Ohne eigene Angabe erbt jeder Text ohne eigene Farbe den Papierton auf hellem Grund
     und ist praktisch unlesbar -- im Emulator war der Personenname genau das. .kri-menu-item
     faerbt sich selbst und war deshalb unauffaellig. */
  .kri-appmenu-panel{position:absolute;right:0;top:calc(100% + 6px);z-index:41;width:300px;
    color:var(--kri-ink);
    max-width:calc(100vw - 24px);max-height:75vh;overflow-y:auto;background:var(--kri-surface);
    border:1px solid var(--kri-line);filter:var(--kri-shadow);display:flex;flex-direction:column;
    clip-path:polygon(0 0,100% var(--kri-skew-m),100% 100%,0 calc(100% - var(--kri-skew-m)))}

  /* Zeilen im App-Menue: Symbol links, FESTE Groesse. Ico() gibt bewusst <svg> ohne width/height aus
     (die Groesse steuert das CSS, siehe Kommentar an Ico() in NavMenu.razor) -- ohne diese Regel dehnt
     der Flex-Container sie auf Panelbreite, und genau das ist beim ersten Lauf im Emulator passiert.
     Jede andere Stelle, die Ico() nutzt, hat ihr Gegenstueck: .kri-side-link svg, .kri-btab svg. */
  .kri-appmenu-panel .kri-menu-item{display:flex;align-items:center;gap:10px;text-decoration:none}
  .kri-appmenu-panel .kri-menu-item svg{width:18px;height:18px;flex:none}

  /* Bottom-Tab-Bar: 4 Kern-Ziele + „Mehr", daumen-erreichbar, fix. */
  .kri-btabs{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:30;
    background:var(--kri-surface);border-top:1px solid var(--kri-line);
    padding-bottom:env(safe-area-inset-bottom)}
  .kri-btab{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:3px;min-height:60px;padding:8px 2px;background:none;border:none;cursor:pointer;
    color:var(--kri-ink2);text-decoration:none;font:inherit}
  .kri-btab svg{width:22px;height:22px}
  .kri-btab-label{max-width:100%;font-size:11px;font-weight:600;line-height:1;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .kri-btab.active{color:var(--kri-accent-text)}
  /* Blubbel an einem Eintrag der unteren Leiste — dieselbe BadgeFor()-Auswertung wie in NavList()
     (Sidebar/Drawer), siehe navigation-ia.md, Abschnitt „Blubbel in der unteren Leiste". Eigener
     Wrapper um Icon+Blubbel, weil .kri-btab selbst eine Spalte (Icon über Beschriftung) ist und die
     Bezugsgröße für "oben rechts am Icon" sonst die ganze, unterschiedlich breite Spalte wäre. */
  .kri-btab-icon{position:relative;display:inline-flex}
  .kri-btab-badge{position:absolute;top:-4px;right:-8px;min-width:14px;height:14px;padding:0 3px;
    background:var(--kri-danger);color:#1a0603;font-size:10px;font-weight:700;line-height:1;
    display:flex;align-items:center;justify-content:center}

  /* Einkaufsliste (mobil): schmalere Zeile (weniger Polsterung) gibt den Platz für einen kleinen
     Zwischenraum zwischen den Einträgen frei, ohne die Zeilenzahl pro Bildschirm zu verringern.
     Der Zwischenraum war zuvor unsichtbar: .kri-shop-row hatte keinen eigenen Hintergrund und lag
     in einer .kri-card mit background:var(--kri-surface) – die 6px-Lücke zeigte exakt dieselbe
     Farbe wie die Zeile. Fix: Liste auf --kri-tint (deutlicherer Farbabstand zu --kri-surface als
     --kri-paper, in beiden Farbmodi geprüft), Zeilen auf --kri-surface – die Lücke zeigt jetzt das
     dunklere Tint durch. Trennlinie entfernt: Linie UND Lücke wäre eine Abgrenzung zu viel. */
  .kri-card.kri-shop-list{background:var(--kri-tint)}
  .kri-shop-row{padding:6px 14px;gap:10px;margin-bottom:6px;background:var(--kri-surface);border-bottom:none}
  .kri-shop-row:last-child{margin-bottom:0}

  /* Schnellerfassung: angeheftet direkt über der Bottom-Tab-Bar (deren Gesamthöhe 60px+Safe-Area
     beträgt, siehe .kri-btabs/.kri-btab oben), damit man beim Einkaufen unten weitertippen kann,
     ohne an der ganzen Liste vorbeizuscrollen. --kri-shop-list-bottom-gap reserviert dafür Platz
     am Ende der Liste, damit der letzte Eintrag nicht dauerhaft dahinter verschwindet. */
  .kri-cat--shopping{--kri-shop-list-bottom-gap:80px}
  /* Schnellerfassung-Wrapper: fix über der Tab-Leiste, enthält die Eingabezeile UND den
     aufklappbaren Mehr-Optionen-Bereich (Menge/Aktion/Foto) darüber — beides bleibt angeheftet,
     kein Sprung/Navigation (vormals ein <a href="#...">, siehe Bugfix). */
  .kri-shop-quickadd-wrap{display:block;position:fixed;left:0;right:0;
    bottom:calc(60px + env(safe-area-inset-bottom));z-index:29;
    background:var(--kri-surface);border-top:1px solid var(--kri-line)}
  .kri-shop-quickadd{display:flex;gap:6px;align-items:center;padding:10px 12px}
  .kri-shop-quickadd input{flex:1;min-width:0;min-height:44px}

  /* Winkel + Beschriftung statt Drei-Punkte-Symbol: sagt, WAS aufklappt (Formularbereich), nicht nur
     DASS etwas passiert — Drei-Punkte ist die Konvention für ein Überlaufmenü mit Aktionen, hier aber
     klappt ein Formularbereich auf (Bottom-Sheet-Konvention: Winkel oben=zu, unten=offen). Trefferfläche
     bleibt bei min. 44x44px, wächst in der Breite nur so viel wie das Wort "Mehr" braucht. */
  .kri-shop-quickadd-more-btn{display:flex;align-items:center;justify-content:center;gap:3px;
    min-width:44px;height:44px;flex-shrink:0;background:none;border:none;color:var(--kri-ink2);
    cursor:pointer;padding:0 6px;font:inherit;font-size:12px;font-weight:600;white-space:nowrap}
  .kri-shop-quickadd-more-label{flex-shrink:0}
  .kri-shop-quickadd-btn{flex-shrink:0;min-height:44px;padding:0 14px}
  /* Aufgeklappter Bereich: eigenes Scrollen statt Inhalt abzuschneiden, wenn er auf kleinen
     Bildschirmen höher würde als sinnvoll Platz bleibt (Tastatur eingeblendet etc.). */
  .kri-quickadd-more{max-height:38vh;overflow-y:auto;padding:10px 12px 0}
  /* Zusammenfassung der zugeklappten "Mehr"-Angaben — erscheint nur, wenn Laden/Menge/Aktion/Foto
     gesetzt sind, sonst kein leerer Platzhalter, der nur Höhe kostet. */
  .kri-shop-quickadd-summary{padding:6px 12px 0;font-size:12.5px}

  /* Drawer (voller Nav-Umfang) + abdunkelnder Scrim. */
  .kri-drawer-scrim{display:block;position:fixed;inset:0;z-index:40;background:rgba(0,0,0,.5);
    opacity:0;pointer-events:none;transition:opacity .2s ease}
  .kri-drawer-scrim.is-open{opacity:1;pointer-events:auto}
  /* Der Drawer liegt über die volle Höhe (top:0), also auch unter Statusleiste und Home-Indicator —
     beide Insets gehören deshalb in die Polsterung, sonst klebt der Schliessen-Knopf im Notch. */
  /* visibility:hidden zusätzlich zu aria-hidden im geschlossenen Zustand: reines transform verschiebt
     den Drawer nur ausserhalb des Viewports, alle Links darin blieben tastaturfokussierbar (Fokus
     landete im Unsichtbaren). Die transition-delay lässt beim Schliessen erst die Slide-out-Animation
     ablaufen, bevor visibility greift; beim Öffnen wird sofort sichtbar (delay 0). */
  .kri-drawer{display:flex;flex-direction:column;gap:12px;position:fixed;top:0;bottom:0;left:0;z-index:41;
    width:min(86vw,320px);background:var(--kri-primary);color:var(--kri-paper);overflow-y:auto;
    padding:calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
    transform:translateX(-100%);visibility:hidden;transition:transform .22s ease,visibility 0s linear .22s}
  .kri-drawer.is-open{transform:translateX(0);visibility:visible;transition:transform .22s ease,visibility 0s linear 0s}
  .kri-drawer-head{display:flex;align-items:center;justify-content:space-between}
  /* Touch-Ziele ≥44px (docs/design/index.md). Die Padding-Werte der Desktop-Sidebar ergeben nur
     ~40px bzw. ~30px für die Icon-Knöpfe — auf dem Touchscreen zu klein. */
  .kri-drawer .kri-side-link,.kri-drawer .kri-switcher-summary{min-height:44px}
  .kri-drawer .kri-switcher-item{min-height:44px;display:flex;align-items:center}
  .kri-side-logout,.kri-bell-btn{min-width:44px;min-height:44px;justify-content:center}
  .kri-drawer .kri-side-nav{flex-direction:column}
  .kri-drawer-tools{display:flex;align-items:center;gap:8px;padding:4px 4px 0;border-top:1px solid var(--kri-on-primary-line)}
  .kri-quick-edit{border-top:1px solid var(--kri-on-primary-line);padding-top:8px}
  .kri-quick-toggle{width:100%;background:none;border:none;cursor:pointer;font:inherit;text-align:left}
  .kri-quick-panel{display:flex;flex-direction:column;gap:6px;padding:8px 4px 4px}
  .kri-quick-item{display:flex;align-items:center;gap:10px;padding:8px 8px;color:var(--kri-paper);
    background:var(--kri-on-primary-veil);cursor:pointer;font-size:14px}
  .kri-quick-item input{width:20px;height:20px;flex:none}
  .kri-quick-item input:disabled{opacity:.4}
  .kri-quick-panel .kri-btn{margin-top:4px}
}

@media (prefers-reduced-motion:reduce){
  .kri-drawer,.kri-drawer-scrim{transition:none}
}

/* === Feed-Redesign === */

/* Feed-Spalte: die Lesebreite gehört EINMAL um die ganze Liste, nicht je Kartentyp. Vorher trug nur
   die Beitragskarte ein eigenes max-width — dadurch lief die Aktivitäts-Spur über die volle Breite und
   die Datumstrenner noch weiter links, drei verschiedene Kanten in einer Liste. Jetzt richten sich
   Beiträge, Aktivitätszeilen, Tagesüberschriften und "Neu seit"-Trenner an derselben Spalte aus
   (schmale Screens: automatisch volle Breite). */
.kri-feed-col{width:100%;max-width:600px;margin:0 auto}

/* Beitragskarte (FeedPostCard, docs/specs/newsfeed.md „Zwei Spuren"): padding:0 auf der Karte selbst
   (zwei Klassen = höhere Spezifität als .kri-card allein, überschreibt dessen 26px-Basis ohne sie
   anzufassen). Kopf/Text/Fusszeile holen sich ihr Padding stattdessen aus .kri-feed-post-pad; das
   Medium (.kri-feed-post-media) bleibt aussen vor und reicht dadurch bis an den (kantig
   beschnittenen) Kartenrand — "edge-to-edge" innerhalb der Karte. Die Breite kommt aus .kri-feed-col. */
.kri-card.kri-feed-post{padding:0}
.kri-feed-post-pad{padding:16px 20px}
.kri-feed-post-media{display:block;width:100%;height:auto;max-height:60vh;object-fit:cover}
/* Kurzer Text ohne Bild/Titel: getönter Statusblock im Stil eines Facebook-Status statt nacktem
   Fliesstext — Schwelle (~80 Zeichen) sitzt in FeedPostCard.IsShortStatus. */
.kri-feed-post-status{background:var(--kri-tint);padding:26px 20px;margin:0 20px}
.kri-feed-post-status p{font-family:var(--kri-display);font-weight:700;font-size:19px;line-height:1.35;margin:0;color:var(--kri-ink)}
/* Längerer Text ohne Bild: kein Statusblock (bei mehreren Zeilen zu dominant), aber trotzdem grösser/
   luftiger als die alte, stets kompakte Fassung. */
.kri-feed-post-text{font-size:16px;line-height:1.5}

/* Aktivitäts-Spur (ActivityEntryRow): kleines Modul-Symbol vor dem Beschreibungssatz, sowohl in der
   Kopfzeile als auch in jeder gruppierten Fortsetzungszeile (siehe ActivityEntryRow.FeatureIcon). */
.kri-activity-feature-icon{display:flex;flex:none;color:var(--kri-primary)}

/* Einladen-Schritt direkt nach dem Anlegen: der Code gross und monospaced, damit man ihn vorlesen und
   abtippen kann, ohne ihn zu entziffern. Eine Community ohne Mitglieder ist der haeufigste Grund, warum
   Kribo nach dem ersten Anlegen liegen bleibt — deshalb steht der Code hier statt in den Einstellungen. */
.kri-invite-code{font-family:var(--kri-mono);font-size:26px;font-weight:700;letter-spacing:.12em;
  padding:10px 12px;background:var(--kri-tint);color:var(--kri-ink);text-align:center;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}

/* Auswahl der Community-Art beim Anlegen: eine Zeile je Art mit Name und einem Satz Erklärung. Als
   echte Radiogruppe (role/aria-checked im Markup), damit Tastatur und Screenreader sie als Auswahl
   erkennen — optisch aber Karten statt Radiopunkte, weil die Erklärung mitgelesen werden soll. */
.kri-type-option{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  padding:10px 12px;background:var(--kri-tint);border:1px solid var(--kri-line);color:var(--kri-ink);
  font:inherit;cursor:pointer}
.kri-type-option:hover{filter:brightness(.97)}
.kri-type-option:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-type-option--on{border-color:var(--kri-primary);box-shadow:inset 0 0 0 1px var(--kri-primary)}
.kri-type-option-name{font-weight:600;font-size:14px}
.kri-type-option-hint{font-size:12.5px;color:var(--kri-ink2)}

/* Navigations-Vorschau beim Anlegen: zeigt in derselben Reihenfolge wie die spätere Seitenleiste
   (ModuleNavOrder), welche Einträge die Community bekommt. Dunkler Grund wie die echte Sidebar, damit
   der Bezug sofort klar ist — es ist eine Vorschau, keine zweite Modulliste. */
.kri-nav-preview{margin-top:12px;padding:12px;background:var(--kri-sidebar-bg,#123A46)}
.kri-nav-preview .kri-cap{color:#cfe6e2}
.kri-nav-preview-list{display:flex;flex-direction:column;gap:2px}
.kri-nav-preview-item{padding:5px 8px;color:#f2efe6;font-size:13px;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-nav-preview-item:nth-child(odd){background:rgba(255,255,255,.06)}

/* Listenkarte: dieselbe Karte, aber mit dem Innenabstand einer Zeile statt dem einer Seite. Die
   26px-Basis von .kri-card ist für eigenständige Blöcke gedacht; in einer Liste kurzer Einträge
   (Termine, Ankündigungen, Buchungen) macht sie jede Kachel dreimal so hoch wie ihr Inhalt.
   Gleiche Werte wie .kri-activity-row/.kri-feed-post-pad, damit Listen überall gleich dicht sind. */
.kri-card--compact{padding:14px 18px}

/* Blubbel am Navigationseintrag „Neuigkeiten": Zahl der Einträge seit dem letzten Besuch der
   Feed-Seite. Sitzt rechts in der Zeile (margin-left:auto), damit die Beschriftungen aller Einträge
   auf derselben Kante beginnen — ein Blubbel direkt hinter dem Wort würde die Zeile ausbeulen.
   Akzentfarbe mit festem dunklem Text wie die übrigen Kennzeichen: die Sidebar ist in beiden Themes
   dunkel, eine themenabhängige Farbe wäre dort in einem der beiden Fälle unlesbar. */
.kri-nav-badge{margin-left:auto;flex:none;min-width:20px;padding:1px 6px;
  background:var(--kri-accent);color:#241206;font-family:var(--kri-mono);font-size:11px;font-weight:700;
  text-align:center;line-height:18px;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}

/* Symbol-Schaltfläche in der Feed-Fusszeile (Reagieren, Kommentieren, Im Modul öffnen): Symbol statt
   Wort, weil drei ausgeschriebene Aktionen je Karte mehr Platz brauchen als der Inhalt darüber. Die
   Beschriftung lebt in aria-label/title weiter — ein Symbol ohne zugänglichen Namen wäre für
   Screenreader ein leerer Knopf.
   Mindestens 32px hoch/breit: darunter wird das Ziel auf einem Touchscreen unzuverlässig (WCAG 2.5.8
   nennt 24px als Untergrenze, Kribo liegt bewusst darüber). */
.kri-icon-btn{display:inline-flex;align-items:center;gap:6px;min-width:32px;min-height:32px;
  padding:6px 8px;background:none;border:none;color:var(--kri-ink2);font:inherit;font-size:13px;
  cursor:pointer;text-decoration:none}
.kri-icon-btn:hover:not(:disabled){color:var(--kri-ink);background:var(--kri-tint)}
.kri-icon-btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-icon-btn:disabled{opacity:.5;cursor:default}
/* Aktiver Zustand: es gibt bereits ein Gespräch. Gefüllte Sprechblase + kräftigere Farbe, damit sich
   „drei Kommentare" auf einen Blick von „noch keiner" unterscheidet — die Zahl allein ist im
   Listen-Überflug zu leise. */
.kri-icon-btn--on{color:var(--kri-primary);font-weight:600}

/* Eigener Status an einem Inserat („Interesse angemeldet") — gehört zu dessen Kennzeichnung oben,
   nicht in die Aktionszeile, wo er wie eine Beschriftung der Nachbarschaltfläche wirkte.
   Farben wie .kri-badge--owner/.kri-chip--on: Akzent mit FESTEM dunklem Text. Nicht var(--kri-primary)
   als Hintergrund — die Variable ist im hellen Design dunkel (#123A46) und im dunklen hell (#8FD3CC),
   eine einzelne Textfarbe darauf wäre in einem der beiden Modi unlesbar. */
.kri-badge--on{background:var(--kri-accent);color:#241206}

/* Umfrage-Option in der Inhaltskarte (FeedContentCard, docs/specs/newsfeed.md „Dritte Spur"): Balken für
   den Stimmenanteil liegt HINTER Text und Zahl (position:absolute + z-index-Reihenfolge), damit der Text
   immer auf dem Hintergrund der Karte lesbar bleibt statt auf einer Farbfläche zu verschwinden. Als
   <button> anklickbar, wenn abstimmen erlaubt ist, sonst als <div> mit identischem Aussehen — die Karte
   soll sich für Gäste und bei geschlossener Umfrage nicht optisch verändern, nur nicht reagieren. */
.kri-poll-option{position:relative;display:flex;align-items:center;gap:10px;width:100%;
  padding:10px 12px;background:var(--kri-tint);border:1px solid var(--kri-line);
  color:var(--kri-ink);font:inherit;text-align:left;cursor:default;overflow:hidden}
button.kri-poll-option{cursor:pointer}
button.kri-poll-option:hover:not(:disabled){filter:brightness(.97)}
button.kri-poll-option:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-poll-option--mine{border-color:var(--kri-primary)}
.kri-poll-option-bar{position:absolute;left:0;top:0;bottom:0;background:var(--kri-accent);opacity:.35;
  transition:width .2s ease}
.kri-poll-option-text{position:relative;flex:1;min-width:0;font-size:14px;font-weight:600}
.kri-poll-option-count{position:relative;flex:none;font-family:var(--kri-mono);font-size:13px;color:var(--kri-ink2)}

/* Innenabstand der Aktivitätszeile — identisch zu .kri-feed-post-pad, damit Avatar, Text und
   Drei-Punkte-Menü beider Spuren auf derselben senkrechten Linie sitzen. Gilt auch auf /activity
   (community-lokaler Verlauf): dieselbe Art Liste soll nicht zwei Abstände haben. */
.kri-activity-row{padding:16px 20px}

/* Profil-Kopf (ProfileSheet.razor): grösserer Avatar, bleibt in der projektweiten Skew-Form
   (.kri-avatar/clip-path) — bewusst NICHT rund, siehe FeedPostCard-Klassenkommentar zur
   Avatar-Konsistenz. */
.kri-profile-avatar--lg{width:104px;height:104px;font-size:34px}

/* "Bietet an"-Karten (ProfileSheet.razor): gruppiert nach Community, kompakte Kachel mit
   Modul-Icon/Titel/Preis statt der früheren flachen .kri-member-Liste. */
.kri-offer-card{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--kri-tint);
  color:var(--kri-ink);text-decoration:none;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-offer-card:hover{filter:brightness(.97)}
.kri-offer-card:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-offer-card-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600}
.kri-offer-card-price{flex:none;font-family:var(--kri-mono);font-size:12.5px;color:var(--kri-ink2)}

/* Nur für Screenreader/aria-describedby: dauerhaft unsichtbar, unabhängig von :focus (im Unterschied
   zu einem Skip-Link ist das Element selbst nicht interaktiv, sondern nur eine Beschreibung, auf die
   andere Elemente verweisen — siehe MessagesThread.razor, Tastatur-Ersatz fürs Langdrücken). */
.kri-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* Messenger (docs/specs/messenger.md).

   Gesprächszeile: derselbe Aufbau wie .kri-offer-card (kantig beschnittene Kachel, Tint-Hintergrund),
   aber mit Avatar und zweizeiliger Mitte. Kein Vorschautext — der Server kennt ihn nicht. */
.kri-conv-row{display:flex;align-items:center;gap:12px;padding:10px 12px;background:var(--kri-tint);
  color:var(--kri-ink);text-decoration:none;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-conv-row:hover{filter:brightness(.97)}
.kri-conv-row:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-conv-main{flex:1;min-width:0}
.kri-conv-name{font-size:15px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-conv-meta{display:flex;align-items:center;gap:8px;font-family:var(--kri-mono);font-size:12px;
  color:var(--kri-ink2)}
/* Vorschau der letzten Nachricht (docs/specs/messenger.md, "Die Oberfläche auf dem Telefon", Punkt 4) —
   auf dem Gerät entschlüsselt, nicht mehr mono/Uhrzeit-artig, deshalb eine eigene Schrift und Kürzung. */
.kri-conv-preview{font-family:var(--kri-sans);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;max-width:220px}

/* Nachricht. Eigene Nachrichten rechts, fremde links — die Schrägung spiegelt mit, damit die Kante
   jeweils zur Sprechrichtung zeigt. Die Farbe der eigenen Blase ist --kri-accent (NICHT --kri-primary:
   das ist im Hellmodus dunkel und im Dunkelmodus hell, eine einzige Textfarbe darauf wäre in einem der
   beiden Modi unlesbar — siehe Kontrast-Linter). */
.kri-msg{max-width:78%;margin-bottom:8px;padding:10px 13px;background:var(--kri-tint);color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%);
  /* touch-action:pan-y: vertikales Scrollen bleibt nativ, horizontale Zeigerbewegung geht an
     kriboUi.bindMessageFeedGestures ("Wischen antwortet") statt vom Browser als Scroll/Zoom
     aufgefasst zu werden. transition fürs Zurückschnappen nach einem abgebrochenen Wisch-Versuch. */
  touch-action:pan-y;transition:transform .15s ease}
.kri-msg--mine{margin-left:auto;background:var(--kri-accent);color:#241206;
  clip-path:polygon(0 0,100% var(--kri-skew-s),100% 100%,0 calc(100% - var(--kri-skew-s)))}
/* Tastatur-Ersatz fürs Langdrücken (docs/specs/messenger.md, "Die Oberfläche auf dem Telefon",
   Punkt 2): Jede Blase ist fokussierbar (tabindex im Markup) — Enter/Kontextmenü-Taste öffnen dasselbe
   Blatt wie ein Langdruck. */
.kri-msg:focus-visible{outline:none;box-shadow:0 0 0 3px var(--kri-accent-soft)}
.kri-msg-body{font-size:15px;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}
.kri-msg-time{margin-top:4px;font-family:var(--kri-mono);font-size:11.5px;opacity:.72}

/* Bild-Anhang in einer Nachricht (docs/specs/messenger.md). Kantig beschnitten wie alles Übrige, und mit
   fester Maximalhöhe: Ein hochkantiges Handyfoto würde sonst den halben Verlauf einnehmen. */
.kri-msg-image{display:block;max-width:100%;max-height:320px;margin-bottom:6px;object-fit:cover;
  -webkit-touch-callout:none;user-select:none;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}

/* Bild antippbar -> Vollbild-Betrachter (docs/specs/messenger.md, "Medien in der Galerie"). Der Button
   selbst traegt keine eigene Optik, nur .kri-msg-image behaelt ihre. */
.kri-msg-image-wrap{position:relative;display:inline-block;max-width:100%}
.kri-msg-image-btn{display:block;padding:0;border:none;background:none;cursor:pointer;max-width:100%}
.kri-msg-image-btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--kri-accent-soft)}
/* Abzeichen "nur noch lokal gespeichert": Der Server hat diesen Anhang nach 30 Tagen geloescht, was in
   der Blase steht, ist die Kopie aus der Galerie DIESES Geraets (docs/specs/messenger.md). */
.kri-msg-local-badge{position:absolute;right:6px;bottom:12px;display:flex;align-items:center;
  justify-content:center;width:22px;height:22px;background:rgba(20,16,10,.65);color:#fff;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}

/* ---- Vollbild-Betrachter (docs/specs/messenger.md, "Medien in der Galerie") ----
   Bewusst OHNE .kri-dialog/.kri-q2: Ein Foto braucht die volle Flaeche, nicht eine Karte mit geneigten
   Ecken -- derselbe Grund, aus dem Google Photos/WhatsApp/Signal ihre Betrachter randlos und dunkel
   zeichnen, unabhaengig vom sonstigen App-Theme. */
.kri-media-viewer-overlay{padding:0;background:rgba(8,8,8,.97)}
.kri-media-viewer{display:flex;flex-direction:column;width:100%;height:100%;
  padding:0;background:none;border:none;filter:none;color:#fff;overflow:hidden}
.kri-media-viewer-bar{display:flex;align-items:center;gap:10px;flex:0 0 auto;color:#fff;
  padding:14px 16px;padding-top:calc(14px + env(safe-area-inset-top, 0px))}
.kri-media-viewer-title{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;
  font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-media-viewer-time{font-family:var(--kri-mono);font-size:11.5px;font-weight:400;opacity:.75}
.kri-media-viewer-count{flex:none;font-family:var(--kri-mono);font-size:12px;opacity:.8}
/* touch-action:none auf BEIDEN (Buehne UND Bild): Stufenloses Kneifen/Verschieben (ui.js,
   bindMediaViewerGestures) braucht die vollen Zeigerereignisse fuer sich allein -- mit "manipulation"
   (der vorherige Wert) duerfte der Browser dieselbe Zwei-Finger-Geste zusaetzlich als eigenes,
   ELEMENT-UNABHAENGIGES Seiten-Pinch-Zoom auffassen (die viewport-meta-Angabe schliesst das nicht aus,
   siehe App.razor/index.html), das kaempfte sichtbar gegen unseren eigenen Massstab. Doppeltipp/-klick
   (dblclick) bleibt davon unberuehrt: touch-action regelt nur Scroll-/Zoom-Standardgesten, nicht die
   Klick-/Doppelklick-Synthese aus Antippen. */
.kri-media-viewer-stage{position:relative;flex:1;min-height:0;display:flex;align-items:center;
  justify-content:center;overflow:hidden;touch-action:none}
.kri-media-viewer-img{max-width:100%;max-height:100%;object-fit:contain;transition:transform .15s ease;
  touch-action:none;-webkit-touch-callout:none;user-select:none}
/* Der Massstab selbst (translate+scale) sitzt seit dem stufenlosen Zoomen als INLINE style.transform in
   ui.js (siehe dortige Klassendoku zu bindMediaViewerGestures) -- eine Inline-Angabe gewinnt gegenueber
   dieser Klassenregel immer, ein zusaetzliches transform:scale(2.2) hier waere also totes, irrefuehrendes
   CSS. Was bleibt, ist der Cursor: JS setzt/entfernt genau diese Klasse (classList.toggle), rein als
   Signal "gerade gezoomt", nicht mehr als Traeger des Massstabs. */
.kri-media-viewer-img--zoomed{cursor:zoom-out}
.kri-media-viewer-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  display:flex;align-items:center;justify-content:center;width:44px;height:44px;
  border:none;background:rgba(0,0,0,.35);color:#fff;cursor:pointer}
.kri-media-viewer-nav:hover{background:rgba(0,0,0,.55)}
.kri-media-viewer-nav:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(255,255,255,.7)}
.kri-media-viewer-nav--prev{left:8px}
.kri-media-viewer-nav--next{right:8px}
@media (max-width:520px){.kri-media-viewer-nav{width:38px;height:38px}}
.kri-media-viewer-hint{margin:0;padding:0 16px 6px;text-align:center;color:#fff;opacity:.8;
  font-size:12.5px;display:flex;align-items:center;justify-content:center;gap:6px}
.kri-media-viewer-actions{display:flex;justify-content:center;gap:10px;flex-wrap:wrap;flex:0 0 auto;
  padding:10px 16px calc(14px + env(safe-area-inset-bottom, 0px))}

/* Sprachnachrichten-Player (docs/specs/messenger.md, "Sprachnachrichten") — ersetzt die graue
   <audio controls>-Bedienleiste des Browsers (VoicePlayer.razor). Wellenform-Balken sind reine
   DEKORATION (aria-hidden im Markup) — Bedienung läuft über den Wiedergabe-Knopf (44x44, .kri-btn--icon)
   und role="slider" mit Pfeiltasten auf .kri-voice-wave, nicht über die Balken selbst. */
.kri-voice-player{display:flex;align-items:center;gap:8px;width:100%;min-width:230px;margin-bottom:6px}
.kri-voice-wave{flex:1;min-width:0;min-height:44px;display:flex;align-items:center;cursor:pointer;
  touch-action:none}
.kri-voice-wave:focus-visible{outline:none;box-shadow:inset 0 0 0 2px currentColor}
.kri-voice-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:26px;overflow:hidden}
.kri-voice-bar{flex:1 1 0;min-width:2px;height:calc(var(--kri-voice-bar-h,.2) * 100%);
  background:currentColor;opacity:.4}
.kri-voice-bar--played{opacity:1}
.kri-voice-bar--placeholder{opacity:.22}
.kri-voice-time{flex:none;font-family:var(--kri-mono);font-size:11.5px;font-variant-numeric:tabular-nums;
  white-space:nowrap}
/* Tempo-Umschalter (1x/1.5x/2x): eigene kleine Fläche wie .kri-chip, aber mit gesicherter
   Berührungsfläche (docs/design/index.md: ≥44px) statt nur auf Mobile — der Player steht in
   Sprechblasen, die auf jedem Gerät per Finger bedient werden können. */
.kri-voice-speed{flex:none;min-width:44px;min-height:44px;padding:2px 10px;border:none;cursor:pointer;
  background:var(--kri-tint);color:var(--kri-primary);font-family:var(--kri-mono);font-size:12px;
  font-weight:600;clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-voice-speed:hover{filter:brightness(.97)}
.kri-voice-speed:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}

.kri-msg-file{display:flex;align-items:center;gap:8px;width:100%;margin-bottom:6px;padding:8px 10px;
  border:none;cursor:pointer;font-family:var(--kri-sans);font-size:14px;text-align:left;
  background:var(--kri-tint);color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-msg-file:hover{filter:brightness(.97)}
.kri-msg-file:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
.kri-msg-file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Standort-Zeile (docs/specs/messenger-location.md, Bauabschnitt A): wie .kri-msg-file-name, aber ZWEI
   Zeilen statt einer (Titel + Genauigkeit) -- deshalb ein eigener Block statt derselben Klasse. */
.kri-msg-location-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.kri-msg-location-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kri-msg-location-accuracy{font-size:12px}
/* Kartenvorschau (docs/specs/messenger-location.md, Bauabschnitt C): Standort-Zeile UND Karte teilen
   sich diese Huelle -- .kri-msg-location-text/-title/-accuracy oben bleiben unveraendert. */
.kri-msg-location{display:flex;flex-direction:column}
/* Feste Flaeche statt eines Seitenverhaeltnisses: MapLibre braucht eine bekannte Pixelgroesse, um den
   Kreis-Ausschnitt korrekt zu berechnen (fitBounds/cameraForBounds, siehe messenger-map.js). Dieselbe
   Schraege wie .kri-msg-image (Bild-Anhaenge) -- eine Karte ist optisch dieselbe Art Medienvorschau. */
.kri-location-map{position:relative;width:100%;max-width:280px;height:158px;margin-bottom:4px;
  background:var(--kri-tint);overflow:hidden;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
/* Vorschau aus/kein Token/"zu ungenau" (messenger-map.js liefert dann ok:false, siehe
   LocationMapPreview.razor) -- der Container bleibt fuer den IntersectionObserver im DOM, zeigt aber
   nichts: Der Aufrufer stellt in diesem Fall seinen eigenen Rueckfalltext daneben (Moeglichkeit D). */
.kri-location-map--hidden{display:none}
/* Gross und quadratisch ueber die volle Breite -- nur in den BLAETTERN (Sende-Blatt, Live-Dauer-Blatt),
   nicht in der Blase (Nutzerentscheidung 2026-09-30, siehe docs/specs/messenger-location.md). Die
   Hoehe kommt hier aus dem Seitenverhaeltnis statt aus einem festen Pixelwert; das ist mit MapLibre
   vertraeglich, seit der Ausschnitt im Konstruktor gesetzt wird (messenger-map.js) -- die
   Container-Groesse steht zu diesem Zeitpunkt aus dem Layout fest, es wird nichts mehr nachtraeglich
   gefittet. Der Zugewinn ist nicht nur Optik: Mehr Hoehe heisst, dass ein gegebener Genauigkeitskreis
   bei hoeherem Zoom noch hineinpasst, die "zu ungenau"-Schwelle rueckt also nach oben. */
.kri-location-map--square{max-width:none;height:auto;aspect-ratio:1/1}
/* "Zurueck zum Standort" (messenger-map.js/addRecenterControl) -- seit das Verschieben freigegeben ist
   (Nutzerentscheidung 2026-09-30) der Rueckweg aus einem weggeschobenen Ausschnitt. Nur Groesse und
   Tinte angepasst; Rahmen, Hintergrund und Schatten bringt maplibre-gl.css mit, damit der Knopf neben
   der Quellenzeile nicht wie ein Fremdkoerper wirkt. 32px statt MapLibres 29px: Kribo ist eine
   Telefon-App, und das ist ein Ziel fuer einen Daumen. */
.kri-map-recenter{display:flex;align-items:center;justify-content:center;width:32px;height:32px;
  color:var(--kri-ink);background:none;border:none;cursor:pointer;padding:0}
/* Die MapLibre-Nadel (siehe buildPinElement in messenger-map.js) -- dasselbe Feather-"map-pin"-Symbol
   wie Icon.razor, damit Standort-Zeile und Kartenvorschau nicht mit zwei verschiedenen Symbolen fuer
   dieselbe Sache auftreten. Farbe/Schatten hier statt inline in JS, damit Hell/Dunkel automatisch passt. */
.kri-location-map-pin{color:var(--kri-accent-text);filter:drop-shadow(0 1px 2px rgba(20,16,10,.55))}
/* Quellenzeile: seit 2026-09-30 nicht mehr eine eigene Zeile unter der Karte, sondern MapLibres
   Attribution-Control IN der Karte (unten rechts, siehe messenger-map.js/addAttribution). Die Steuerung
   bringt ihr Aussehen aus vendor/maplibre/maplibre-gl.css mit; hier nur zwei Anpassungen, damit sie in
   einer 158px hohen Blase nicht wie ein Fremdkoerper wirkt: kleiner gesetzt und mit Kribos Tinte statt
   MapLibres Schwarz. Der Hintergrund bleibt halbdeckend weiss -- Lesbarkeit ueber jedem Kartenbild ist
   hier eine Lizenzfrage (ODbL), keine Geschmacksfrage. */
.kri-location-map .maplibregl-ctrl-attrib,
.kri-live-map-canvas .maplibregl-ctrl-attrib{font-size:10px;line-height:1.3}
.kri-location-map .maplibregl-ctrl-attrib a,
.kri-live-map-canvas .maplibregl-ctrl-attrib a{color:var(--kri-ink);text-decoration:underline}

/* ---- Live-Standort (docs/specs/messenger-location.md, Bauabschnitt E) ---- */

/* Der Schieberegler fuer die Dauer (LiveLocationDurationFields.razor) -- eigens gestylt statt des
   Browser-Standard-Sliders, dessen Kreis-Griff die einzige runde Form im ganzen Produkt waere. Die Spur
   bleibt ein schlichtes Rechteck (kein Eckenradius gesetzt), der Griff ist eine Raute (clip-path), keine
   runde Kugel. */
.kri-live-slider{-webkit-appearance:none;appearance:none;width:100%;height:6px;margin:14px 0 6px;
  background:var(--kri-line);cursor:pointer}
.kri-live-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:22px;height:22px;
  background:var(--kri-accent);border:2px solid var(--kri-surface);cursor:pointer;
  clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
.kri-live-slider::-moz-range-thumb{width:22px;height:22px;background:var(--kri-accent);
  border:2px solid var(--kri-surface);cursor:pointer;
  clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
.kri-live-slider-value{font-weight:700;font-size:16px}
.kri-live-slider-end{color:var(--kri-ink2);font-size:13px;margin-top:2px}

/* Blase "lebt" (Bauabschnitt E) -- kleines Abzeichen neben dem Standort-Titel, solange eine Freigabe
   laeuft. Dieselbe Form wie .kri-badge, eigene Farbe (Gefahr-Ton waere hier falsch -- es ist kein
   Fehlerzustand, sondern ein aktiver, gewollter Vorgang). */
.kri-msg-location-live{display:inline-flex;align-items:center;gap:4px;font-family:var(--kri-mono);
  font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
  padding:2px 7px;margin-left:6px;background:var(--kri-accent);color:#241206;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-msg-location-stop{margin-top:6px}

/* Banner "3 Personen teilen ihren Standort" oben im Gespraech (Bauabschnitt E) -- dieselbe Klasse wie die
   "Anruf laeuft"-Leiste (.kri-call-bar): eine ganze Zeile als Klick-Flaeche, kein zweiter Baustein. */
.kri-live-banner{margin-bottom:10px}

/* ---- Vollbild-Kartenblatt (Bauabschnitt E) ----
   Bewusst OHNE .kri-dialog/.kri-q2, aus demselben Grund wie der Medien-Betrachter: Eine Karte braucht die
   volle Flaeche, nicht eine Karte mit geneigten Ecken. Anders als der (dunkle) Medien-Betrachter bleibt
   dieses Blatt im normalen Oberflaechenton -- eine Karte ist kein Foto, sie braucht kein abgedunkeltes
   Kino, und Hell-/Dunkelmodus der Karte selbst folgt ohnehin schon theme.css (siehe resolveIsDark in
   messenger-map.js). */
.kri-live-map-overlay{padding:0}
.kri-live-map-sheet{display:flex;flex-direction:column;width:100%;height:100%;padding:0;
  background:var(--kri-surface);border:none;filter:none;color:var(--kri-ink);overflow:hidden}
.kri-live-map-bar{display:flex;align-items:center;gap:10px;flex:0 0 auto;
  padding:14px 16px;padding-top:calc(14px + env(safe-area-inset-top, 0px));
  border-bottom:1px solid var(--kri-line)}
.kri-live-map-title{flex:1;min-width:0;font-size:16px;font-weight:700}
.kri-live-map-stage{position:relative;flex:1;min-height:0}
/* width/height:100% ZUSAETZLICH zu inset:0 -- und das ist kein Guertel-und-Hosentraeger, sondern die
   Behebung eines Fehlers, der das Vollbild-Kartenblatt leer liess (gemessen 2026-10-01, Bildschirmfoto
   des Nutzers 1:1 im Pruefaufbau nachgestellt): MapLibre haengt dem Kartenbehaelter beim Aufbau die
   Klasse .maplibregl-map an, und die traegt `position:relative; overflow:hidden` -- GLEICHE
   Spezifitaet wie .kri-live-map-canvas, aber maplibre-gl.css wird zur LAUFZEIT ans Ende des <head>
   gehaengt (siehe messenger-map.js/ensureMapLibreStylesheet) und gewinnt damit die Kaskade. Aus
   `position:absolute` wurde `relative`, `inset:0` verlor jede Wirkung, die Hoehe fiel von 639px auf 0
   -- und `overflow:hidden` schnitt die 412x300 grosse WebGL-Flaeche darin vollstaendig weg. Sichtbar
   blieb: Kopfzeile, Knopfleiste, dazwischen nichts. Mit expliziten 100% haengt die Hoehe am Elternteil
   (.kri-live-map-stage, flex:1 mit bestimmter Hoehe) statt an der Positionierung, und die MapLibre-Regel
   kann ihr nichts mehr anhaben. */
.kri-live-map-canvas{position:absolute;inset:0;width:100%;height:100%}
.kri-live-map-list{flex:1;min-height:0;overflow:auto;padding:12px 16px}
/* Nebenwege unter der Karte (Karten-App, Koordinaten kopieren) und die Rueckmeldung dazu -- beide
   flex:0 0 auto, damit sie der Karte (flex:1) nie Platz streitig machen und auf einem kleinen
   Bildschirm nicht aus dem Blatt fallen. */
.kri-live-map-actions{flex:0 0 auto;display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;
  padding:12px 16px;padding-bottom:calc(12px + env(safe-area-inset-bottom, 0px));
  border-top:1px solid var(--kri-line)}
.kri-live-map-status{flex:0 0 auto;margin:0;padding:8px 16px;font-size:13px;background:var(--kri-tint)}
/* Personen-Marker auf der Live-Karte (buildLiveMarkerElement in messenger-map.js) -- dieselbe geneigte
   Flaeche wie .kri-avatar, nur kleiner, mit Initialen statt eines Fotos (die Karte kennt keine Profilbilder). */
.kri-live-map-pin{width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  font-family:var(--kri-display);font-weight:800;font-size:11px;color:#fff;
  filter:drop-shadow(0 1px 2px rgba(20,16,10,.55));
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-live-map-pin--own{background:var(--kri-accent);color:#241206}
.kri-live-map-pin--teal{background:var(--kri-teal);color:#0c211d}
.kri-live-map-pin--pink{background:var(--kri-pink);color:#2a0d16}
.kri-live-map-pin--yellow{background:var(--kri-yellow);color:#3a2a00}

/* Kopf des Gespraechs (Zurueck-Pfeil · Name · Aktionen) bleibt beim Scrollen oben stehen — das
   Gegenstueck zur Eingabe unten. Haelt schon dadurch stand, dass er in .kri-thread-page (siehe weiter
   unten) ein fester Flex-Endpunkt VOR dem scrollenden .kri-thread-feed ist -- position:sticky bleibt
   zusaetzlich stehen als Rueckfallebene fuer den Fall, dass .kri-thread-page trotzdem einmal ueber
   .kri-workspace hinauswaechst (z. B. sehr grosse Mitgliederliste, siehe Kommentar dort); sticky griffe
   dann relativ zu .kri-workspace. Der Hintergrund MUSS deckend sein, sonst scrollt der Verlauf sichtbar
   durch den Kopf hindurch. Der negative Aussenabstand oben gleicht das Padding von .kri-dashboard aus,
   damit ueber dem Kopf kein heller Streifen mit durchlaufendem Inhalt bleibt. z-index: unter
   .kri-mtopbar (30), ueber dem Verlauf — die Eingabe unten liegt auf 2.

   Mobil braucht dieser Kopf KEINEN eigenen top-Versatz (mehr): .kri-thread-page ist dort fixiert
   (siehe dessen Kommentar weiter unten) und scrollt als Ganzes nie mit dem Dokument mit — sticky hat
   also nichts, wogegen es "kleben" muesste, und verhaelt sich wie ein normales Flex-Kind ganz oben in
   der Spalte. Bis 2026-09-28 stand hier ein zusaetzlicher `top:calc(56px + safe-area-top)`-Versatz
   fuer den Fall, dass .kri-thread-page (damals ueber 100dvh berechnet) das Dokument doch scrollen
   liess; da .kri-thread-page das jetzt strukturell nicht mehr kann, ist der Versatz ersatzlos
   entfallen statt nur "vorsichtshalber" stehen zu bleiben.

   EINE Zeile statt zwei (seit 2026-10-01, Beanstandung "die 3 Punkte oben brauchen richtig viel
   Platz"): bis dahin stand darueber eine eigene, volle Breite einnehmende "← Zurueck zu den
   Gespraechen"-Textzeile (GROSSBUCHSTABEN, .kri-cap) — auf dem Telefon kostete das zusammen mit der
   Namenszeile knapp ein Sechstel der Bildschirmhoehe, bevor ueberhaupt die erste Nachricht zu sehen
   war. padding-top faellt von 24px auf 10px (symmetrisch zum padding-top von .kri-composer unten) —
   der ausgleichende negative Aussenabstand oben bleibt UNVERAENDERT bei -24px, der gleicht weiterhin
   GENAU das (von der Zeilenzahl hier drin unabhaengige) Padding von .kri-dashboard aus. */
.kri-thread-head{position:sticky;top:0;z-index:3;margin-top:-24px;padding:10px 0;
  background:var(--kri-paper)}
/* Reiner Pfeil-Knopf statt der fruehen vollen Textzeile (siehe Kommentar oben) — ghost statt
   gefuellt/geschraegt wie die Aktions-Knoepfe rechts, weil "zurueck" Navigation ist, keine gleichrangige
   Aktion neben Anruf/Kamera/Menue (.kri-btn--ghost bleibt ohne Flaechen-Beschnitt, siehe dessen eigene
   Ausnahme-Markierung oben). Dieselbe 36px-Flaeche wie .kri-thread-head-actions .kri-btn--icon unten
   haelt die ganze Kopfzeile auf einer gemeinsamen Hoehe statt dass der Pfeil (vorher implizit 44px ueber
   .kri-btn--icon) die Zeile wieder aufspreizt. */
.kri-thread-back{flex:0 0 auto;width:36px;height:36px;min-width:36px;min-height:36px;padding:0}
.kri-thread-head-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
/* Einziges Kind mit flex:1 — nimmt den Platz zwischen Pfeil und Aktionen ein und schiebt beide an ihren
   Rand, auch OHNE .kri-thread-head-actions (Gruppen, siehe Kommentar in MessagesThread.razor: dort hat
   die Zeile nur ZWEI statt drei Kinder). min-width:0 ist fuer das Ellipsis-Abschneiden unten in einer
   Flex-Zeile zwingend — ohne das haelt ein Flex-Kind seine Inhaltsbreite als Minimum und sprengt die
   Zeile (bzw. drueckt die Aktions-Knoepfe hinaus), statt lange Namen abzuschneiden. font-size 18px statt
   der 22px von .kri-title: der Name bleibt die groesste Schrift der Zeile (Hauptinformation), aber die
   Kopfzeile selbst darf nicht mehr Hoehe brauchen als die 36px-Knoepfe daneben. */
.kri-thread-head-title{flex:1;min-width:0;margin:0;font-size:18px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
/* Fasst Hoerer- und Kamera-Knopf zu EINEM Kind von .kri-thread-head-row zusammen (siehe Kommentar in
   MessagesThread.razor) — beide rechtsbuendig nebeneinander statt space-between ueber vier Kinder
   verteilt. flex:0 0 auto haelt die Gruppe bei langen Gespraechsnamen auf fester Breite statt sie zu
   quetschen. gap 8px→4px (Beanstandung 2026-10-02, "beim Dreipunkt-Knopf ist das Margin/Padding
   gewaltig"): am Android-Emulator per Pixelvergleich nachgemessen (1080px-Gerät, Skalierung 2,75x)
   waren .kri-menu-trigger/.kri-btn--icon selbst NICHT die Ursache — beide landen rechnerisch exakt
   gleich (36px-Box, box-sizing:border-box kompensiert das unterschiedliche padding von .kri-btn--icon
   [0] und .kri-menu-trigger [6px] vollstaendig, 9px Resultat beidseits) UND .kri-menu-wrap traegt kein
   eigenes Padding/Margin. Tatsaechlich frei drehbar war einzig dieser gap: 8px bei 2,75x Skalierung
   waren ca. 22px sichtbarer Leerraum zwischen je zwei Knoepfen, wie WhatsApp als Referenz vorgegeben
   ("dicht am Rand, wenig Luft") halbiert auf 4px (≈11px) — Knopfgroesse/Trefferflaeche (36px, siehe
   unten) bleibt dabei unangetastet. */
.kri-thread-head-actions{display:flex;align-items:center;gap:4px;flex:0 0 auto}
/* Hoerer/Kamera (.kri-btn--icon, sonst 44x44) UND das Dreipunkt-Menue (.kri-menu-trigger, sonst per
   @media max-width:900px auf min. 44x44 gehoben) auf dieselbe kompaktere 36px-Flaeche gebracht wie
   .kri-call-icon-btn--tiny (siehe dort) — gleiche Begruendung: --kri-skew-s (2px) statt --kri-skew-m/-l
   laesst bei 36px Kantenlaenge einen ungeschraegten Kern von 36 − 2×2 = 32px uebrig, klar ueber der
   WCAG-2.5.8-Mindestgroesse von 24px (clip-path schneidet in Chromium AUCH die Trefferflaeche, nicht nur
   das Gerenderte — siehe .kri-call-icon-btn-Kommentar "Berührungsziel NACH dem Beschnitt"). min-width/
   min-height MUESSEN explizit mit — Hoerer/Kamera tragen (anders als .kri-call-icon-btn--tiny) weiterhin
   die Basisklasse .kri-btn, und genau DIE bekommt in dieser Media Query eine eigene min-height:44px
   (Zeile ~430) — ohne diesen Gegenwert blieb der Knopf (am Android-Emulator per Pixelvergleich
   gemessen, 2026-10-01) trotz width/height:36px auf 44px Hoehe haengen, nur die Breite schrumpfte.
   Hoehere Spezifitaet als BEIDE Media-Query-Regeln (.kri-btn/.kri-menu-trigger, je eine Klasse) gewinnt
   dank zwei Klassen im Selektor UNABHAENGIG von der Reihenfolge im Stylesheet. Die Trefferflaeche bleibt
   bei BEIDEN Knopfarten 36px — NICHT verkleinert, siehe Kommentar an .kri-thread-head-actions oben. */
.kri-thread-head-actions .kri-btn--icon{width:36px;height:36px;min-width:36px;min-height:36px;
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
.kri-thread-head-actions .kri-menu-trigger{width:36px;height:36px;min-width:36px;min-height:36px}
/* Das Dreipunkt-Symbol selbst (drei winzige, senkrecht aufgereihte Punkte, siehe Icon.razor "kebab")
   nutzt von seinem 24x24-Zeichenraster nur eine schmale Mittelspalte — anders als Hoerer/Kamera, deren
   Pfade fast das gesamte Raster ausfuellen. Bei GLEICHER 36px-Box und GLEICHEM Icon-Schritt (Lg, 18px)
   wirkt die Flaeche UM die Punkte dadurch optisch deutlich groesser, obwohl Box/Padding/gap rechnerisch
   identisch sind (siehe Kommentar oben) — die Knopf-FLAECHE bleibt unangetastet (siehe Grenze oben), nur
   das SVG darin wird groesser gerendert (18px→22px, naechste Icon.razor-Stufe IconSize.Xl), damit die
   Punkte ihren Platz in der Box sichtbar besser ausfuellen. Bewusst eine reine CSS-Ueberschreibung des
   svg-Elements (ueberschreibt dessen width/height-Attribut, Standardverhalten fuer SVG in HTML) statt
   eines TriggerIcon-Parameters in MessagesThread.razor: so bleibt KebabMenu/Icon.razor unveraendert und
   jedes ANDERE Dreipunkt-Menue im Produkt exakt gleich — nur dieser eine, beanstandete Knopf wird
   angepasst. */
.kri-thread-head-actions .kri-menu-trigger svg{width:22px;height:22px}

/* Eingabe bleibt IMMER unten stehen — als fester Flex-Endpunkt NACH dem scrollenden .kri-thread-feed
   in .kri-thread-page (siehe dort), unabhaengig von der Verlaufslaenge. position:sticky bleibt
   zusaetzlich als Rueckfallebene stehen (relativ zu .kri-workspace), siehe Kommentar an
   .kri-thread-head oben — analoge Begruendung. Der Hintergrund MUSS deckend sein — sonst scrollt der
   Verlauf sichtbar durch die Eingabe hindurch. Der negative Innenabstand unten gleicht das Padding
   von .kri-dashboard aus, damit unter der Leiste kein heller Streifen stehen bleibt.

   Mobil braucht auch DIESE Regel keinen eigenen bottom-Versatz mehr: .kri-thread-page endet dort
   bereits GENAU an der Oberkante der Tab-Leiste (position:fixed;bottom:var(--kri-btabs-h), siehe
   dessen Kommentar weiter unten), die Eingabe ist einfach der letzte Flex-Endpunkt in dieser
   bereits richtig bemessenen Spalte. Bis 2026-09-28 stand hier `bottom:calc(64px + safe-area-bottom)`,
   weil .kri-composer damals relativ zum DOKUMENT klebte (die ganze mobile Spalte scrollte mit dem
   Body, siehe Git-Historie) und dadurch hinter der fixen Tab-Leiste verschwand. Mit der jetzt fixierten
   .kri-thread-page waere derselbe Versatz ein Fehler in die andere Richtung: Er wuerde die Tab-
   Leisten-Hoehe ein zweites Mal abziehen, obwohl .kri-thread-page sie schon ausspart — exakt das
   Bug-Muster (doppelt abgezogene Leistenhoehe), das diese ganze Umstellung beheben sollte. */
.kri-composer{position:sticky;bottom:0;z-index:2;margin-top:14px;padding:10px 0 24px;
  margin-bottom:-24px;background:var(--kri-paper)}
/* Mobil rueckt die Verfasserzeile an den Rand — sie ist der Teil der Seite, den der Daumen dauernd
   trifft, und stand mit den 24px Seiten- und Bodenabstand von .kri-dashboard sichtbar weiter innen als
   in jedem Messenger (Beanstandung 2026-09-30: "unten und links zu viel Abstand"). Die negativen
   Seitenabstaende heben das Padding von .kri-dashboard auf (dieselbe Technik wie beim -24px unten, das
   den deckenden Hintergrund bis zur Unterkante zieht) und werden durch 10px eigenes Polster ersetzt.
   Der VERLAUF darueber behaelt seine 24px: Blasen am Bildschirmrand klebten sonst an der Kante.
   padding-bottom faellt von 24px auf 8px — die Tab-Leiste darunter bringt ihren eigenen Abstand mit,
   der Rest war doppelt. margin-bottom bleibt bei -24px, sonst bliebe unter der Zeile ein heller
   Streifen aus dem Padding von .kri-dashboard stehen. */
@media (max-width:900px){
  .kri-composer{margin-left:-24px;margin-right:-24px;padding-left:10px;padding-right:10px;
    padding-bottom:8px;margin-bottom:-24px}
}

/* Chat-/Messenger-Verlaufsseiten (CommunityChat.razor, MessagesThread.razor): sticky allein (siehe
   .kri-thread-head/.kri-composer oben) klebt nur, WENN der umgebende Scrollbereich tatsaechlich
   ueberlaeuft. Bei einem kurzen Verlauf (wenige Nachrichten) tat er das nicht -- im Browser gemessen
   2026-09-16: Seitenhoehe == Fensterhoehe bei zwei Nachrichten, die Eingabe stand direkt nach der
   letzten Nachricht mit Leerflaeche darunter, statt am unteren Rand zu stehen. Fix: eine Spalte in
   FESTER Hoehe (Desktop: exakt die Hoehe von .kri-workspace, also 100dvh minus .kri-top) -- Kopf und
   Eingabe liegen darin als normale Flex-Kinder FEST oben/unten, NUR der Verlauf dazwischen
   (.kri-thread-feed) bekommt einen EIGENEN Scrollbereich.
   Height gilt zusaetzlich zu .kri-dashboard (max-width/padding bleiben) -- beide Klassen stehen am
   selben Element. */
.kri-thread-page{display:flex;flex-direction:column;height:calc(100dvh - 60px)}
/* Mobil (mit Bottom-Tab-Bar und App-Bar): KEIN dvh-minus-Pixel-Betrag mehr, sondern position:fixed
   mit top/bottom auf denselben zwei Leisten-Variablen wie .kri-mtopbar/.kri-main-col (siehe Definition
   ganz oben im Mobile-Block) — dieselbe Verankerung, ueber die auch .kri-btabs selbst fest am unteren
   Bildschirmrand haengt. Am Telefon nachgemessen (Android-Emulator, WebView/Chrome ohne eigenes
   `interactive-widget` im Viewport-Meta, also im engineseitigen Default `resizes-visual`, 2026-09-28):
   Oeffnet die Tastatur, aendert sich weder `innerHeight` noch `100dvh` noch die Groesse des Layout-
   Viewports — nur der SICHTBARE (visuelle) Ausschnitt schrumpft. .kri-btabs verfolgte die Tastatur in
   der Praxis trotzdem korrekt (`position:fixed`-Elemente haengen sich in diesem Modus an den
   visuellen statt an den Layout-Viewport), waehrend .kri-thread-page ueber die alte dvh-Rechnung
   seine urspruengliche (zu grosse) Hoehe behielt: Die Spalte endete zu tief, die sticky positionierte
   Eingabe blieb auf halber Bildschirmhoehe haengen, darunter blieb papierfarbene Leerflaeche bis zur
   (korrekt hochgerutschten) Tab-Leiste — exakt der gemeldete Fehler. Mit position:fixed haengt sich
   .kri-thread-page an DENSELBEN Anker wie .kri-btabs und bleibt dadurch unter jeder denkbaren
   Tastatur-/dvh-Interpretation korrekt (auch falls eine kuenftige Engine-Version stattdessen den
   Layout-Viewport schrumpft: dann schrumpft die Spalte einfach mit, weil top/bottom relativ zu genau
   diesem Viewport neu ausgewertet werden) — die Spaltenhoehe MUSS dafuer auf `auto` stehen, sonst
   gewinnt die feste `height` aus der Basisregel gegen die beiden Versatz-Angaben (CSS-Spezifikation
   fuer ueberbestimmte position:fixed-Boxen). Ein Emulator-Grenzfall blieb ungeprueft: Auf DIESEM
   Emulator zeigt Chrome grundsaetzlich keine Bildschirmtastatur (physische Emulator-Tastatur laesst
   die Anfrage danach entfallen, siehe docs/deployment.md) — die obigen Werte stammen aus MDN/WICG-
   Dokumentation zum `interactive-widget`-Standardverhalten, nicht aus einem live beobachteten
   Tastatur-Oeffnen-Screenshot dieses Geraets. */
@media (max-width:900px){
  .kri-thread-page{position:fixed;top:var(--kri-mtopbar-h);bottom:var(--kri-btabs-h);left:0;right:0;
    height:auto}
}
/* min-height:0 bricht die sonst uebliche Flex-Falle: ohne das haelt ein Flex-Kind mit overflow-y:auto
   seine eigene min-height auf "auto" (= Inhalt), waechst also einfach mit dem Verlauf mit, statt bei
   der zugewiesenen Groesse zu scrollen. */
.kri-thread-feed{flex:1;min-height:0;overflow-y:auto}
.kri-pending{position:relative;max-width:240px}
.kri-pending-x{position:absolute;top:2px;right:2px}

/* Eingabezeile (docs/specs/messenger.md, "Die Oberfläche auf dem Telefon", Punkt 1): ein Plus
   (Anhang-Menü), ein wachsendes Textfeld, ein Knopf mit zwei Bedeutungen (Mikrofon/Senden). Ersetzt die
   vorherige Knopfreihe (Bild/Datei/Aufnehmen/Senden nebeneinander), die bei 400px auf zwei Zeilen
   umbrach und die Ruhehöhe der Eingabe auf ~234px trieb. */
.kri-composer-row{display:flex;align-items:flex-end;gap:8px}
.kri-composer-input{flex:1;min-height:44px;max-height:150px;resize:none;overflow-y:auto;
  padding:11px 14px;line-height:1.4}
.kri-composer-action{flex:0 0 auto;width:44px;height:44px;padding:0;display:flex;
  align-items:center;justify-content:center;font-size:19px}
/* touch-action:none NUR am Mikrofon — und das ist keine Feinheit, sondern die Bedingung dafuer, dass
   die Halte-/Wisch-Geste im Android-WebView ueberhaupt ankommt (am Emulator belegt, 2026-09-30):
   Fehlt sie, zieht der Browser die Beruehrung nach der ersten Bewegung fuer das Scrollen des Verlaufs
   ein, schickt ein einzelnes `pointercancel` und danach KEIN `pointermove` mehr. Die Geste stirbt
   lautlos — der Hinweis "wischen" stand da, das Wischen tat nichts. `preventDefault()` im pointerdown
   hilft dagegen NICHT (siehe kriboUi.bindVoiceHold in ui.js). Am Sende-Knopf waere dieselbe Regel
   falsch: Dort gibt es keine Geste, und ein Wischen, das zufaellig auf dem Knopf beginnt, soll die
   Seite ganz normal scrollen.
   user-select/touch-callout dazu: Ein langes Halten loest im WebView sonst Textauswahl bzw. das
   Kontextmenue aus — beides bricht die laufende Geste ab. */
.kri-composer-action--mic{touch-action:none;-webkit-user-select:none;user-select:none;
  -webkit-touch-callout:none}

/* Aufnahme läuft: ersetzt die GANZE Eingabezeile (Text und Aufnahme schliessen sich aus, siehe
   docs/specs/messenger.md). Der Punkt "blinkt" als Aufnahme-Hinweis — kantig statt rund (kein
   border-radius), deshalb dieselbe kleine Skew-Fläche wie sonst auch im Theme.
   align-items:flex-end und KEIN horizontales Innenpolster sind bewusst identisch zu .kri-composer-row
   (docs/specs/messenger.md, "Sprachnachrichten", Abschnitt "Aufnehmen: je Eingabegerät verschieden"): Der
   Stop-Knopf trägt dieselbe .kri-composer-action-Klasse wie zuvor Mikrofon/Senden und landet dadurch am
   exakt selben rechten Rand (kein Innenpolster verschiebt ihn) und derselben Bodenlinie (flex-end, statt
   center) — zwei Klicks direkt hintereinander treffen ohne Mausbewegung denselben Punkt. */
/* flex-wrap:nowrap + der schrumpfende Hinweis darunter: Seit im GESPERRTEN Zustand einer
   Beruehrungs-Aufnahme zusaetzlich ein Sende-Knopf in dieser Leiste steht, passte der Inhalt auf einem
   411px breiten Telefon nicht mehr — am Emulator gesehen (2026-09-30): "Aufnahme abbrechen" brach auf
   zwei Zeilen, der Hinweistext auf drei, und der Sende-Knopf stand halb ausserhalb des Bildschirms.
   Der Hinweis ist das einzige Element, das schrumpfen DARF (die anderen sind Knoepfe und die Dauer),
   deshalb traegt er flex:1 mit Ellipsis statt eines Umbruchs: auf dem Telefon verschwindet er
   praktisch, am Rechner steht er vollstaendig da. */
.kri-recording-bar{display:flex;align-items:flex-end;gap:10px;min-height:44px;flex-wrap:nowrap}
.kri-recording-bar .kri-btn{white-space:nowrap}
.kri-recording-cancel{display:inline-flex;align-items:center;gap:6px}
/* Nur optisch weg, nicht aus dem Zugaenglichkeitsbaum (dieselben Angaben wie .kri-visually-hidden) —
   der Papierkorb daneben traegt die Bedeutung, die Beschriftung bleibt der Name des Knopfs. */
@media (max-width:560px){
  .kri-recording-cancel-text{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
}
.kri-recording-dot{width:10px;height:10px;flex:none;background:var(--kri-danger);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%);
  animation:kri-recording-pulse 1.4s ease-in-out infinite}
.kri-recording-time{font-family:var(--kri-mono);font-variant-numeric:tabular-nums}
.kri-recording-hint{color:var(--kri-ink2);font-size:13px;flex:1 1 auto;min-width:0;
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
@keyframes kri-recording-pulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.kri-recording-dot{animation:none}}

/* Sprachnachricht per Halten aufnehmen, nur Beruehrung (docs/specs/messenger.md, "Sprachnachrichten").
   Solange die Beruehrung den Mikrofon-Knopf haelt, ERSETZT die Aufnahme den linken Teil der
   Verfasserzeile (Plus und Textfeld weichen Zeit und Abbrechen-Hinweis) — wie bei WhatsApp, und anders
   als bis 2026-09-30, wo ein Hinweiskasten ZUSAETZLICH ueber dem Verlauf schwebte, waehrend Plus und
   Textfeld darunter stehen blieben. Die Zeile selbst (und mit ihr der Mikrofon-Knopf) bleibt dabei
   dieselbe — siehe MessagesThread._recordingHeldByTouch fuer das Warum. */
.kri-composer-row--recording{align-items:center;min-height:44px;padding-left:4px}
.kri-voice-cancel{flex:1;display:flex;align-items:center;justify-content:center;gap:4px;
  font-size:13px;color:var(--kri-ink2);white-space:nowrap;overflow:hidden;
  /* Folgt dem Finger nach links (kriboUi.bindVoiceHold setzt --kri-voice-dx auf .kri-composer) —
     ohne Uebergang, sonst liefe die Rueckmeldung der Bewegung hinterher. */
  transform:translateX(var(--kri-voice-dx,0px))}
/* Das offene Schloss ueber dem Mikrofon: Dorthin zieht der Finger zum Sperren. Absolut ueber der
   Eingabe (deren .kri-composer position:sticky ist und damit der Bezugsrahmen), damit es waehrend des
   Haltens keinen Platz in der Zeile beansprucht — dieselbe Technik wie beim frueheren Hinweiskasten.
   Breite 44px = Breite des Mikrofons darunter, right:0 setzt beide auf dieselbe Achse. */
.kri-voice-lock-cue{position:absolute;right:0;bottom:calc(100% + 6px);width:44px;
  display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 0;
  background:var(--kri-tint);color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%);
  transform:translateY(var(--kri-voice-dy,0px))}
/* Kurzer Hinweis nach einer zu kurzen Beruehrung ("halten, nicht tippen") — steht IN der Eingabe ueber
   der Zeile statt als Toast; verschwindet nach wenigen Sekunden von selbst (MessagesThread). */
.kri-voice-hint{margin:0 0 6px;padding:6px 10px;font-size:13px;background:var(--kri-tint);
  color:var(--kri-ink);
  clip-path:polygon(0 var(--kri-skew-s),100% 0,100% calc(100% - var(--kri-skew-s)),0 100%)}
/* Ruhiger Puls am gehaltenen Mikrofon-Knopf statt der blinkenden Punkt-Animation (die steht schon
   links in der Zeile) — hier genuegt eine dezente Umrandung, die zeigt: "es passiert etwas". Das
   Mikrofon wandert zusaetzlich mit dem Finger (siehe .kri-voice-cancel), damit sichtbar wird, dass die
   Geste erkannt ist und nicht ins Leere laeuft. */
.kri-composer-action--recording{box-shadow:0 0 0 3px var(--kri-danger);animation:kri-voice-hold-pulse 1.4s ease-in-out infinite;
  transform:translate(var(--kri-voice-dx,0px),var(--kri-voice-dy,0px))}
@keyframes kri-voice-hold-pulse{0%,100%{box-shadow:0 0 0 3px var(--kri-danger)}50%{box-shadow:0 0 0 6px var(--kri-danger)}}
@media (prefers-reduced-motion:reduce){.kri-composer-action--recording{animation:none}}

/* Antworten und Reaktionen im Messenger.
   Das Zitat sitzt IN der Blase und nicht darüber: Es gehört zur Antwort, nicht zum Verlauf — sonst läse
   es sich wie eine eigene Nachricht. */
.kri-msg-quote {
    border-left: 3px solid var(--kri-line);
    padding: 4px 8px;
    margin-bottom: 6px;
    font-size: 0.86rem;
    color: var(--kri-ink2);
    background: var(--kri-tint);
}

/* kri-msg-foot/kri-msg-act sind mit dem Redesign entfallen (docs/specs/messenger.md, "Die Oberfläche
   auf dem Telefon", Punkt 2): Zeit/Häkchen/Antworten/Reagieren/Weiterleiten stehen jetzt im
   Langdruck-Blatt statt dauerhaft an jeder Blase — siehe .kri-dialog-actions/.kri-menu-item dort sowie
   die wiederverwendeten .kri-reaction-picker/.kri-reaction-choice direkt darunter. */

.kri-reaction-picker {
    display: flex;
    gap: 2px;
    margin-top: 4px;
    padding: 3px;
    background: var(--kri-tint);
    border: 1px solid var(--kri-line);
}

.kri-reaction-choice {
    background: none;
    border: none;
    cursor: pointer;
    padding: 3px 5px;
    font-size: 1.1rem;
    line-height: 1;
}

.kri-reaction-choice:hover:not(:disabled) {
    background: var(--kri-surface);
}

.kri-reactions {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 4px;
}

.kri-reaction {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px 6px;
    background: var(--kri-tint);
    border: 1px solid var(--kri-line);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--kri-ink);
}

/* Die eigene Reaktion ist abgesetzt — ohne das weiss man nach einem Neuladen nicht mehr, ob der Daumen
   von einem selbst kam, und drückt ihn ein zweites Mal. */
.kri-reaction--mine {
    border-color: var(--kri-accent);
}

.kri-reaction-count {
    font-variant-numeric: tabular-nums;
    color: var(--kri-ink2);
}

.kri-reply-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    margin-bottom: 6px;
    background: var(--kri-tint);
    border-left: 3px solid var(--kri-accent);
}

.kri-reply-text {
    flex: 1;
    font-size: 0.86rem;
    color: var(--kri-ink2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Weiterleiten. Der Hinweis steht klein über der Nachricht — er ist eine Herkunftsangabe, keine Aussage. */
.kri-msg-forwarded {
    font-size: 0.78rem;
    color: var(--kri-ink2);
    margin-bottom: 3px;
}

.kri-forward-box {
    padding: 8px;
    margin-bottom: 8px;
    background: var(--kri-tint);
    border-left: 3px solid var(--kri-accent);
}

.kri-forward-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
    font-size: 0.88rem;
    color: var(--kri-ink2);
}

.kri-forward-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.kri-forward-target {
    padding: 4px 10px;
    background: var(--kri-surface);
    border: 1px solid var(--kri-line);
    color: var(--kri-ink);
    cursor: pointer;
    font-size: 0.88rem;
}

.kri-forward-target:hover:not(:disabled) {
    border-color: var(--kri-accent);
}

/* Frist für verschwindende Nachrichten. */
.kri-disappearing {
    margin-top: 14px;
    max-width: 340px;
}

.kri-disappearing label {
    display: block;
    font-size: 0.86rem;
    color: var(--kri-ink2);
    margin-bottom: 4px;
}

.kri-disappearing-hint {
    margin: 5px 0 0;
    font-size: 0.8rem;
}

/* Gruppen im Messenger. Eine Gruppe hat kein Gesicht — das Bild irgendeines Mitglieds zu zeigen wäre
   irreführend, deshalb ein neutrales Zeichen in derselben Grösse wie ein Profilbild. */
.kri-group-avatar {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--kri-tint);
    border: 1px solid var(--kri-line);
    font-size: 1.2rem;
}

/* Community-Chat in der gemeinsamen Gespraechsliste (docs/specs/community-chat.md, "Das MUSS die
   Oberflaeche benennen"): GESTRICHELTER statt durchgezogener Rand, zusaetzlich zur Badge unten. Ein
   Symbol allein reicht laut Spec nicht -- eine Zeile, die auf den ersten Blick wie ein normales
   (verschluesseltes) Gespraech aussieht, waere genau das Missverstaendnis, das die Verschluesselungs-
   zusage bei Direktnachrichten und Gruppen wertlos macht. */
.kri-community-avatar {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--kri-tint);
    border: 1px dashed var(--kri-ink2);
    color: var(--kri-ink2);
    font-size: 1.2rem;
}

.kri-badge--community {
    background: var(--kri-tint);
    border: 1px solid var(--kri-ink2);
    color: var(--kri-ink2);
}

.kri-member-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    background: var(--kri-surface);
    border: 1px solid var(--kri-line);
    cursor: pointer;
}

/* Einfachauswahl in PersonPicker.razor: dieselbe Zeile, aber als <button> statt <label>+Ankreuzfeld --
   braucht deshalb die ueblichen Resets (Breite/Schrift/Ausrichtung), die ein <button> sonst mitbringt. */
.kri-member-row--button {
    width: 100%;
    font: inherit;
    color: inherit;
    text-align: left;
}

.kri-member-row:hover {
    border-color: var(--kri-accent);
}

/* Name + Herkunftszeile (gemeinsame Communities) in PersonPicker.razor. flex:1 + min-width:0 lassen
   beide Zeilen bei knapper Breite (~400px) per Ellipse kuerzen statt die Beruehrungszeile zu sprengen
   oder eine dritte Zeile aufzureissen -- der Name bleibt so die eine verlaessliche Information, egal wie
   schmal der Bildschirm ist. */
.kri-member-main {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.kri-member-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Gedaempft und kleiner als der Name -- der Name bleibt die Hauptinformation, die Community nur die
   Einordnung dazu (siehe PersonPicker.razor, OriginLabel). */
.kri-member-origin {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    color: var(--kri-ink2);
}

/* Systemereignisse in Gruppen: keine Blase, sondern eine ruhige Zeile in der Mitte. Sie sind Vorgänge,
   keine Aussagen von jemandem. */
.kri-msg-system {
    align-self: center;
    text-align: center;
    font-size: 0.82rem;
    color: var(--kri-ink2);
    padding: 3px 10px;
    margin: 2px 0;
}

/* Absender über einer fremden Gruppennachricht — in 1:1 überflüssig, dort gibt es nur eine Gegenseite. */
.kri-msg-sender {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--kri-ink2);
    margin-bottom: 2px;
}

/* Nicht belegte Urheberschaft. Deutlich, aber ohne den Inhalt zu verstecken: Die Nachricht soll sichtbar
   als zweifelhaft dastehen, nicht verschwinden. */
.kri-msg-unverified {
    display: inline-block;
    font-size: 0.78rem;
    color: var(--kri-danger-text);
    border: 1px solid var(--kri-danger);
    padding: 1px 6px;
    margin-bottom: 4px;
}

/* Gruppenverwaltung unter dem Verlauf. */
.kri-group-box {
    margin-top: 18px;
    padding: 12px;
    background: var(--kri-tint);
    border: 1px solid var(--kri-line);
}

/* Zustellungszustände an eigenen Nachrichten. Gelesen wird hervorgehoben, zugestellt bleibt ruhig —
   der Unterschied zwischen „angekommen" und „gesehen" soll sichtbar sein, ohne zu drängen. */
.kri-msg-tick {
    font-size: 0.8rem;
    color: var(--kri-ink2);
    opacity: 0.7;
    letter-spacing: -1px;
}

.kri-msg-tick--read {
    color: var(--kri-teal);
    opacity: 1;
}

/* Anrufe (docs/specs/calls.md) — Klingelbanner, "Anruf läuft"-Leiste (beide MainLayout) und das
   Anruffenster selbst (MessagesCall.razor, unter CallLayout). */
.kri-call-banner {
    position: fixed;
    top: 12px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1200;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 18px;
    background: var(--kri-surface);
    border: 1px solid var(--kri-line);
    filter: var(--kri-shadow);
    max-width: calc(100vw - 24px);
    clip-path: polygon(0 0, 100% var(--kri-skew-m), 100% 100%, 0 calc(100% - var(--kri-skew-m)));
}

.kri-call-banner-name {
    font-weight: 700;
    margin-right: 6px;
}

.kri-call-banner-actions {
    display: flex;
    gap: 8px;
}

/* Annehmen/Ablehnen tragen Symbol UND Wort, solange Platz ist — ZWEI unterschiedliche Hörer-Formen
   (phone/phone-down, siehe Icon.razor), nicht dasselbe Symbol nur in Grün/Rot: wer Farben schlecht
   unterscheidet, sähe sonst zweimal exakt dasselbe Zeichen und müsste bei einem klingelnden Telefon
   raten. Unter ~400px (ein schmales Telefon-/Browserfenster — dieselbe Grössenordnung, ab der
   .kri-call-banner über max-width:calc(100vw - 24px) selbst schon schmaler wird) entfällt NUR das Wort;
   aria-label/title sitzen am Button selbst und bleiben in jedem Fall erhalten. */
.kri-call-banner-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
}

@media (max-width: 400px) {
    .kri-call-banner-btn-label {
        display: none;
    }

    .kri-call-banner-btn {
        width: 44px;
        height: 44px;
        padding: 0;
        justify-content: center;
    }
}

/* Eigener Button statt eines Links: die ganze Zeile ist der Klick-Bereich, kein <a> mit Ziel-URL. */
.kri-call-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 10px 16px;
    margin-bottom: 12px;
    background: var(--kri-tint);
    border: 1px solid var(--kri-line);
    color: var(--kri-ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
    clip-path: polygon(0 var(--kri-skew-s), 100% 0, 100% calc(100% - var(--kri-skew-s)), 0 100%);
}

.kri-call-bar:hover {
    filter: brightness(.97);
}

.kri-call-bar-hint {
    color: var(--kri-ink2);
    font-size: 0.85rem;
}

.kri-call-window {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    text-align: center;
    max-width: 320px;
}

/* Anrufbildschirm, vollflächig statt Formular (docs/specs/calls.md, „Anrufbildschirm" — WhatsApp-Vorbild):
   Bild der Gegenseite (Video) bzw. ein grosser Avatar (Ton) füllt die ganze Fläche, Name/Zustand liegen als
   Kopfzeile darüber, die Bedienung als schwebende Knöpfe unten. .kri-call-window (oben) bleibt für den
   EINFACHEN Fehlerzustand reserviert (kein Anruf zustande gekommen — CallSession._fatal, MessagesCall ohne
   CallId): eine simple, zentrierte Karte statt der vollen Bühne, siehe .kri-call-fatal unten.

   position:fixed statt Teil des normalen Flusses: Web sitzt sie sonst in .kri-center (CallLayout,
   padding:24px) — die Bühne wäre sonst kleiner als das Fenster. MAUI braucht das nicht für die Grösse
   (.kri-call-overlay, MainLayout.razor, ist selbst schon position:fixed;inset:0, siehe dessen eigener
   Kommentar unten — .kri-call-stage deckt darin exakt dasselbe Rechteck nochmal ab, das ist Absicht, kein
   Zufall: der äussere Layer traegt den z-index gegen die App-Leisten, der innere die Optik). inset:0 deckt
   in BEIDEN Wirten exakt das zur Verfügung stehende Rechteck ab (das ~380×640-Popup unter Web, den ganzen
   Bildschirm unter MAUI). isolation:isolate zieht eine eigene Stapelkontext-Grenze, damit nichts hier
   drinnen (Kopf-/Fusszeile, Video, Avatar) mit z-index ausserhalb dieser Bühne konkurriert.
   --kri-primary als Hintergrund ist dieselbe Farbe wie Sidebar/Drawer/App-Bar — für den Tonanruf (kein
   Video, nur der grosse Avatar) bereits AA-kontrastiert gegen --kri-paper/--kri-on-primary-soft geprüft
   (scripts/check_contrast.py, „Nav-Text auf Sidebar/Drawer/App-Bar"). Bei Video ist diese Fläche ohnehin
   nicht sichtbar (das <video> deckt sie vollständig ab, siehe .kri-call-video--remote unten) — die
   eigentliche Bedienung darüber verlässt sich deshalb NICHT auf diese Themen-Farbe, siehe
   .kri-call-topbar/-bottombar unten. */
.kri-call-stage {
    position: fixed;
    inset: 0;
    isolation: isolate;
    background: var(--kri-primary);
}

/* Anruf-Overlay: NUR MAUI/Android (MainLayout.razor, ausserhalb von @Body, siehe dortiger Kommentar) —
   der Platzhalter, in den .kri-call-stage (oben) hineinrendert. isolation:isolate auf der Bühne kapselt
   zwar alles DARIN (Kopf-/Fusszeile, Video, Avatar) gegen z-index-Konkurrenz von aussen, muss aber SELBST
   erst innerhalb des umgebenden Stapelkontexts einsortiert werden — ohne einen eigenen z-index HIER landet
   die ganze isolierte Bühne bei "auto" (~0), und jede App-Leiste mit explizitem z-index (.kri-mtopbar/
   .kri-btabs 30, .kri-drawer-scrim/-drawer/-menu/-appmenu-panel 40/41, #blazor-error-ui 1000, Toast 1100,
   .kri-call-banner/.kri-modal 1200) malt trotzdem sichtbar DARÜBER — das war der Fehler, der beim Umbau
   auf die vollflächige Bühne (d0b61c4) unbemerkt entstand: die alte, ausführliche .kri-call-overlay-Regel
   (padding, eigener Hintergrund, dieser z-index) verschwand komplett, weil die neue .kri-call-stage die
   Optik übernahm — dass die Klasse dabei auch ihren einzigen Zweck verlor, DAS Overlay ueberhaupt vor die
   App-Leisten zu heben, fiel nicht auf: Ohne Gegenstelle sieht ein Test-Anruf (Ton oder Video, endet nach
   Sekunden von selbst) am Emulator fast gleich aus wie einer, der sichtbar VOR statt HINTER App-Kopfzeile/
   Navigation liegt, wenn man nicht gezielt danach schaut. z-index:1300 wählt bewusst NICHT nur "über den
   App-Leisten" (dafür reichte 1000+), sondern über ALLEM, inklusive Toast (1100) UND Modal/Klingelbanner
   (1200): Ein laufender Anruf verlangt die volle, ungeteilte Aufmerksamkeit UND die volle Bedienbarkeit —
   Auflegen/Stumm/Minimieren duerfen nie von einem Toast verdeckt werden, der zufaellig an derselben Kante
   erscheint (.kri-toast sitzt wie .kri-call-bottombar am unteren Rand), und ein Anruf, der gerade erst
   angenommen wurde, darf nicht wieder hinter dem eigenen Klingelbanner verschwinden, solange dessen
   Ausblend-Animation noch läuft. Dieselbe Güteklasse trug schon die ALTE Regel vor diesem Umbau (siehe
   Git-Historie), nur eben versehentlich ohne Wirkung, weil sie mit der ganzen Regel verschwand. */
.kri-call-overlay {
    position: fixed;
    inset: 0;
    z-index: 1300;
}

/* Reiner Blazor-Diff-sicherer Anker (CallSession.razor, docs/specs/calls.md „Mobil"): JS hängt hier die
   <video>-Elemente ein, Blazor selbst rendert nie Kinder hinein. display:contents nimmt dem Anker seine
   eigene Box. Bleibt ein Nachfahre von .kri-call-stage, die wiederum ein Nachfahre von .kri-call-overlay
   ist (MAUI) — dieselbe Verschachtelung wie zuvor unter .kri-call-window, aus demselben Grund: Ein
   Nachfahre mit eigenem Stapelkontext (hier: .kri-call-stage selbst, position:fixed erzeugt IMMER einen)
   malt nach dem Hintergrund seines Vorfahren, das Overlay verdeckt die Videos deshalb nicht (siehe
   CallSession-Klassendoku). */
.kri-call-video-host {
    display: contents;
}

/* Grosser Avatar mittig, NUR solange kein Bild fliesst (Tonanruf oder Video noch nicht zugeschaltet,
   siehe CallSession.IsVideoActive) — füllt dieselbe Fläche, die sonst das Video einnähme. Nie gleichzeitig
   mit einem <video> sichtbar (siehe CallSession-Markup), deshalb unbedenklich auf --kri-primary. */
.kri-call-avatar-stage {
    position: fixed;
    inset: 0;
    z-index: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Gemeinsame, feste (NICHT themenabhängige) Abdunklung für alles, was über einem UNVORHERSEHBAREN
   Videobild liegen kann (Kopf-/Fusszeile, Hinweise) — bewusst KEIN --kri-Token: --kri-primary/--kri-paper
   tauschen zwischen hell/dunkel je Theme, ein Kamerabild kennt dieses Theme aber nicht. Alpha .68 ist kein
   Zufallswert: Selbst im Worst Case eines komplett WEISSEN Videobildes bleibt die gemischte Fläche mit
   diesem Wert unter der Leuchtdichte, die ein AA-Kontrastverhältnis von 4.5:1 gegen reines Weiss verlangt
   (rechnerisch reicht bereits .56; die Marge hier ist bewusst grosser). Deshalb tragen die Symbole/Texte
   darauf ebenfalls feste Farben (#fff bzw. #1a0603 auf Rot, siehe .kri-call-icon-btn), keine Theme-Tokens —
   scripts/check_contrast.py prüft nur Token-Paare aus theme.css, kein Videobild; das hier ist das
   Gegenstück von Hand. */
.kri-call-topbar,
.kri-call-bottombar,
.kri-call-hint {
    background: rgba(10, 8, 6, .68);
    color: #fff;
}

/* Kopfzeile: Name + Zustand ÜBER dem Bild, nicht als Seitenüberschrift (docs/specs/calls.md). Grid statt
   Flex, damit der Name auch MIT Minimieren-Knopf (nur MAUI, linke Spalte) wirklich mittig bleibt — die
   rechte Spalte bleibt als Platzhalter leer, egal ob CanMinimize gesetzt ist. */
.kri-call-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2;
    display: grid;
    /* 56px statt 44px, damit die linke Spalte zum vergrösserten .kri-call-minimize passt (siehe dessen
       Kommentar) — die rechte Spalte bleibt ein leerer Platzhalter und wird nur der Symmetrie wegen
       gleich breit gehalten, damit die mittlere Spalte (Name/Zustand) unabhängig von CanMinimize zentriert
       bleibt. */
    grid-template-columns: 56px 1fr 56px;
    align-items: start;
    gap: 8px;
    padding: calc(10px + env(safe-area-inset-top)) 12px 14px;
}

.kri-call-heading {
    grid-column: 2;
    min-width: 0;
    text-align: center;
}

.kri-call-name {
    font-size: 1.15rem;
    font-weight: 700;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kri-call-state {
    margin: 2px 0 0;
    font-size: 0.9rem;
    opacity: .85;
}

/* Minimieren (nur MAUI, docs/specs/calls.md „Mobil") — eigener Platz oben links, kein Bestandteil der
   Medien-Bedienung unten: Es beendet nichts, es räumt nur aus dem Weg (siehe CanMinimize-Doku in
   CallSession.razor). */
.kri-call-minimize {
    grid-column: 1;
}

/* Schwebende Symbolknöpfe ohne Beschriftung (docs/specs/calls.md, „Anrufbildschirm") — bewusst KEIN
   gemeinsamer, abgerundeter Leisten-Hintergrund darunter: .kri-call-bottombar liefert bereits eine eigene,
   feste Abdunklung (siehe oben), jeder Knopf trägt ZUSÄTZLICH seinen eigenen (dunklen, nicht hellen!)
   Fleck — dunkel, damit er nur dunkler macht, was ohnehin schon dunkel ist, statt die vom Bottombar-Alpha
   errechnete Kontrastgarantie durch eine kompensierende Aufhellung wieder aufzuweichen. Bei .72 Alpha
   bleibt selbst STANDALONE (ohne die Abdunklung der Leiste darunter, rein rechnerisch der schlechtere Fall)
   gegen ein komplett WEISSES Videobild eine Leuchtdichte, die 4.5:1 gegen weisse Symbole klar überschreitet
   (~8.4:1) — verglichen mit der knapperen Rechnung, die für die Leiste selbst reicht (siehe deren
   Kommentar).

   Ursprünglich (bis 2026-09-27) RUND: border-radius:50% ist zwar die ausdrücklich erlaubte Ausnahme in
   scripts/check_design.py, aber das war der falsche Schluss aus einer richtigen Beobachtung — ERLAUBT
   heisst nicht PASSEND. Kribos Formensprache ist durchgehend geschrägt-kantig (--kri-skew*, clip-path);
   runde Knöpfe waren eine Fremdform mitten in dieser Sprache, korrigiert auf Nutzerhinweis (docs/specs/
   calls.md, „Bedienung: Kribos Form statt WhatsApps Form"). Übernommen von WhatsApp war ausdrücklich nur
   die ANORDNUNG der Knöpfe (Lautsprecher · Kamera · Mikrofon · Auflegen, siehe „Bedienung: WhatsApp-
   Reihenfolge" oben), nie deren runde Form. Jetzt dieselbe Schrägung wie .kri-btn--icon (in Kombination
   mit .kri-btn--secondary, siehe VoicePlayer.razor/MessagesThread.razor) — ein 44×44-Symbolknopf, der
   diese Kontur schon vor dieser Änderung trug.

   Berührungsziel NACH dem Beschnitt, nicht nur die CSS-Grösse: Anders als border-radius schneidet
   clip-path in Chromium AUCH die Trefferfläche mit, nicht nur das Gerenderte — ein Knopf ist dort nur da
   klickbar, wo er auch sichtbar ist. Für den hier verwendete Schräg-Umriss (polygon(0 skew, 100% 0, 100%
   calc(100% - skew), 0 100%), ein echtes Parallelogramm) bleibt in der Mitte ein Fenster mit VOLLER Breite
   und einer Höhe von (Grösse − 2×Schrägung) übrig; ausserhalb dieses Fensters läuft die Trefferfläche zu
   den beiden geschrägten Ecken hin aus. Bei 56px/--kri-skew-m (5px) bleiben 46px (56 − 10), bei 64px
   (Auflegen) 54px — beides ≥44px, also KEINE Grössenanpassung nötig für Mute/Kamera/Lautsprecher/Auflegen.
   Minimieren hätte mit den ursprünglichen 44px nur 34px core gehabt (44 − 10 < 44) — deshalb dort auf 56px
   angehoben (.kri-call-minimize.kri-call-icon-btn unten). Der Kamerawechsel-Knopf
   (.kri-call-icon-btn--tiny, 36px) bekommt bewusst die schwächere --kri-skew-s (2px) statt --kri-skew-m —
   derselbe, bereits dokumentierte 24px-Massstab (WCAG 2.5.8) wie vor dieser Änderung, siehe dortiger
   Kommentar. */
.kri-call-icon-btn {
    width: 56px;
    height: 56px;
    min-width: 44px;
    min-height: 44px;
    flex: none;
    border: none;
    clip-path: polygon(0 var(--kri-skew-m), 100% 0, 100% calc(100% - var(--kri-skew-m)), 0 100%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    background: rgba(8, 6, 4, .72);
    color: #fff;
    transition: filter .15s, background-color .15s;
}

.kri-call-icon-btn:hover {
    filter: brightness(1.2);
}

.kri-call-icon-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff;
}

.kri-call-icon-btn:disabled {
    opacity: .55;
    cursor: not-allowed;
}

/* "Eingeschaltet"/aktiver Zustand (stumm, Kamera an) — feste Farben wie der Rest dieser Gruppe, nicht nur
   Farbe als Unterschied: Das Symbol selbst wechselt bereits (mic/mic-off, video/video-off, siehe
   CallSession.razor), diese Umkehrung ist nur die zusätzliche, redundante Bestätigung. */
.kri-call-icon-btn--active {
    background: #fff;
    color: #171310;
}

/* 56px statt der ursprünglichen 44px (siehe .kri-call-icon-btn-Kommentar oben, „Berührungsziel NACH dem
   Beschnitt") — bei --kri-skew-m (5px) hätte eine exakte 44px-Box nur 34px core übrig gehabt, spürbar
   unter dem Berührungsziel. Jetzt dieselbe Grösse wie die Hauptknöpfe der Bedienleiste, nur an anderer
   Stelle (Kopfzeile statt Bedienleiste) — .kri-call-topbar (oben) räumt dafür ihrer ersten/letzten
   Grid-Spalte denselben zusätzlichen Platz ein. */
.kri-call-minimize.kri-call-icon-btn {
    width: 56px;
    height: 56px;
}

/* Auflegen: deutlich abgesetzt statt einer Reihe gleich aussehender Knöpfe (docs/specs/calls.md) — rot,
   grösser, mit eigenem Abstand zu den übrigen Knöpfen (siehe .kri-call-hangup-slot). Textfarbe fest dunkel
   statt #fff — dieselbe, bereits belegte Ausnahme wie bei .kri-btn--danger: Weiss auf --kri-danger
   (#E0492F) liegt nur bei ~4.07:1 (AA-Fail bei kleiner Fläche). --kri-danger ist unabhängig vom Theme
   (nur einmal definiert, siehe :root oben) — hier deshalb als Token statt fest verdrahtet. */
.kri-call-icon-btn--hangup {
    width: 64px;
    height: 64px;
    background: var(--kri-danger);
    color: #1a0603;
}

.kri-call-icon-btn--hangup:hover {
    filter: brightness(1.08);
}

.kri-call-hangup-slot {
    margin-left: 18px;
}

.kri-call-bottombar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
    opacity: 1;
    transition: opacity .3s ease;
}

/* Bei Video blendet die Bedienung nach ein paar Sekunden Ruhe aus, bei Ton bleibt sie stehen — dort gibt
   es nichts zu verdecken (docs/specs/calls.md). Diese Klasse setzt nur CallSession, wenn IsVideoActive
   zutrifft (siehe dortige Doku zu _controlsHidden). */
.kri-call-bottombar--idle,
.kri-call-topbar--idle {
    opacity: 0;
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
    /* "kein Ein-/Ausblenden der Leiste" (docs/specs/calls.md) — bei reduzierter Bewegung bleibt die
       Bedienung immer sichtbar, unabhängig vom Ruhe-Timer in CallSession; die globale Regel weiter unten
       (transition:none!important) nimmt der Änderung ohnehin schon die Animation, das hier verhindert
       zusätzlich das (dann unanimierte, aber sofortige) Verschwinden selbst. */
    .kri-call-bottombar--idle,
    .kri-call-topbar--idle {
        opacity: 1;
        pointer-events: auto;
    }
}

/* Hinweise (Drosselung, Kamera-Fehler) — eigene Fläche mit derselben festen Abdunklung wie Kopf-/
   Fusszeile (siehe oben), kantig (clip-path), nicht rund — Text ist kein Kreis-Fall. */
.kri-call-hint {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(108px + env(safe-area-inset-bottom));
    z-index: 2;
    max-width: calc(100% - 32px);
    padding: 10px 16px;
    font-size: 0.85rem;
    clip-path: polygon(0 var(--kri-skew-s), 100% 0, 100% calc(100% - var(--kri-skew-s)), 0 100%);
}

/* Ende-/Fehlerzustand (docs/specs/calls.md): "Stattdessen schreiben" + "Schliessen" — ruhige Fläche auf
   der Bühnenfarbe (kein Video mehr aktiv, siehe CallSession-Markup), IMMER sichtbar, kein Ruhe-Timer. */
.kri-call-end-actions {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 2;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
}

/* Einfacher Fehlerzustand VOR jedem Anruf-Bild (Gespräch nicht gefunden/geladen, Gruppen nicht
   unterstützt, Mikrofon verweigert) — dieselbe Karte wie früher an dieser Stelle, jetzt zentriert auf der
   vollen Bühne statt in .kri-center. Text/Knopf-Farben: --kri-on-primary-soft/kri-btn* sind gegen
   --kri-primary geprüft (siehe .kri-call-stage), hier unbedenklich, weil zu diesem Zeitpunkt garantiert
   kein Video läuft. */
.kri-call-fatal {
    position: fixed;
    inset: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    text-align: center;
    padding: 24px;
}

.kri-call-fatal-text {
    color: var(--kri-on-primary-soft);
    font-size: 14px;
    margin: 0;
}

/* Video (docs/specs/calls.md) — die beiden <video>-Elemente selbst werden NICHT von Blazor gerendert,
   sondern von messenger-call.js in #kri-call-video-host eingehängt (siehe dessen Klassendoku). Bewusst
   rechteckig (kein border-radius ausser 50%, siehe scripts/check_design.py). remote füllt die volle
   Bühne (WhatsApp-Vorbild: Bild der Gegenseite vollflächig), local ist die kleine Kachel in der Ecke —
   unterhalb der Kopfzeile platziert, damit sie nicht mit Name/Zustand kollidiert, und über der Bedienung
   unten (z-index 1 vs. deren 2, siehe oben) liegt sie darunter, falls sich beides je überlappen sollte. */
.kri-call-video--remote {
    position: fixed;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #000;
}

.kri-call-video--local {
    position: fixed;
    right: 16px;
    top: calc(84px + env(safe-area-inset-top));
    z-index: 1;
    width: 84px;
    height: 112px;
    object-fit: cover;
    border: 2px solid #fff;
    background: #000;
}

/* Kamerawechsel-Knopf, sitzt AUF der eigenen Videokachel statt in der Bedienleiste (docs/specs/calls.md,
   „Kamerawechsel vorn/hinten", WhatsApp-Vorbild) — deshalb dieselbe Positionierung wie .kri-call-video--local
   (right/top), nur an deren UNTEREM rechten Eck ausgerichtet (top-Offset + eigene Höhe = deren Unterkante)
   und mit derselben z-index-Stufe wie Kopf-/Fusszeile (2), damit er über der Kachel (z-index 1) klickbar
   bleibt. Kleiner als die übrigen Anruf-Knöpfe (.kri-call-icon-btn--tiny unten): Diese Kachel ist selbst nur
   84px breit, ein 56px/44px-Knopf sässe kaum noch AUF ihr — 36px bleibt klar über der WCAG-Mindestgrösse von
   24px (2.5.8) und liegt gaenzlich innerhalb der 84×112px-Kachel. */
.kri-call-camera-switch.kri-call-icon-btn {
    position: fixed;
    right: 20px;
    top: calc(84px + 112px - 36px + env(safe-area-inset-top));
    z-index: 2;
}

/* Verkleinerte Variante von .kri-call-icon-btn (siehe .kri-call-camera-switch oben) — eigene Klasse statt
   eines Parameters, weil bislang JEDER Anrufknopf dieselbe Grösse trug; erst der Kamerawechsel-Knopf auf
   der kleinen Videokachel braucht eine kleinere. Eigene, schwächere Schrägung statt der --kri-skew-m der
   Basisklasse (dieselbe Spezifität, gewinnt aber über die Reihenfolge im Stylesheet — diese Regel steht
   NACH .kri-call-icon-btn): Bei 36px liesse --kri-skew-m (5px) nur noch 26px Berührungsziel-Kernfläche
   übrig (siehe .kri-call-icon-btn-Kommentar, „Berührungsziel NACH dem Beschnitt"), --kri-skew-s (2px)
   lässt 32px — mehr Marge über der hier ohnehin geltenden 24px-Mindestgrösse (WCAG 2.5.8, siehe Kommentar
   bei .kri-call-camera-switch oben). */
.kri-call-icon-btn--tiny {
    width: 36px;
    height: 36px;
    min-width: 36px;
    min-height: 36px;
    clip-path: polygon(0 var(--kri-skew-s), 100% 0, 100% calc(100% - var(--kri-skew-s)), 0 100%);
}

/* ---- Menüplanung, Wochenübersicht (Feinschliff 2026-10-18; docs/specs/menueplanung/index.md) ----
   Zeitstrahl der Tagesleiste: horizontal scrollbar, rastet je Woche ein (scroll-snap). Eine Woche ist so breit wie die Leiste und läuft
   nie in die nächste (scroll-snap-stop: always: ein Wisch, eine Woche). Die Scrollleiste ist ausgeblendet; bedient wird per Finger, Maus
   (Rad/Pfeile ‹ ›) und Tastatur (Pfeile ‹ ›, Tabs). */
.mp-timeline{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;position:relative;scrollbar-width:none;overscroll-behavior-x:contain}
.mp-timeline::-webkit-scrollbar{display:none}
.mp-tlweek{flex:0 0 100%;min-width:0;scroll-snap-align:start;scroll-snap-stop:always;display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;box-sizing:border-box;padding:0 1px}
.mp-weekbtn{font-family:var(--kri-display);font-weight:800;font-size:16px;letter-spacing:-.01em;color:var(--kri-ink);background:transparent;border:none;cursor:pointer;
  padding:4px 4px;min-height:44px;max-width:100%;text-align:center;overflow-wrap:anywhere}
.mp-weekbtn:hover{background:var(--kri-tint)}
.mp-weekbtn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-primary)}
/* Ein Tag der Wochenliste (Runde 2): die Kopfzeile fehlt nie; "Heute" ist ein dezentes Abzeichen, kein kräftiges Orange. */
/* Feinschliff 29: Der Seitenkopf „Menüplanung" bleibt immer sichtbar (sticky unter der App-Bar); die Tagesleiste (.kri-weekbar) hängt mit
   top = App-Bar + --mp-pagehead-h darunter. Feste Höhe, damit beide ohne Messen aneinander anschliessen. Deckend (kein Durchscheinen). */
.mp-pagehead{position:sticky;top:var(--kri-mtopbar-h, 0px);z-index:21;height:var(--mp-pagehead-h, 56px);box-sizing:border-box;display:flex;align-items:center;background:var(--kri-paper)}
.mp-pagehead .kri-dash-head{flex:1;min-width:0;margin:0}
.mp-day{margin-bottom:12px;padding:12px 14px;scroll-margin-top:calc(var(--mp-bar-bottom, calc(var(--kri-mtopbar-h, 0px) + 170px)) + 8px)}
.mp-day-head{display:flex;align-items:center;gap:4px 6px;margin-bottom:6px;min-height:44px}
/* Tagesname, "Heute" und Statusabzeichen stehen in EINER Zeile; bei Platzmangel wird nur der Name kürzer ("Sa 17. Okt." statt "Samstag, 17. Oktober"), nie bricht ein Abzeichen um. */
.mp-day-title{flex:1 1 0;min-width:0;display:flex;align-items:center;flex-wrap:nowrap;gap:2px 6px}
.mp-dh{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mp-dh-short{display:none}
@media (max-width:520px){.mp-dh-full{display:none}.mp-dh-short{display:inline}}
.mp-status{flex:none;font-family:var(--kri-mono);font-size:11px;font-weight:600;padding:1px 5px;color:var(--kri-ink2);border:1px solid var(--kri-line);white-space:nowrap}
.mp-status--plan{color:var(--kri-primary);border-color:currentColor}
.mp-glyph{font-size:17px;font-weight:700;width:1.2em;text-align:center}
.mp-today{flex:none;font-size:11.5px;font-weight:600;color:var(--kri-ink2);border:1px solid var(--kri-line);padding:1px 5px;white-space:nowrap}
/* Eine Mahlzeit in der Wochenliste, EINZEILIG: Name, Titel (gekürzt), Zeit; rechts die kleinen Symbolknöpfe ✓ und ⟳. Auf schmalen Geräten rutscht nur die Zeit
   in eine zweite Zeile (sonst bliebe vom Titel nichts). */
.mp-addmeal,.mp-custom-row{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 8px}
.mp-addmeal .kri-input,.mp-custom-row .kri-input{flex:1 1 140px;min-width:0;width:auto}
.mp-meal{display:flex;align-items:stretch;gap:4px;margin-bottom:6px}
.mp-mealbtn{flex:1 1 auto;min-width:0;display:block;text-align:left;font:inherit;color:var(--kri-ink);background:var(--kri-paper);border:1px solid var(--kri-line);
  padding:4px 8px;cursor:pointer;box-sizing:border-box;min-height:44px;line-height:1.25}
.mp-mealbtn--line{display:flex;align-items:center;flex-wrap:wrap;gap:0 8px}
.mp-ml{flex:none}
.mp-mt{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mp-mmeta{flex:1 0 100%;display:flex;align-items:baseline;flex-wrap:wrap;gap:0 6px;min-width:0}
.mp-mx{flex:none;font-size:12px;white-space:nowrap}
.mp-mtime{flex:none;font-size:12px;line-height:1.2}
@media (min-width:640px){.mp-mealbtn--line{flex-wrap:nowrap}.mp-mmeta{flex:none;margin-left:auto;flex-wrap:nowrap}.mp-mt{flex:0 1 auto}}
.mp-mealbtn:hover{background:var(--kri-tint)}
.mp-mealbtn:focus-visible,.mp-dact:focus-visible,.mp-card:focus-visible,.mp-tile:focus-visible,.mp-bar-btn:focus-visible,.mp-undo-btn:focus-visible{outline:none;box-shadow:inset 0 0 0 3px var(--kri-ink),inset 0 0 0 5px var(--kri-surface)}
.mp-mealact{flex:none;display:flex;gap:4px;align-items:stretch}
.mp-iconbtn{width:40px;height:auto;min-height:44px}
.mp-mealdone{display:inline-flex;align-items:center;justify-content:center;width:40px;color:var(--kri-primary)}
.mp-time{font-family:var(--kri-sans);font-size:13px;color:var(--kri-ink2);white-space:nowrap}
/* Das Blatt von unten: ein Dialog am unteren Rand, auf dem Telefon über die ganze Breite, auf Desktop eine schmale Spalte. */
.kri-overlay.mp-sheet-overlay{align-items:flex-end;justify-content:center;padding:0}
.mp-sheet{background:var(--kri-surface);color:var(--kri-ink);border:1px solid var(--kri-card-border);border-bottom:0;width:100%;max-width:560px;
  max-height:88vh;overflow:auto;padding:16px 16px calc(16px + env(safe-area-inset-bottom));box-sizing:border-box;filter:var(--kri-shadow);
  clip-path:polygon(0 var(--kri-skew-m),100% 0,100% 100%,0 100%)}
.mp-sheet-head{display:flex;align-items:flex-start;gap:8px}
.mp-sheet-sub{display:flex;align-items:center;gap:6px}
.mp-head-sub{margin:2px 0 0;font-size:13.5px;display:flex;gap:4px 10px;flex-wrap:wrap;align-items:center}
/* G4: Gang-Kärtchen als wischbare Tab-Liste (scroll-snap), das letzte gestrichelt "+ Gang". */
.mp-cardscroll{position:relative;overflow-x:auto;scroll-snap-type:x proximity;padding:12px 2px 8px;margin:0 -2px;overscroll-behavior-x:contain;scrollbar-width:thin}
.mp-cards{display:flex;gap:8px;width:max-content}
.mp-card{flex:0 0 96px;scroll-snap-align:start;display:flex;flex-direction:column;gap:3px;text-align:left;font:inherit;color:var(--kri-ink);
  background:var(--kri-paper);border:1px solid var(--kri-line);padding:0 0 8px;cursor:pointer;box-sizing:border-box;min-height:44px;overflow:hidden}
.mp-card[aria-selected="true"]{border-color:var(--kri-primary);border-width:2px;padding-bottom:7px;background:var(--kri-tint)}
.mp-card-img{display:flex;align-items:center;justify-content:center;height:44px;background:var(--kri-tint);color:var(--kri-primary)}
.mp-card[aria-selected="true"] .mp-card-img{background:var(--kri-surface)}
.mp-card-cap{font-family:var(--kri-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--kri-ink2);padding:0 7px}
.mp-card-title{font-size:12.5px;font-weight:600;line-height:1.2;padding:0 7px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow-wrap:anywhere}
.mp-card--add{border-style:dashed;align-items:center;justify-content:center;gap:2px;color:var(--kri-primary);padding:6px}
.mp-card--add[aria-selected="true"]{border-style:solid}
.mp-card-plus{display:inline-flex}
.mp-detail{border-top:1px solid var(--kri-line);padding:10px 0 2px}
.mp-detail-cap{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:0 0 4px}
.mp-detail-title{margin:0 0 2px;font-family:var(--kri-display);font-size:19px;font-weight:800;overflow-wrap:anywhere}
.mp-detail-acts{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0 0}
.mp-dact{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;padding:6px 12px;font-family:var(--kri-sans);font-size:13.5px;font-weight:600;
  text-decoration:none;cursor:pointer;border:none;background:var(--kri-tint);color:var(--kri-primary);box-sizing:border-box}
.mp-dact:disabled{opacity:.5;cursor:default}
.mp-tiles{display:flex;gap:8px;flex-wrap:wrap;margin:6px 0 0}
.mp-tile{min-height:44px;padding:8px 18px;font-family:var(--kri-sans);font-size:14px;font-weight:600;cursor:pointer;border:1px solid var(--kri-line);background:var(--kri-paper);color:var(--kri-ink)}
.mp-tile[aria-pressed="true"]{background:var(--kri-accent);color:#241206;border-color:var(--kri-accent)}
/* Die Leiste unten: ✓ Nehmen · ⏱ Zeit · ⋯ Mehr. */
.mp-bar{display:grid;grid-template-columns:1.5fr 1fr 1fr;gap:8px;margin-top:14px}
.mp-bar-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:48px;padding:8px 6px;font-size:14px;box-sizing:border-box;min-width:0}
.mp-bar-btn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mp-bar-btn[aria-disabled="true"]{cursor:default;filter:none}
.mp-bar-btn--main[aria-disabled="true"]{background:var(--kri-tint);color:var(--kri-primary)}
.mp-more{display:flex;flex-direction:column;border:1px solid var(--kri-line);margin-top:12px}
.mp-why{border-top:1px solid var(--kri-line);padding:10px 12px}
.mp-chooser-list{list-style:none;margin:0;padding:0;max-height:45vh;overflow:auto}
/* Rückgängig: dunkler Hinweis unten im Blatt (Live-Region); Farben aus den Tokens, im Dunkelmodus kehrt sich das Paar um. */
.mp-undo-region{margin-top:12px}
.mp-undo{display:flex;align-items:center;justify-content:space-between;gap:12px;background:var(--kri-ink);color:var(--kri-paper);padding:10px 14px;font-size:14px}
.mp-undo-text{min-width:0;overflow-wrap:anywhere}
.mp-undo-btn{flex:none;min-height:44px;padding:0 6px;background:transparent;border:none;color:var(--kri-paper);font:inherit;font-weight:700;text-decoration:underline;cursor:pointer}
/* Geteilter Knopf „Wie Woche N | ▾": zwei getrennte Knöpfe Kante an Kante. */
.mp-split{display:inline-flex;align-items:stretch;gap:2px}
/* Standardwoche/Wochenvorlage: Anzahl je Gang mit − und + (aria-disabled statt disabled: Der Fokus bleibt auf dem Knopf, wenn die Grenze erreicht ist). */
.mce-counts{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}
.mce-count{display:flex;align-items:center;gap:6px;border:1px solid var(--kri-line);padding:4px 6px 4px 10px;min-width:0}
.mce-count-name{flex:1 1 auto;min-width:0;font-weight:600;overflow-wrap:anywhere}
.mce-count-n{min-width:1.6em;text-align:center;font-weight:700}
.mce-step{min-width:44px;min-height:44px;padding:0;font-size:20px;line-height:1}
.mce-step[aria-disabled="true"]{opacity:.45;cursor:default}
@media (max-width:520px){.mp-day{padding:10px}}

/* Feinschliff Runde 4 (Menüplanung): Endlos-Liste der Tage, Wochentrenner, Zustandsknöpfe, Heute-Karte. */
.mp-weekhead{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;margin:18px 0 10px;padding:6px 0;border-bottom:2px solid var(--kri-line);scroll-margin-top:calc(var(--mp-bar-bottom, calc(var(--kri-mtopbar-h, 0px) + 170px)) + 8px)}
.mp-week:first-child .mp-weekhead{margin-top:0}
.mp-weekhead-title{flex:1 1 auto;min-width:0;margin:0;font-family:var(--kri-display);font-size:16px;font-weight:800;letter-spacing:-.01em}
.mp-weekhead-empty{font-weight:600;color:var(--kri-ink2);font-size:13px}
.mp-weekhead-acts{display:flex;flex-wrap:wrap;align-items:center;gap:6px;position:relative}
.mp-sentinel{height:1px;width:100%}
.mp-glyph--done,.mp-tabglyph--done{color:var(--kri-ok-text)}
.mp-day--today{background:var(--kri-tint);border-left:5px solid var(--kri-accent)}
.mp-day-done{margin:0 0 6px;font-weight:600;color:var(--kri-ok-text)}
.mp-mealdone{color:var(--kri-ok-text)}
.mp-mark{flex:none;display:inline-flex;align-items:center;color:var(--kri-ok-text)}
.mp-cardx-layer{position:absolute;top:12px;left:2px;display:flex;gap:8px;pointer-events:none}
.mp-cardx-slot{flex:0 0 96px;display:flex;justify-content:flex-end}
.mp-cardx{pointer-events:auto;margin:2px;width:28px;height:28px;min-width:24px;min-height:24px;padding:0;display:inline-flex;align-items:center;justify-content:center;
  background:var(--kri-surface);color:var(--kri-ink);border:1px solid var(--kri-line);cursor:pointer}
.mp-cardx:hover{background:var(--kri-tint)}
.mp-cardx:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kri-ink)}

/* === Essgewohnheiten als Chips (feinschliff.md, Punkt 32) === */
.mp-chipset{border:0;padding:0;margin:0 0 20px;min-width:0}
.mp-chipset-legend{font-weight:600;margin-bottom:8px}
.mp-chips{display:flex;flex-wrap:wrap;gap:8px}
.mp-chip{font-family:var(--kri-sans);font-size:13px;font-weight:600;min-height:44px;box-sizing:border-box;padding:6px 14px;cursor:pointer;
  background:transparent;color:var(--kri-ink);border:2px solid var(--kri-primary)}
.mp-chip:hover{background:var(--kri-tint)}
.mp-chip[aria-pressed="true"]{background:var(--kri-accent);color:#241206;border-color:var(--kri-accent)}
.mp-chip--add{border-style:dashed}
.mp-chip:focus-visible{outline:none;box-shadow:0 0 0 2px var(--kri-surface),0 0 0 4px var(--kri-ink)}
