/* ============================================================
   Mittelfranken Informatik — style.css
   Dark slate UI, Kupfer-Akzent (#E09A4A) als Mittelfranken-Informatik-Signatur.
   Fonts: Space Grotesk (Display) / Inter (Body) / JetBrains Mono (IDs)
   ============================================================ */

/* ---------- Schriften ----------
   Lokal, nicht von Google. Zwei Gruende: kein Aufruf nach aussen bei jedem
   Seitenladen, und das gerenderte PDF benutzt GARANTIERT dieselben Dateien wie
   die Vorschau. Fehlt eine Schrift beim Rendern, substituiert Chromium still
   eine andere — das Layout verschiebt sich ohne Fehlermeldung.
   Lizenz (SIL OFL 1.1) und Herkunft: frontend/fonts/README.md */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('../fonts/inter-400.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('../fonts/inter-500.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('../fonts/inter-600.woff2') format('woff2'); }
@font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('../fonts/spacegrotesk-500.woff2') format('woff2'); }
@font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('../fonts/spacegrotesk-600.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('../fonts/jetbrainsmono-400.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('../fonts/jetbrainsmono-500.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('../fonts/jetbrainsmono-700.woff2') format('woff2'); }

:root {
  /* ---------- FLAECHENFARBEN — background. Nie als color. ----------
     Von dunkel nach hell: die Seite, das Panel darauf, der aufgeklappte
     Kasten darin. `--line` und `--line-soft` sind Trennlinien und gehoeren
     dazu, weil sie eine Kante zeichnen und keinen Text tragen. */
  --bg:        #0A0D13;
  --surface:   #12171F;
  --raised:    #19212B;
  --hover:     #222C39;
  --line:      #242E3B;
  --line-soft: #1A212B;

  /* ---------- TEXTFARBEN — color. Nie als background hinter Text. ----------
     Am 01.09.2026 stand `--faint` als Flaeche am Bearbeiter im Rechnungsbogen
     (`.bg-kt-auf > td`). Was sie den Inhalten darauf gab, gerechnet: Text
     2,89:1, Aufschriften 1,14:1, der Knopfrand 1,07:1 — und daraus wurden
     DREI Befunde an einem Tag (unsichtbarer Rand, blasse Aufschriften,
     unlesbarer Hinweis), jeder einzeln behoben, bevor die Flaeche auffiel.

     Punkte und Balken OHNE Text sind ausgenommen: gemessen am selben Tag,
     fuenf Faelle (Rollbalken, `.msg-event .dot`, `.odoo-link .st.off`) — dort
     traegt die Farbe eine Form, keinen Untergrund. Raender ebenso, dreizehn
     Faelle, ueberwiegend `:hover`. Ein Rand ist keine Flaeche.

     Rangfolge faint < muted < text. */
  --text:   #EAEEF5;
  --muted:  #8C97A8;
  /* Angehoben von #5A6577 (27.07.2026). Der alte Wert ergab auf --surface nur
     3,05:1 — kleine Schrift braucht 4,5:1, und JEDES Datum in der Zeitleiste
     benutzt --faint. Gemessen, nicht geschaetzt: #808DA1 liefert 5,34:1 auf
     --surface, 4,83:1 auf --raised und 4,20:1 auf --hover (dort steht nur
     Grossschrift). Die Rangfolge faint < muted < text bleibt erhalten. */
  --faint:  #808DA1;

  /* ---------- AKZENTFARBEN — Raender, Hervorhebung. ---------- */
  --accent:      #E6A256;
  --accent-dim:  #C2843E;
  --accent-soft: rgba(230,162,86,.12);
  --accent-grad: linear-gradient(135deg, #EFB069 0%, #D98A3C 100%);
  --ring:        rgba(230,162,86,.30);

  --s-neu:     #5BA8EA;
  --s-offen:   #9AA8C0;
  --s-arbeit:  #E6A256;
  --s-warten:  #B08AE0;
  --s-geloest: #4EC58C;
  --s-zu:      #5C6678;

  --p-kritisch: #F0606B;
  --p-hoch:     #E6A256;
  --p-normal:   #5BA8EA;
  --p-niedrig:  #566175;

  --ok:     #4EC58C;
  --danger: #F0606B;

  /* WhatsApp-Kanal. Bewusst dunkler als --s-geloest (#4EC58C), damit ein
     grüner Rahmen am Composer nicht wie der Status „Gelöst" gelesen wird. */
  --wa:      #3AAE72;
  --wa-soft: rgba(58,174,114,.12);

  /* ============ Die drei Arten, EIN Ort (22.08.2026) ============
     Wer hat geschrieben — Kunde, wir, oder die Maschine. Das ist etwas anderes
     als der KANAL (Mail/WhatsApp) und etwas anderes als der ZUSTAND (ungelesen,
     erledigt). Drei Sachen, drei Farbsprachen, und keine erbt von der anderen.

     Hierher gezogen, weil zwei Ansichten sie brauchen: die Zeitleiste im Ticket
     und die Glocke. Vorher standen die Werte direkt in den `.msg.*`-Regeln, und
     die Glocke hätte sie ein zweites Mal erfinden müssen.

     Gemessen am 22.08.2026, im Browser und nicht im Stilblatt: `.msg.mail_out`
     (Spezifität 0,2,0) stach `.msg-art-eigen` (0,1,0) — eine eigene Antwort
     bekam #E6A256 statt #4EC58C. Die Artregel war seit dem 20.08. da und kam
     zur Hälfte nicht an. Deshalb setzen die Typregeln jetzt keine linke Kante
     mehr; sie färben nur noch ihr Schild.

     --art-system ist NICHT --line (#242E3B): das ist die Rahmenfarbe, auf
     dunklem Grund praktisch unsichtbar. Eine dritte Art, die man nicht sieht,
     ist keine dritte Art. */
  --art-ein:    #5BA8EA;   /* eingehend — vom Kunden, gleich über welchen Kanal */
  --art-aus:    #4EC58C;   /* von uns — Antwort, Notiz, alles Eigene */
  --art-system: #6B7A90;   /* Maschine — Ereignisse, Wachen, Einfügespuren */

  --radius: 14px;
  --radius-sm: 9px;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.25);
  --shadow:    0 6px 20px -6px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 28px 64px -16px rgba(0,0,0,.65);

  --font-display: 'Space Grotesk', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  color-scheme: dark; /* native Controls (Datepicker, Checkboxen) dunkel */
}

/* ---------- Die Markierung muss man SEHEN (Punkt 2, 21.09.2026) ----------
   Hier stand `background: var(--accent-soft)` — ein Orange mit 12 % Deckkraft.
   Vom Browser gezeichnet und zurueckgelesen: die markierte Flaeche unterschied
   sich vom Grund um 1,19:1 auf der Seite und 1,23:1 im Dialog. Die Schwelle
   fuer eine Flaeche, die einen Zustand zeigt, ist 3:1 — also rund 40 % davon.
   Der Text litt dabei nicht (14,03:1), die Marke war nur keine.

   Mehr Deckkraft loest es nicht: bei 60 % nimmt die Flaeche ihre Schwelle
   (3,81:1) und der Text faellt unter seine (4,16:1 gegen 4,5). Zwei
   Anforderungen auf demselben durchscheinenden Grund ziehen gegeneinander.
   Der Ausweg ist eine DECKENDE Flaeche mit festgelegter Schriftfarbe — genau
   das, was jeder Browser von Haus aus tut.

   Gemessen mit --accent-dim: 6,17:1 gegen die Seite, 5,71:1 im Dialog, und
   der dunkle Text darauf 6,17:1. Beide Schwellen mit Reserve.

   NICHT der volle Akzent (8,95:1): --accent heisst im Haus `aktiv`. Eine
   Markierung in derselben Farbe waere eine zweite Bedeutung fuer sie. */
::selection { background: var(--accent-dim); color: var(--bg); }

/* ---------- Scrollbars ---------- */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: 6px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--faint); }

/* ============ App-Shell ============ */
.app {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

/* ---------- Sidebar ---------- */
.sidebar {
  grid-area: side;
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 18px 12px;
}

.brand {
  display: block;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.02em;
  padding: 2px 10px 20px;
  user-select: none;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.brand:hover .y { color: var(--accent-dim); }
.brand .y { color: var(--accent); }
.brand small {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 3px;
}

.nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }

.nav a {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 500;
  transition: background .12s, color .12s;
}
.nav a:hover { background: var(--raised); color: var(--text); }
.nav a.active { background: var(--accent-soft); color: var(--accent); }
.nav a svg { width: 17px; height: 17px; flex: none; }
.nav a .count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  background: var(--raised);
  color: var(--muted);
  padding: 1px 7px;
  border-radius: 20px;
}
.nav a.active .count { background: rgba(224,154,74,.22); color: var(--accent); }

.nav .sep {
  margin: 14px 10px 8px;
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 600;
}

.side-foot {
  border-top: 1px solid var(--line);
  padding-top: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--muted);
}
.side-foot .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); flex: none; }
.side-foot .dot.off { background: var(--danger); }

/* ---------- Topbar ---------- */
.topbar {
  grid-area: top;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 22px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.search {
  flex: 1;
  min-width: 0;
  max-width: 440px;
  position: relative;
}
.search input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 8px 12px 8px 36px;
  font-family: var(--font-body);
  font-size: 13px;
  outline: none;
  transition: border-color .12s;
}
.search input:focus { border-color: var(--accent-dim); }
.search input::placeholder { color: var(--faint); }
.search svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; color: var(--faint); }

.topbar .spacer { flex: 1; }

.user-chip {
  display: flex; align-items: center; gap: 9px;
  font-size: 13px; color: var(--muted); font-weight: 500;
}

/* ---------- Avatare ---------- */
.avatar {
  width: 27px; height: 27px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  flex: none;
  user-select: none;
}
.avatar.sm { width: 22px; height: 22px; font-size: 9.5px; }
.avatar.c { background: rgba(224,154,74,.2); color: var(--accent); }
.avatar.d { background: rgba(91,168,234,.2); color: var(--s-neu); }
.avatar.none { background: var(--raised); color: var(--faint); }
.avatar.team { background: rgba(120,140,170,.18); color: var(--muted); font-size: 12px; }
.avatar.team.sm { font-size: 10px; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--line);
  background: var(--raised);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background .12s, border-color .12s;
  white-space: nowrap;
}
.btn:hover { background: var(--hover); border-color: var(--faint); }
.btn svg { width: 15px; height: 15px; }

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #1A1209;
  font-weight: 600;
}
.btn.primary:hover { background: #EBAB60; border-color: #EBAB60; }

.btn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--raised); color: var(--text); }

.btn.danger { color: var(--danger); }
.btn.sm { padding: 5px 10px; font-size: 12px; }

/* ---------- Main ---------- */
.main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 30px 40px;
}

/* Ansichten, die die Fensterhoehe selbst ausfuellen (Chat): der SEITENKOERPER
   scrollt dann nicht, die Spalten darin scrollen einzeln.

   Vorher stand am Chat `height: calc(100vh - 130px)` — eine geratene Konstante.
   Am 28.07.2026 im Fenster gemessen: bei 1512x760 war `.ch-layout` 630 px hoch,
   die Schiene darin aber 5673 px und der Verlauf 5568 px. Grund: Grid- und
   Flex-Kinder haben `min-height: auto` und wachsen ueber den Container hinaus,
   statt zu scrollen. Das `overflow-y: auto` an den Spalten griff deshalb nie,
   der Ueberlauf lief nach aussen, und `#main` scrollte alles zusammen.

   Keine Konstante mehr: `#main` ist schon ein Flex-Kind mit `min-height: 0`,
   also genuegt `flex: 1` am Layout — die Hoehe kommt aus dem Fenster. */
.main.main-fest { display: flex; overflow: hidden; padding-bottom: 24px; }
.main.main-fest > * { flex: 1; min-height: 0; }

.page-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}
.page-head h1 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -.01em;
}
.page-head .sub { color: var(--muted); font-size: 13px; }
.page-head .actions { margin-left: auto; display: flex; gap: 9px; }

/* ============ Dashboard ============ */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.stat.click { cursor: pointer; transition: border-color .12s, transform .12s; }
.stat.click:hover { border-color: var(--accent-dim); transform: translateY(-1px); }
.stat.warn .value { color: var(--accent); }
.stat .label { font-size: 12px; color: var(--muted); font-weight: 500; margin-bottom: 8px; }
.stat .value {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -.02em;
  line-height: 1;
}
.stat .value small { font-size: 13px; color: var(--faint); font-weight: 400; margin-left: 4px; }
.stat .trend { font-size: 11.5px; margin-top: 8px; font-family: var(--font-mono); }
.stat .trend.up { color: var(--ok); }
.stat .trend.down { color: var(--danger); }
.stat .trend.flat { color: var(--faint); }
.stat.alert .value { color: var(--p-kritisch); }

.dash-cols { display: grid; grid-template-columns: 1.7fr 1fr; gap: 14px; }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.panel-head {
  display: flex;
  align-items: center;
  padding: 13px 17px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-display);
  font-size: 13.5px;
  font-weight: 600;
}
.panel-head .more { margin-left: auto; font-size: 12px; color: var(--accent); cursor: pointer; font-family: var(--font-body); font-weight: 500; }

/* ---------- Ticket-Zeilen (Dashboard / Liste) ---------- */
.t-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 11px 17px;
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
  transition: background .1s;
  position: relative;
}
.t-row:last-child { border-bottom: 0; }
.t-row:hover { background: var(--raised); }
.t-row::before {
  content: '';
  position: absolute;
  left: 0; top: 8px; bottom: 8px;
  width: 3px;
  border-radius: 2px;
  background: var(--pcolor, transparent);
}
.t-row .num {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--muted);
  background: var(--raised);
  padding: 2px 7px;
  border-radius: 5px;
  flex: none;
}
.t-row .subj {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 500;
  font-size: 13.5px;
}
.t-row .cust { color: var(--faint); font-size: 12px; flex: none; max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.t-row .age { font-family: var(--font-mono); font-size: 11px; color: var(--faint); flex: none; width: 44px; text-align: right; }
/* ---------- Das Kanalabzeichen (117.4, 17.09.2026) ----------
   Nur an Zeilen mit einem FREMDEN Kanal (mail, whatsapp, website) — `system`
   und `manuell` sind der Normalfall und tragen keins. Ein Abzeichen, das an
   der Mehrzahl der Zeilen den Normalfall nennt, ist Dekoration.

   Die KURZE Form (`Mail`, `WA`, `Web`), und das ist gemessen: das volle Wort
   kostet 66 px, die kurze 49. Bei 560 px Listenbreite bleiben dem Betreff
   damit 107 statt 90 px — und die Zeile ist seit 108 die, an der ein
   zusaetzliches starres Element dem nachgiebigen Nachbarn den Platz nimmt.

   Gedaempft wie `.cust` und `.age`: es ist eine Herkunftsangabe, kein
   Zustand. Das Statusabzeichen daneben traegt Farbe, dieses nicht — sonst
   konkurrieren zwei Abzeichen in derselben Zeile um dieselbe Aufmerksamkeit. */
.t-row .t-kanal {
  flex: none;
  font-size: 11px;
  color: var(--faint);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 6px;
  white-space: nowrap;
}

/* ---------- Badges ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 600;
  padding: 2.5px 9px;
  border-radius: 20px;
  white-space: nowrap;
  flex: none;
}
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.neu        { color: var(--s-neu);     background: rgba(91,168,234,.12); }
.badge.offen      { color: var(--s-offen);   background: rgba(154,168,192,.12); }
.badge.in_arbeit  { color: var(--s-arbeit);  background: rgba(224,154,74,.12); }
.badge.warten     { color: var(--s-warten);  background: rgba(176,138,224,.12); }
.badge.geloest    { color: var(--s-geloest); background: rgba(78,197,140,.12); }
.badge.geschlossen{ color: var(--s-zu);      background: rgba(92,102,120,.15); }

.prio {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  /* Gross, wie bisher — seit 126 aus dem Blatt, nicht aus einem zweiten Wort
     in app.js. Die Liste hat eine Beschriftung je Stufe. */
  text-transform: uppercase;
  flex: none;
}
.prio.kritisch { color: var(--p-kritisch); }
.prio.hoch     { color: var(--p-hoch); }
.prio.normal   { color: var(--p-normal); }
.prio.niedrig  { color: var(--p-niedrig); }

/* ---------- Aktivität ---------- */
.activity { padding: 6px 0; }
.act-row { display: flex; gap: 12px; padding: 10px 17px; font-size: 12.5px; }
.act-row .when { font-family: var(--font-mono); font-size: 10.5px; color: var(--faint); flex: none; width: 40px; padding-top: 2px; }
.act-row .what { color: var(--muted); line-height: 1.45; }
.act-row .what b { color: var(--text); font-weight: 600; }
.act-row .what .mono { font-family: var(--font-mono); font-size: 11px; color: var(--accent); }

/* ============ Kanban-Board ============ */
.board {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(240px, 1fr);
  gap: 13px;
  align-items: start;
  min-height: calc(100vh - 170px);
}

.col {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 150px);
}
.col-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 12px 14px 10px;
  border-top: 2px solid var(--ccolor);
  border-radius: var(--radius) var(--radius) 0 0;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
}
.col-head .cnt {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
  background: var(--raised);
  padding: 1px 8px;
  border-radius: 20px;
}
.col-body {
  padding: 6px 9px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  flex: 1;
  min-height: 60px;
}
.col-body.dragover { background: rgba(224,154,74,.05); border-radius: 0 0 var(--radius) var(--radius); }

.card {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 11px 12px 11px 15px;
  cursor: grab;
  position: relative;
  transition: border-color .12s, transform .12s;
}
.card:hover { border-color: var(--faint); }
.card:active { cursor: grabbing; }
.card.dragging { opacity: .45; transform: rotate(1.5deg); }
.card::before {
  content: '';
  position: absolute;
  left: 0; top: 9px; bottom: 9px;
  width: 3px;
  border-radius: 2px;
  background: var(--pcolor);
}
.card .top { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.card .num { font-family: var(--font-mono); font-size: 10.5px; color: var(--muted); }
.card .top .prio { margin-left: auto; }
.card .subj { font-size: 13px; font-weight: 500; line-height: 1.4; margin-bottom: 9px; }
.card .bot { display: flex; align-items: center; gap: 8px; }
.card .cust { font-size: 11.5px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; }
.card .age { font-family: var(--font-mono); font-size: 10.5px; color: var(--faint); }

/* ============ Ticket-Liste (Tabelle) ============ */
.filters { display: flex; gap: 9px; margin-bottom: 16px; flex-wrap: wrap; }
.filters select, .filters input {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  padding: 7px 11px;
  outline: none;
}
.filters select:focus, .filters input:focus { border-color: var(--accent-dim); }

/* ============ Ticket-Detail ============ */
.detail-grid { display: grid; grid-template-columns: 1fr 296px; gap: 16px; align-items: start; }

.detail-head {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin-bottom: 16px;
}
.detail-head .row1 { display: flex; align-items: center; gap: 11px; margin-bottom: 9px; flex-wrap: wrap; }
.detail-head .num {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 3px 10px;
  border-radius: 6px;
}
.detail-head h1 {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.35;
}
.detail-head .meta { display: flex; gap: 16px; margin-top: 11px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted); align-items: center; }
.detail-head .meta .mono { font-family: var(--font-mono); font-size: 11.5px; }

.detail-controls { display: flex; gap: 9px; margin-left: auto; flex-wrap: wrap; }
.detail-controls select {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 12.5px;
  padding: 6px 9px;
  outline: none;
  cursor: pointer;
}

/* ---------- Timeline ---------- */
.timeline { display: flex; flex-direction: column; gap: 11px; }

.msg {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.msg .msg-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line-soft);
  font-size: 12.5px;
}
.msg .msg-head .who { font-weight: 600; }
.msg .msg-head .when { margin-left: auto; font-family: var(--font-mono); font-size: 11.5px; color: var(--faint); }
.msg .msg-body { padding: 14px 16px; font-size: 13.5px; line-height: 1.6; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }

/* Die linke Kante gehoert der ART (siehe --art-* oben), nicht dem Typ. Bis zum
   22.08.2026 setzten diese drei Regeln sie ebenfalls und stachen die Artregel,
   weil sie eine Klasse mehr tragen. Der HINTERGRUND der Notiz bleibt: er sagt
   „intern", und das ist eine Aussage ueber die Sichtbarkeit, keine ueber den
   Absender. */
.msg.note     { background: rgba(176,138,224,.04); }
.msg .tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 4px;
}
/* WhatsApp: Kunde und wir müssen auf einen Blick zu unterscheiden sein.
   Ohne farbige Kante sahen beide Richtungen identisch aus — beim Testen mit
   der eigenen Nummer war überhaupt nicht mehr erkennbar, wer geschrieben hat. */
/* Auch hier keine Kante mehr — sie kam von der Art. Die Schilder unten
   behalten ihre Kanalfarbe: WhatsApp bleibt an seinem Gruen erkennbar, ohne
   dass zwei Sachen um dieselbe Kante streiten. */
.msg.wa_in  .tag { color: var(--s-neu); background: rgba(91,168,234,.12); }
.msg.wa_out .tag { color: var(--wa); background: var(--wa-soft); }

.msg.mail_in  .tag { color: var(--s-neu); background: rgba(91,168,234,.12); }
.msg.mail_out .tag { color: var(--accent); background: var(--accent-soft); }
.msg.note     .tag { color: var(--s-warten); background: rgba(176,138,224,.12); }

/* ---------- Antwort-Editor ---------- */
/* Antwort-Bereich: zwei KLAR getrennte Composer (Kundenmail vs. interne Notiz) */
.reply { margin-top: 14px; display: flex; flex-direction: column; gap: 12px; }
.compose {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
/* Kundenmail = akzentuiert (geht nach außen!), Notiz = gedämpft/gelblich (intern) */
.compose.mail { border-left: 3px solid var(--accent); }
.compose.note { border-left: 3px solid #c9a227; background: rgba(201,162,39,.04); }
/* WhatsApp = eigener Kanal, grün abgesetzt von der Mail */
.compose.whatsapp { border-left: 3px solid var(--wa); }
.compose.whatsapp .compose-to { color: var(--wa); font-family: var(--font-mono); }

/* ---------- WhatsApp: Servicefenster-Countdown ----------
   Drei Zustände über data-zustand, gesetzt vom Ticker in app.js. Die Farbe
   allein trägt die Aussage nicht — der Text nennt die Restzeit immer. */
.wa-fenster {
  margin-left: auto; display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 10px; border-radius: 999px; font-size: 11.5px;
  border: 1px solid var(--line); background: var(--raised); white-space: nowrap;
}
.wa-fenster .wa-titel { color: var(--muted); }
.wa-fenster .wa-rest { font-family: var(--font-mono); font-weight: 600; color: var(--text); }
.wa-punkt { width: 7px; height: 7px; border-radius: 50%; background: var(--wa); flex: none; }

.wa-fenster[data-zustand="warnung"] { border-color: var(--accent-dim); background: var(--accent-soft); }
.wa-fenster[data-zustand="warnung"] .wa-punkt { background: var(--accent); }
.wa-fenster[data-zustand="warnung"] .wa-rest { color: var(--accent); }

.wa-fenster[data-zustand="zu"] { border-color: rgba(240,96,107,.45); background: rgba(240,96,107,.10); }
.wa-fenster[data-zustand="zu"] .wa-punkt { background: var(--danger); }
.wa-fenster[data-zustand="zu"] .wa-rest { color: var(--danger); }

/* Hinweis unter dem Feld, wenn das Fenster während des Tippens zufällt.
   Das Feld wird gesperrt, der Text bleibt stehen. */
.wa-zu-hinweis {
  margin: 0 14px 10px; padding: 9px 12px; border-radius: var(--radius-sm);
  border: 1px solid rgba(240,96,107,.35); background: rgba(240,96,107,.08);
  color: var(--text); font-size: 12.5px; line-height: 1.5;
}
.compose.whatsapp textarea:disabled { opacity: .6; cursor: not-allowed; }

/* Handy: Titel, Empfänger und Countdown quetschen sich sonst in eine Zeile.
   Der Countdown bekommt eine eigene, linksbündig unter dem Titel. */
@media (max-width: 620px) {
  /* Gilt für alle Composer: sonst quetscht sich der Mail-Kopf mit dem
     Baustein-Auswahlfeld auf drei Zeilen. Bei geschlossenem Fenster ist die
     Mail der Ausweichweg zum Kunden — sie muss auf dem Handy taugen. */
  .compose-head { flex-wrap: wrap; row-gap: 6px; }
  .compose-head .tpl-select { margin-left: 0; }
  .compose.whatsapp .wa-fenster { margin-left: 0; }
}

/* Ausweg bei geschlossenem Fenster: Verweis auf die Mail-Antwort. */
.wa-ausweg {
  margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(240,96,107,.25);
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  color: var(--muted); font-size: 12px;
}
.wa-ausweg .mono { font-family: var(--font-mono); color: var(--text); }

/* Sendefehler. Bleibt stehen bis zum nächsten Versuch — ein Toast wäre weg,
   bevor jemand „Systembenutzer prüfen" gelesen hat. */
.wa-fehler {
  margin: 0 14px 10px; padding: 10px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--raised);
  font-size: 12.5px; line-height: 1.55; color: var(--text);
}
.wa-fehler[data-art="fenster_zu"],
.wa-fehler[data-art="empfaenger"] { border-color: var(--accent-dim); background: var(--accent-soft); }
.wa-fehler[data-art="token"],
.wa-fehler[data-art="konfiguration"] { border-color: rgba(240,96,107,.45); background: rgba(240,96,107,.10); }
.wa-fehler-zusatz { margin-top: 5px; color: var(--muted); font-size: 11.5px; }
.wa-fehler .mono { font-family: var(--font-mono); }

/* ---------- Zustellstatus ausgehender Nachrichten ----------
   Kein reines Farbspiel: jede Stufe trägt ihr Wort. `failed` wird nicht als
   Häkchenfarbe abgetan, sondern bekommt zusätzlich ein Band an der Nachricht. */
.wa-zustell {
  font-size: 10.5px; letter-spacing: .3px; text-transform: uppercase;
  padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); background: var(--raised);
}
.wa-zustell.delivered { color: var(--wa); border-color: rgba(58,174,114,.4); background: var(--wa-soft); }
.wa-zustell.read      { color: var(--wa); border-color: var(--wa); background: var(--wa-soft); font-weight: 600; }
.wa-zustell.failed    { color: var(--danger); border-color: var(--danger); background: rgba(240,96,107,.14); font-weight: 700; }
/* Mail: der Versand ist gespeichert, aber nicht bestätigt. Bewusst auffällig —
   „unbestätigt" ist kein Nebenzustand, sondern eine offene Frage. */
.wa-zustell.wird_gesendet { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }

/* Klassennamen tragen historisch `wa-`, gelten aber fuer jeden Kanal: der
   Zustellstatus ist kanalneutral. Beide Namen greifen, damit ein aelterer
   Renderpfad nicht ploetzlich ungestylt dasteht. */
.msg.wa-gescheitert, .msg.gescheitert { border-color: rgba(240,96,107,.5); box-shadow: inset 3px 0 0 var(--danger); }
.wa-gescheitert-band, .gescheitert-band {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 12px 12px; padding: 8px 11px; border-radius: var(--radius-sm);
  background: rgba(240,96,107,.12); border: 1px solid rgba(240,96,107,.4);
  color: var(--text); font-size: 12.5px;
}
.wa-gescheitert-band .mono, .gescheitert-band .mono { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }

/* Medienhinweis in einer eingehenden Nachricht — Dateien werden bewusst noch
   nicht geholt, der Hinweis soll sich nicht wie Kundentext lesen. */
.wa-medium {
  display: inline-block; margin-bottom: 6px; padding: 4px 10px;
  border-radius: var(--radius-sm); border: 1px dashed var(--line);
  background: var(--raised); color: var(--muted); font-size: 12px;
}

/* Kennzeichnung der Nummer im Ticketkopf */
.wa-zuordnung { padding: 1px 8px; border-radius: 999px; font-size: 11.5px; border: 1px solid var(--line); }
.wa-zuordnung.ok { color: var(--wa); border-color: rgba(58,174,114,.4); background: var(--wa-soft); }
.wa-zuordnung.offen { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }

/* ============ WhatsApp-Tab: Gespräche ============ */
.wa-split { display: grid; grid-template-columns: 340px 1fr; gap: 16px; align-items: start; }
.wa-liste-spalte, .wa-verlauf-spalte { min-width: 0; }

.wa-liste-kopf { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.wa-liste-kopf input[type="search"] {
  width: 100%; padding: 8px 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface); color: var(--text); font-size: 13px;
}
.wa-filter { display: flex; gap: 5px; flex-wrap: wrap; }
.wa-f {
  padding: 3px 9px; border-radius: 999px; font-size: 11.5px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
}
.wa-f:hover { border-color: var(--accent-dim); }
.wa-f.active { border-color: var(--wa); color: var(--wa); background: var(--wa-soft); }
.wa-liste-info { font-size: 11px; color: var(--faint); }

.wa-liste { display: flex; flex-direction: column; gap: 6px; }
.wa-li {
  display: block; padding: 10px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface);
  text-decoration: none; color: var(--text);
}
.wa-li:hover { border-color: var(--accent-dim); background: var(--raised); }
.wa-li.active { border-color: var(--wa); background: var(--wa-soft); }
/* Ungelesen trägt eine Kante UND die fette Vorschau — nicht nur Farbe. */
.wa-li.ungelesen { box-shadow: inset 3px 0 0 var(--wa); }
.wa-li.ungelesen .wa-li-vorschau { color: var(--text); font-weight: 600; }

.wa-li-kopf { display: flex; align-items: center; gap: 7px; margin-bottom: 3px; }
.wa-li-wer { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wa-li-zahl {
  flex: none; min-width: 18px; text-align: center; padding: 0 5px;
  border-radius: 999px; background: var(--wa); color: #06210f;
  font-size: 11px; font-weight: 700;
}
.wa-li-zeit { margin-left: auto; flex: none; font-size: 11px; color: var(--faint); font-family: var(--font-mono); }
.wa-li-vorschau {
  font-size: 12.5px; color: var(--muted); margin-bottom: 6px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Richtung der letzten Nachricht. Ein Pfeilglyph war beim Überfliegen zu
   leise — ein kurzes Wort liest sich sofort mit. */
.wa-li-von { color: var(--wa); font-weight: 600; }

/* Ein WhatsApp-Profilname ist vom Kunden selbst gesetzt und frei wählbar.
   Die gepunktete Linie plus Tooltip sagt: Anzeige ja, Identität nein. */
.wa-profilname { text-decoration: underline dotted var(--faint); text-underline-offset: 3px; cursor: help; }
.wa-namensquelle {
  flex: none; padding: 1px 7px; border-radius: 999px; font-size: 10.5px;
  border: 1px solid var(--line); background: var(--raised); color: var(--muted);
}
.wa-li-fuss { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wa-fenster.klein { padding: 1px 7px; font-size: 10.5px; gap: 5px; }
.wa-fenster.klein .wa-rest { font-size: 10.5px; }
.wa-zuordnung.fehler { color: var(--danger); border-color: rgba(240,96,107,.45); background: rgba(240,96,107,.10); }

.wa-verlauf-spalte {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
.wa-verlauf-kopf {
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  padding: 12px 15px; border-bottom: 1px solid var(--line-soft);
}
.wa-verlauf-wer { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 14px; }
.wa-verlauf-wer .mono { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }
.wa-verlauf-tasten { margin-left: auto; }
.wa-zurueck { display: none; color: var(--muted); text-decoration: none; font-size: 13px; }

.wa-tickets {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 15px; border-bottom: 1px solid var(--line-soft);
  font-size: 12px; color: var(--muted);
}
.wa-ticket-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px; border-radius: 999px; text-decoration: none;
  border: 1px solid var(--line); background: var(--raised);
  color: var(--text); font-family: var(--font-mono); font-size: 11.5px;
}
.wa-ticket-chip:hover { border-color: var(--accent); }
.wa-ticket-chip span { color: var(--muted); font-family: var(--font-sans, inherit); }

/* Ticketbezug: „läuft aktuell in" muss sich deutlich von „auch verknüpft mit"
   unterscheiden — das eine bestimmt, wo neue Nachrichten landen, das andere
   ist Historie. Der Zustand „keins" wird ausgeschrieben, nicht weggelassen. */
.wa-tickets { flex-direction: column; align-items: stretch; gap: 7px; }
.wa-tickets-zeile { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.wa-bezug {
  flex: none; padding: 2px 9px; border-radius: 999px; font-size: 11px;
  text-transform: uppercase; letter-spacing: .3px;
  border: 1px solid var(--line); background: var(--raised); color: var(--muted);
}
.wa-bezug.laeuft { border-color: var(--wa); color: var(--wa); background: var(--wa-soft); font-weight: 600; }
.wa-bezug.kein { border-color: var(--accent-dim); color: var(--accent); background: var(--accent-soft); }
.wa-bezug-text { font-size: 11.5px; color: var(--muted); }
.wa-ticket-chip.aktiv { border-color: var(--wa); }
.wa-ticket-paar { display: inline-flex; align-items: center; gap: 5px; }
.wa-ticket-paar .btn { margin: 0; }

.wa-timeline { padding: 14px 15px; max-height: 58vh; overflow-y: auto; }
/* .timeline ist ein Spalten-Flexcontainer. Sobald er eine begrenzte Höhe
   bekommt (hier max-height), schrumpfen die Kinder per Vorgabe
   (flex-shrink: 1) — und weil .msg overflow:hidden hat, wird der Text
   vertikal abgeschnitten: Kopfzeile sichtbar, Inhalt weg. Gemessen waren es
   36 px sichtbar bei 93 px nötig. Kinder dürfen hier nicht schrumpfen,
   gescrollt wird der Container. */
.wa-timeline > * { flex: none; }
.wa-verlauf-spalte .compose.whatsapp { border: none; border-top: 1px solid var(--line); border-radius: 0; border-left: 3px solid var(--wa); }
.empty.klein { padding: 22px 14px; font-size: 12.5px; color: var(--faint); text-align: center; }

/* Handy: entweder Liste oder Verlauf, mit Zurück-Weg. Beides nebeneinander
   wäre auf 390 px unbedienbar. */
@media (max-width: 920px) {
  .wa-split { grid-template-columns: 1fr; }
  .wa-split[data-offen="1"] .wa-liste-spalte { display: none; }
  .wa-split[data-offen="0"] .wa-verlauf-spalte { display: none; }
  .wa-zurueck { display: inline; }
  .wa-timeline { max-height: none; }
}

/* ---------- Auswahlmodus: Nachrichten zu einem Ticket bündeln ---------- */
.wa-pick { display: none; flex: none; accent-color: var(--wa); width: 15px; height: 15px; cursor: pointer; }
.timeline.auswahl .wa-pick { display: inline-block; }
.timeline.auswahl .msg { cursor: default; }
.timeline.auswahl .msg:has(.wa-pick:checked) { border-color: var(--wa); background: var(--wa-soft); }

.wa-auswahl-leiste {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 9px 15px; border-bottom: 1px solid var(--line-soft);
  background: var(--wa-soft); font-size: 12.5px;
}
.wa-auswahl-leiste .btn { margin: 0; }

/* Verknüpfung an der Nachricht — im Gesprächsverlauf, nicht im Ticket. */
.wa-gehoert {
  font-size: 10.5px; padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--raised);
  color: var(--muted); font-family: var(--font-mono);
}

/* Dialog */
.wa-dlg-info { font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.wa-dlg-hint { font-size: 11.5px; color: var(--faint); margin-top: 4px; }
.wa-dlg-warnung {
  padding: 10px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--accent-dim); background: var(--accent-soft);
  font-size: 12.5px; line-height: 1.55;
}
.wa-dlg-warnung.laut { border-color: var(--danger); background: rgba(240,96,107,.12); }
.wa-dlg-check { display: flex; align-items: center; gap: 7px; margin-top: 8px; font-size: 12.5px; }
.wa-dlg-check input { accent-color: var(--accent); }

/* ---------- Dashboard auf schmalen Schirmen ----------
   Abgegrenzter Zusatz aus Runde 6d, gleiche Ursache und gleiche Lösung wie
   bei .detail-grid: Grid-Kinder schrumpfen wegen min-width:auto nicht unter
   die Inhaltsbreite ihrer breitesten Zeile, die Spalte wurde dadurch breiter
   als der Bildschirm (721 px bei 390 px) und schnitt rechts ab. */
.dash-cols > * { min-width: 0; }

/* ---------- Detailansicht auf schmalen Schirmen ----------
   Grid-Kinder haben min-width:auto und schrumpfen deshalb nicht unter die
   Inhaltsbreite ihrer breitesten Zeile (Nachrichtenkopf, Ereigniszeile). Die
   Spalte wurde dadurch breiter als der Bildschirm und Inhalte rechts
   abgeschnitten. min-width:0 gibt die Spalte frei, danach greifen die
   normalen Umbrüche. */
.detail-grid > * { min-width: 0; }
@media (max-width: 620px) {
  .msg-head { flex-wrap: wrap; row-gap: 4px; }
  .msg-head .when { margin-left: 0; }
  .msg-event { flex-wrap: wrap; row-gap: 2px; }
  .msg-event .when { margin-left: 0; }
}
.compose-head {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px; border-bottom: 1px solid var(--line-soft);
}
.compose-title { font-size: 13px; font-weight: 600; color: var(--text); }
.compose-to { font-size: 11.5px; color: var(--faint); }
.compose.mail .compose-to { color: var(--accent); }
.compose-to.intern { color: #c9a227; }
.compose-head .tpl-select { margin-left: auto; }
.compose textarea {
  width: 100%; min-height: 96px; background: transparent; border: none;
  color: var(--text); font-family: var(--font-body); font-size: 13.5px;
  line-height: 1.6; padding: 13px 15px; resize: vertical; outline: none;
}
.compose.note textarea { min-height: 64px; }
/* Das Feld ist randlos und füllt die Karte bis an die Kante. Der globale
   Fokus-Ring (Z. ~1230) zeichnet deshalb einen 3px-Rahmen ohne Bezug zu einer
   Kante, den `overflow: hidden` der Karte zusätzlich abschneidet — sichtbar
   als bernsteinfarbener Kasten mitten im Composer. Derselbe Fehler war beim
   To-do-Composer schon abzuschalten (siehe .td-composer).
   Den Ring an die Karte zu hängen war ebenfalls falsch: dann liegt derselbe
   Streifen eben an der Oberkante. Der Fokus kommt hier ohne Farbe — das Feld
   hebt sich eine Spur ab, mehr braucht es bei einem Textfeld dieser Größe
   nicht. Die Aufhellung ist durchscheinend, damit der gelbliche Grund der
   internen Notiz erhalten bleibt; die Kanalfarbe der linken Kante (grün
   WhatsApp, gold Notiz, Akzent Mail) bleibt in jedem Fall unangetastet. */
.compose textarea:focus { box-shadow: none; background: rgba(255,255,255,.025); }
.reply-foot { display: flex; align-items: center; gap: 9px; padding: 10px 14px; border-top: 1px solid var(--line-soft); }
.reply-foot .hint { font-size: 11.5px; color: var(--faint); }
.reply-foot .btn { margin-left: auto; }

/* ---------- Detail-Sidebar ---------- */
.side-panel { display: flex; flex-direction: column; gap: 14px; }

.sp {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
}
/* Einklappbare Sidebar-Box (z. B. Zeiterfassung im Ticket) */
details.sp-fold summary { display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none; }
details.sp-fold summary::-webkit-details-marker { display: none; }
details.sp-fold summary h3 { margin: 0; }
details.sp-fold .sp-fold-val { margin-left: auto; font-family: var(--font-mono); font-size: 12px; color: var(--muted); }
details.sp-fold summary::after { content: '▸'; color: var(--faint); font-size: 11px; }
details.sp-fold[open] summary::after { content: '▾'; }
details.sp-fold[open] summary { margin-bottom: 10px; }
.sp h3 {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 12px;
}
.sp .kv { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; padding: 5px 0; }
.sp .kv .k { color: var(--faint); }
.sp .kv .v { color: var(--text); font-weight: 500; text-align: right; }
.sp .kv .v.mono { font-family: var(--font-mono); font-size: 11.5px; }
/* Eine Auswahl in DETAILS (die Kategorie, 124.6 c) — wie die im Kopf, kleiner. */
.sp .kv.kv-wahl { align-items: center; }
.sp .kv select {
  background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); font: inherit; font-size: 12.5px; font-weight: 500;
  padding: 3px 6px; cursor: pointer; max-width: 100%;
}

.odoo-link {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px;
  padding: 8px 10px;
  background: var(--raised);
  border-radius: var(--radius-sm);
  margin-bottom: 9px;
}
.odoo-link .st { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.odoo-link .st.on { background: var(--ok); }
.odoo-link .st.off { background: var(--faint); }

.time-list { display: flex; flex-direction: column; gap: 1px; margin-bottom: 11px; }
.time-row { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 12.5px; border-bottom: 1px solid var(--line-soft); }
.time-row:last-child { border-bottom: 0; }
.time-row .mins { font-family: var(--font-mono); font-size: 12px; color: var(--accent); flex: none; width: 52px; }
.time-row .note { color: var(--muted); flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.time-total {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 12.5px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
  color: var(--text);
}
.time-add { display: flex; gap: 7px; margin-top: 11px; }
.time-add input {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 12.5px;
  padding: 6px 9px;
  outline: none;
}
.time-add input.mins { width: 62px; font-family: var(--font-mono); }
.time-add input.note { flex: 1; }

/* ============ Kunden ============ */
.cust-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 13px; }
.cust-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 17px;
}
.cust-card .name { font-family: var(--font-display); font-size: 14.5px; font-weight: 600; margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cust-card .mail { font-size: 12px; color: var(--muted); font-family: var(--font-mono); margin-bottom: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cust-card .stats { display: flex; gap: 14px; font-size: 12px; color: var(--faint); }
.cust-card .stats b { color: var(--text); font-family: var(--font-mono); font-weight: 500; }

/* ============ Modal ============ */
/* ---------- Ein Modal ist hoechstens so hoch wie der Bildschirm (18.08.2026) ----------
   Gemessen am gerechneten Kasten, nicht am Stilblatt gelesen: der Stopp-Dialog
   eines Timers mit acht Abschnitten und viel Text war 1554 px hoch in einem
   900 px hohen Fenster (Handy: 6782 bei 844). „Speichern" lag bei top 1587 —
   687 px unter der Kante, auf dem Handy 5966 px. 21 bzw. 33 bedienbare Elemente
   waren unerreichbar, und der eingetippte Text liess sich nicht sichern.

   Warum NICHTS scrollen konnte, und das ist der eigentliche Befund:
     .modal-bg   position: fixed, Hoehe = Fenster, overflow-y: visible
                 -> laeuft ueber, kann aber nicht scrollen
     .modal      max-height: none, overflow: visible  -> waechst unbegrenzt
     .modal-body max-height: none, overflow: visible  -> waechst unbegrenzt
     document    scrollHeight == clientHeight         -> die Seite auch nicht,
                 weil ein fixed Element nichts zur Dokumenthoehe beitraegt
   Es war also kein Flex-Kind ohne `min-height: 0` — es gab ueberhaupt keinen
   Ort, an dem gescrollt werden konnte.

   Und es betraf nicht den Timer, sondern ALLE 21 Modale: `max-height` und
   `overflow` standen nirgends. Der Timer-Dialog ist nur der, der mit den Daten
   waechst. Deshalb steht die Regel hier und nicht dort.

   Gebaut wie `.rk-preview-bg` weiter unten, das dieses Muster schon hatte:
   Kopf und Fuss stehen fest, der Rumpf scrollt. So bleibt „Speichern" immer
   sichtbar — statt am Ende eines langen Weges zu liegen.

   `overflow-y: auto` am Hintergrund ist die Rettungsleine fuer den Fall, dass
   ein Kind die Deckelung doch ueberschreitet (feste Hoehe, `position: absolute`
   im Rumpf). Dann ist es unschoen, aber erreichbar. */
.modal-bg {
  position: fixed; inset: 0;
  background: rgba(5,8,12,.7);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 9vh 12px 24px;
  overflow-y: auto;
  z-index: 50;
  backdrop-filter: blur(2px);
}
.modal {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  width: 540px;
  max-width: 92vw;
  box-shadow: 0 24px 60px rgba(0,0,0,.5);
  display: flex;
  flex-direction: column;
  /* `dvh` zuerst NICHT: aeltere Browser wuerden die ganze Deklaration
     verwerfen. `vh` als Grundlage, `dvh` gewinnt, wo es verstanden wird —
     auf dem Handy ist das der Unterschied zwischen „passt" und „die
     Adressleiste frisst den Fuss". */
  max-height: calc(100vh - 9vh - 24px);
  max-height: calc(100dvh - 9vh - 24px);
}
/* Kopf und Fuss geben nicht nach, der Rumpf schon. `min-height: 0` ist der
   Teil, ohne den ein Flex-Kind nicht unter seine Inhaltshoehe schrumpft — und
   dann liefe `overflow` daneben ins Leere. */
.modal > h2 { flex: none; }
.modal > .modal-foot { flex: none; }
.modal h2 {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  padding: 17px 20px;
  border-bottom: 1px solid var(--line);
}
.modal .modal-body { padding: 17px 20px; display: flex; flex-direction: column; gap: 13px;
  overflow-y: auto; min-height: 0; }
.modal label { font-size: 12px; font-weight: 600; color: var(--muted); display: block; margin-bottom: 5px; }
/* hidden-Attribut muss auch bei Elementen mit eigenem display:flex greifen. */
[hidden] { display: none !important; }

.modal input, .modal select, .modal textarea {
  width: 100%;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  padding: 8px 11px;
  outline: none;
}
/* Saubere, eigene Checkboxen — exakt mittig zum Text, dunkeltauglich.
   !important, damit keine generische .modal-input-Regel sie wieder aufbläht. */
.switch-line { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--muted); line-height: 1; cursor: pointer; }
input.chk,
.switch-line input[type="checkbox"],
.time-bill-line input[type="checkbox"] {
  appearance: none !important; -webkit-appearance: none !important; -moz-appearance: none !important;
  flex: none !important; width: 18px !important; height: 18px !important; margin: 0 !important; padding: 0 !important;
  border: 1.5px solid var(--faint) !important; border-radius: 5px !important; background: var(--raised) !important;
  display: inline-block; position: relative; cursor: pointer; vertical-align: middle;
  transition: background .12s, border-color .12s; box-shadow: none !important;
}
input.chk:hover,
.switch-line input[type="checkbox"]:hover,
.time-bill-line input[type="checkbox"]:hover { border-color: var(--accent) !important; }
input.chk:checked,
.switch-line input[type="checkbox"]:checked,
.time-bill-line input[type="checkbox"]:checked { background: var(--accent) !important; border-color: var(--accent) !important; }
input.chk:checked::after,
.switch-line input[type="checkbox"]:checked::after,
.time-bill-line input[type="checkbox"]:checked::after {
  content: ''; position: absolute; left: 6px; top: 2px; width: 4px; height: 9px;
  border: solid #1a1206; border-width: 0 2.5px 2.5px 0; transform: rotate(45deg);
}
.modal input:focus, .modal select:focus, .modal textarea:focus { border-color: var(--accent-dim); }
.modal textarea { min-height: 90px; resize: vertical; }
.modal .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.modal .modal-foot { display: flex; justify-content: flex-end; gap: 9px; padding: 13px 20px; border-top: 1px solid var(--line); }
.modal .modal-foot .foot-spacer { flex: 1; }

/* ============ Dokumentenablage, Stufe 1 (19.08.2026) ============ */
/* Der Knopf sitzt IN der Kundenkarte, unterhalb der Zahlen — er ist der Zugang
   zur Ablage an dem Ort, an dem die Frage entsteht, was zu einem Kunden
   vorliegt. Eine eigene Seite waere die vollstaendigere Ansicht, aber eine
   Liste, die man aufsuchen muss, wird nicht gelesen. */
.cust-dok { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-soft); }
.cust-dok-btn {
  width: 100%; padding: 6px 10px; cursor: pointer;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  font-family: var(--font-body); font-size: 12px;
  transition: background .12s, color .12s, border-color .12s;
}
.cust-dok-btn:hover { background: var(--hover); color: var(--text); border-color: var(--accent-dim); }

.ab-kopf { display: flex; align-items: center; gap: 10px; }
.ab-zahl { color: var(--muted); font-size: 12.5px; }
.ab-kopf .btn { margin-left: auto; }
/* Die Liste scrollt IM Rumpf, und der Rumpf ist seit dem 18.08. der Scrollort
   des Dialogs — hier also keine zweite Hoehenbegrenzung, sonst gaebe es zwei
   Bildlaufleisten ineinander. */
.ab-liste { display: flex; flex-direction: column; gap: 8px; }
.ab-zeile {
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
  background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.ab-haupt { flex: 1; min-width: 0; }
.ab-titel { font-weight: 600; font-size: 13.5px; overflow-wrap: anywhere; }
.ab-sub {
  display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 3px;
  color: var(--faint); font-size: 11.5px;
}
.ab-art { color: var(--accent); }
.ab-datei { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.ab-tags { margin-top: 4px; color: var(--muted); font-size: 11.5px; }
/* „Kunde entfernt" und „nie zugeordnet" sehen NICHT gleich aus — sonst waere
   ein geloeschter Kunde von einem nie gesetzten ununterscheidbar. */
.ab-weg { color: var(--danger); }
.ab-ohne { color: var(--faint); font-style: italic; }
.ab-tasten { display: flex; gap: 6px; flex: none; }
.ab-leer { padding: 18px; text-align: center; color: var(--faint); font-size: 13px; }

@media (max-width: 700px) {
  .ab-zeile { flex-direction: column; }
  .ab-tasten { width: 100%; }
  .ab-tasten .btn { flex: 1; text-align: center; }
  .ab-datei { max-width: 100%; }
}

/* ============ Toast ============ */
.toast {
  position: fixed;
  bottom: 22px; right: 22px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 11px 17px;
  font-size: 13px;
  /* ---------- Ueber ALLEM (49.5.4, 04.09.2026) ----------
     Hier stand 80. Gemessen am 03.09.2026 mit elementFromPoint: die Meldung
     lag hinter der Rechnungsvorschau (300), und der Riegel aus 49.3.2 —
     „faellt die Messung aus, wird es GESAGT" — war damit gebaut und
     unsichtbar. Christian sah keine Meldung, weil keine zu sehen war.

     Die Vollzaehlung an dem Tag: der Toast lag an achter Stelle von oben,
     sechs Ueberlagerungen vor ihm (200 .rk-modal-bg, 250 .bell-tray,
     300 .rk-preview-bg, 400 .ac-menu, 420 .fl-pop/.rk-menu, 500 .lkasten,
     900 #aufsteiger-root).

     1000 und nicht 301: eine Meldung, die vor der Vorschau und hinter dem
     naechsten Kasten liegt, ist derselbe Fehler an neuer Stelle. Sie gehoert
     ueber ALLES, sonst ist sie eine Meldung mit Ausnahmen.
     Wer hier etwas Hoeheres einfuehrt, macht die Meldung wieder unsichtbar —
     backend/tests/test-toast-sichtbar.js zaehlt nach. */
  z-index: 1000;
  animation: toast-in .2s ease;
  box-shadow: 0 8px 28px rgba(0,0,0,.4);
}
.toast.err { border-left-color: var(--danger); }

/* Eine Meldung, die einen Weg zurueck anbietet, darf nicht nach 3,2 s weg sein —
   sonst ist sie ein Aufblitzen und kein Hinweis. Sie bleibt, bis der Nutzer sie
   schliesst, und macht rechts Platz fuer den Schliessknopf. */
.toast.bleibt { padding-right: 34px; max-width: min(460px, calc(100vw - 44px)); }
.toast-link { margin-left: 10px; color: var(--accent); font-weight: 600; text-decoration: none; white-space: nowrap; }
.toast-link:hover { text-decoration: underline; }
.toast-zu {
  position: absolute; top: 6px; right: 8px;
  background: none; border: 0; padding: 2px 4px; line-height: 1;
  color: var(--muted); font-size: 13px; cursor: pointer;
}
.toast-zu:hover { color: var(--text); }

/* Aufsteiger: eingehende Benachrichtigung. OBEN RECHTS, unter der Glocke — die
   Toasts unten rechts sind die Rueckmeldung auf eigenes Tun, das hier kommt von
   aussen. Zwei Bedeutungen, zwei Orte; im selben Stapel waere „Gespeichert" nicht
   von „Demyan hat geschrieben" zu unterscheiden. */
#aufsteiger-root { position: fixed; top: 56px; right: 14px; z-index: 900;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
  pointer-events: none; }
.aufsteiger { pointer-events: auto; position: relative; display: block; width: 300px;
  background: var(--raised); color: var(--text); text-decoration: none;
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 7px; padding: 9px 30px 9px 11px; font-size: 12.5px; line-height: 1.45;
  box-shadow: var(--shadow-lg); animation: aufsteigen .22s ease-out; }
.aufsteiger b { display: block; font-weight: 600; }
.aufsteiger span { display: block; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.aufsteiger:hover { border-left-color: var(--ok); }
.aufsteiger.geht { animation: absteigen .24s ease-in forwards; }
.aufsteiger-zu { position: absolute; top: 4px; right: 4px; background: none; border: none;
  color: var(--faint); cursor: pointer; font-size: 15px; line-height: 1; padding: 2px 5px;
  border-radius: 4px; }
.aufsteiger-zu:hover { color: var(--text); background: var(--bg); }
/* Die Anzahl im gebuendelten Eintrag. Gedaempft: sie ist ein Umfangshinweis, kein
   Inhalt — „Bestand niedrig: Artikel X" allein waere bei sieben Artikeln eine
   Falschaussage ueber den Umfang. */
.aufsteiger-zahl, .bell-zahl { display: block; font-size: 11px; color: var(--faint); font-style: italic; }
.aufsteiger-mehr { border-left-color: var(--muted); color: var(--muted);
  font-size: 12px; padding-right: 11px; text-align: right; }
@keyframes aufsteigen { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@keyframes absteigen { to { opacity: 0; transform: translateY(-6px); } }
@keyframes toast-in { from { transform: translateY(8px); opacity: 0; } }

/* ============ Leerzustand ============ */
.empty {
  text-align: center;
  padding: 48px 20px;
  color: var(--faint);
  font-size: 13px;
}
.empty .big { font-family: var(--font-display); font-size: 15px; color: var(--muted); margin-bottom: 6px; font-weight: 600; }

/* ============ Demo-Banner ============ */
.demo-banner {
  background: var(--accent-soft);
  border: 1px solid rgba(224,154,74,.3);
  color: var(--accent);
  font-size: 12.5px;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  gap: 9px;
}

/* ============ Responsive ============ */
@media (max-width: 1100px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .dash-cols { grid-template-columns: 1fr; }
  .detail-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ============ SLA / Fälligkeit ============ */
.due {
  font-size: 11px; font-weight: 600;
  padding: 2px 7px; border-radius: 999px;
  color: var(--muted); background: var(--raised);
  white-space: nowrap;
}
.due.soon { color: var(--accent); background: var(--accent-soft); }
.due.over { color: var(--danger); background: rgba(232,96,107,.13); }
.t-row.overdue { box-shadow: inset 2px 0 0 var(--danger); }
.card-due { margin: 2px 0 8px; }
.overdue-text { color: var(--danger) !important; }

/* ============ Timeline: System-Events ============ */
.msg-event {
  display: flex; align-items: center; gap: 9px;
  /* Links 5px statt 8px: die Artkante (3px) nimmt den Rest, damit die Zeile
     genau so weit einrueckt wie vorher und nicht gegen die Nachrichten
     verspringt. */
  padding: 5px 4px 5px 5px; margin: 2px 0;
  font-size: 12px; color: var(--muted);
}
.msg-event .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--faint); flex: none;
}
.msg-event .txt { font-weight: 500; }
.msg-event .when { margin-left: auto; color: var(--faint); font-family: var(--font-mono); font-size: 11px; }

/* ---- Die Einfuegespur: eine Zeile sichtbar, der Rest aufklappbar (23.08.2026) ----
   Sie war die laengste Zeile der ganzen Liste und stand fuenfmal in MFI-1007 —
   ausgerechnet die Spur des Zusammenfuehrens machte die zusammengefuehrte
   Ansicht unlesbar. Immer eingeklappt, unabhaengig von der Klappschwelle: eine
   einzelne Spur ist genauso unleserlich wie fuenf.

   Der volle Wortlaut steht im DOM und in der Datenbank. Eingeklappt ist nicht
   geloescht — derselbe Riegel wie beim Zustandsklapper. */
.msg-event-spur > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
}
.msg-event-spur > summary::-webkit-details-marker { display: none; }
/* Ein eigenes Zeichen statt des Vorgabedreiecks: das steht sonst VOR dem Punkt
   und verschiebt die ganze Zeile gegen die anderen Ereignisse. */
.msg-event-spur > summary::after {
  content: '›'; margin-left: 6px; color: var(--faint); transition: transform .12s;
}
.msg-event-spur[open] > summary::after { transform: rotate(90deg); display: inline-block; }
.msg-event-spur > summary:hover { color: var(--text); }
.msg-event-voll {
  margin: 6px 0 0 20px; color: var(--muted); font-size: 12px; line-height: 1.5;
}

/* Notiz löschen + lange Nachrichten einklappen */
.msg-del { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 12px; padding: 2px 6px; border-radius: 5px; }
.msg-del:hover { color: var(--p-kritisch); background: var(--bg); }
.msg-bwrap { position: relative; }
.msg-bwrap.clamped .msg-body { max-height: 190px; overflow: hidden; -webkit-mask-image: linear-gradient(#000 65%, transparent); mask-image: linear-gradient(#000 65%, transparent); }
.msg-more { display: block; background: none; border: none; color: var(--accent); cursor: pointer; font: inherit; font-size: 12px; padding: 4px 12px 8px; }
.msg-more:hover { text-decoration: underline; }

/* Eingeklappte Event-Gruppen (Status/Zuweisungs-Historie) */
.msg-events { margin: 2px 0; }
.msg-events summary { display: flex; align-items: center; gap: 9px; padding: 5px 4px 5px 8px; font-size: 12px; color: var(--muted); cursor: pointer; list-style: none; border-radius: 7px; }
.msg-events summary::-webkit-details-marker { display: none; }
.msg-events summary:hover { background: var(--surface); color: var(--text); }
.msg-events summary .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--faint); flex: none; }
.msg-events summary .when { margin-left: auto; color: var(--faint); font-family: var(--font-mono); font-size: 11px; }
.msg-events[open] summary { color: var(--text); }
.msg-events .msg-event { padding-left: 22px; }

/* Eingeklappte ältere WhatsApp-Nachrichten im Ticket. Gleiches Muster wie die
   Ereignisgruppe, nur in Kanalfarbe — es ist Gesprächsinhalt, keine Historie
   von Feldänderungen. */
.msg-wa-alt { margin: 2px 0; }
.msg-wa-alt summary {
  display: flex; align-items: center; gap: 9px; padding: 7px 10px;
  font-size: 12px; color: var(--muted); cursor: pointer; list-style: none;
  border: 1px dashed var(--line); border-radius: var(--radius-sm); background: var(--surface);
}
.msg-wa-alt summary::-webkit-details-marker { display: none; }
.msg-wa-alt summary:hover { border-color: var(--wa); color: var(--text); }
.msg-wa-alt summary .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--wa); flex: none; }
.msg-wa-alt summary .when { margin-left: auto; color: var(--faint); font-family: var(--font-mono); font-size: 11px; }
.msg-wa-alt[open] summary { margin-bottom: 9px; color: var(--text); }
/* Der Inhalt ist eine normale Nachrichtenfolge — gleicher Abstand wie außen. */
.msg-wa-alt[open] > .msg { margin-bottom: 9px; }

/* ---------- Kommunikation ----------
   Der Kanal steht als Wort da, nicht als Farbe allein: „WA" und „Mail" sind
   auf einen Blick unterscheidbar, ein grüner und ein blauer Punkt nicht. */
.komm-kanal {
  flex: none; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; padding: 1px 6px; border-radius: 4px;
  border: 1px solid var(--line); color: var(--muted); background: var(--raised);
}
.komm-kanal.whatsapp { color: #4EC58C; border-color: rgba(78,197,140,.4); background: rgba(78,197,140,.10); }
.komm-kanal.mail     { color: var(--s-neu); border-color: rgba(91,168,234,.4); background: rgba(91,168,234,.10); }

/* Kein Antwortfeld, wo nicht gesendet werden kann. Die Auskunft steht an der
   Stelle, an der sonst das Feld stünde — nicht als Fußnote irgendwo. */
.komm-kein-versand {
  margin: 12px 14px 16px; padding: 12px 14px; font-size: 12.5px; line-height: 1.55;
  border: 1px dashed var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--muted);
}
.komm-kein-versand b { color: var(--text); }

/* Verweis vom Ticket in das Gespräch, aus dem die Nachricht stammt. */
.komm-verweis {
  font-size: 10.5px; text-decoration: none; color: var(--accent);
  border: 1px solid var(--accent-dim); border-radius: 4px; padding: 0 5px;
}
.komm-verweis:hover { background: var(--accent-soft); }

/* ---------- Verträge ----------
   Der Status trägt die Aussage im Klartext, nicht nur als Farbe: „Final" heißt
   unveränderlich, und das darf man nicht erraten müssen. */
.vt-status {
  display: inline-block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .3px;
  padding: 1px 8px; border-radius: 999px; border: 1px solid var(--line);
  color: var(--muted); background: var(--raised);
}
.vt-status.entwurf { color: var(--muted); border-style: dashed; }
.vt-status.final { color: var(--s-neu); border-color: rgba(91,168,234,.45); }
.vt-status.versendet { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); }
.vt-status.unterzeichnet { color: var(--ok); border-color: rgba(78,197,140,.5); font-weight: 600; }
.vt-status.abgelehnt, .vt-status.abgelaufen { color: var(--danger); border-color: rgba(240,96,107,.45); }
.vt-status.ersetzt { color: var(--faint); }

/* Tickets zusammenfuehren: die Richtung muss man SEHEN, nicht lesen muessen. */
.tm-richtung {
  display: flex; flex-direction: column; gap: 4px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 11px 13px; font-size: 12.5px; background: var(--surface);
}
.tm-weg { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--danger); }
.tm-pfeil { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .06em; }
.tm-bleibt { color: var(--text); }
/* ---- Die Nummer fuer geschlossene Tickets (23.08.2026) ----
   Das Auswahlfeld zeigt nur Laufende: auf Prod sind neun von elf Tickets
   geschlossen, und bei hundert waere es kein Auswahlfeld mehr. Geschlossene
   bleiben ueber die Nummer erreichbar — den Weg nimmt ohnehin, wer weiss,
   welches Ticket er meint.

   Drei Antworten, drei Toene aus dem vorhandenen Satz. Keine neuen Farben:
   `--danger` fuer „gibt es nicht", `--accent` fuer „angenommen, aber", `--ok`
   fuer „angenommen". */
.tm-nummer { margin-top: 10px; }
.tm-nummer > label { display: block; margin-bottom: 4px; }
.tm-numzeile { display: flex; gap: 6px; align-items: center; }
.tm-numzeile input {
  flex: 1 1 auto; min-width: 0; font-family: var(--font-mono); font-size: 12.5px;
}
.tm-num-antwort { min-height: 17px; margin-top: 5px; font-size: 12px; color: var(--muted); }
.tm-num-antwort.tm-nein { color: var(--danger); }
.tm-num-antwort.tm-warn { color: var(--accent); }
.tm-num-antwort.tm-ja   { color: var(--ok); }

.tm-bilanz { font-size: 12.5px; color: var(--muted); }

/* Vertragsverweis am Ticket — bei einer SLA-Frage die erste Frage. */
.tk-vertraege { display: inline-flex; align-items: center; gap: 5px; }
.tk-vertrag {
  text-decoration: none; font-family: var(--font-mono); font-size: 11.5px;
  border: 1px solid var(--line); border-radius: 5px; padding: 0 6px; color: var(--muted);
}
.tk-vertrag.unterzeichnet { color: var(--ok); border-color: rgba(78,197,140,.45); }
.tk-vertrag.versendet { color: var(--accent); border-color: var(--accent-dim); }
.tk-vertrag:hover { background: var(--raised); }

/* Eine Vorlage, die nicht einliest, muss stehen bleiben. */
.vt-syncfehler {
  border: 1px solid rgba(240,96,107,.45); background: rgba(240,96,107,.07);
  border-radius: var(--radius-sm); padding: 11px 13px; margin-bottom: 14px; font-size: 12.5px;
}
.vt-syncfehler b { color: var(--danger); }
.vt-syncfehler ul { margin: 6px 0 0; padding-left: 18px; color: var(--muted); }

.vt-zeile { cursor: pointer; }
.vt-tbl td { vertical-align: middle; }
.vt-klein { font-size: 11.5px; color: var(--muted); }
.vt-hint { font-size: 11px; color: var(--faint); }
.vt-suche {
  margin-left: auto; min-width: 200px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 999px; color: var(--text);
  font: inherit; font-size: 12.5px; padding: 5px 12px;
}
.vt-suche:focus { outline: none; border-color: var(--accent-dim); }

/* Detail: links die Angaben, rechts das Dokument. Auf schmalen Schirmen
   untereinander — das Dokument braucht Breite. */
.vd-grid { display: grid; grid-template-columns: minmax(0, 360px) minmax(0, 1fr); gap: 16px; align-items: start; }
.vd-grid > * { min-width: 0; }
.vd-links, .vd-rechts { display: flex; flex-direction: column; gap: 14px; }
@media (max-width: 1000px) { .vd-grid { grid-template-columns: 1fr; } }

.vd-meta > div, .vd-wert { display: flex; gap: 10px; align-items: baseline; font-size: 12.5px; padding: 4px 0; }
.vd-meta > div > span, .vd-wert > span { color: var(--muted); flex: none; min-width: 120px; }
.vd-meta b, .vd-wert b { color: var(--text); font-weight: 600; }
/* Die Prüfsumme ist 64 Zeichen — sie darf umbrechen statt die Karte zu sprengen. */
.vd-hash { font-size: 10.5px; overflow-wrap: anywhere; color: var(--muted) !important; font-weight: 400 !important; }
.vd-status-tasten { display: flex; gap: 8px; flex-wrap: wrap; }

.vd-inhalt { padding: 0; }
.vd-md {
  margin: 0; padding: 16px 18px; max-height: 70vh; overflow: auto;
  font-family: var(--font-body); font-size: 12.5px; line-height: 1.65;
  color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere;
}
.vd-pdf { width: 100%; height: 76vh; border: none; display: block; background: #fff; }

/* Gesetzte Vorschau: weißes Blatt, wie das PDF. Bewusst hell und mit Serifen —
   das Dokument geht an einen Kunden und sieht dort nicht aus wie die Suite. */
.vd-blatt {
  background: #fff; color: #1b1f27; margin: 14px; padding: 16mm 18mm;
  max-height: 70vh; overflow: auto; border-radius: 4px;
  box-shadow: 0 2px 14px rgba(0,0,0,.35);
  font-family: Georgia, 'Times New Roman', serif; font-size: 13px; line-height: 1.62;
}
.vd-blatt h1 { font-size: 21px; margin: 0 0 12px; line-height: 1.25; }
.vd-blatt h2 { font-size: 16px; margin: 20px 0 7px; }
.vd-blatt h3 { font-size: 14px; margin: 16px 0 5px; }
.vd-blatt h4 { font-size: 13px; margin: 13px 0 4px; }
.vd-blatt p { margin: 0 0 9px; text-align: justify; hyphens: auto; }
.vd-blatt ul, .vd-blatt ol { margin: 0 0 9px; padding-left: 20px; }
.vd-blatt li { margin-bottom: 3px; }
.vd-blatt strong { font-weight: 700; }
.vd-blatt hr { border: none; border-top: 1px solid #d6dae1; margin: 16px 0; }
.vd-blatt table { border-collapse: collapse; width: 100%; margin: 0 0 10px; font-size: 12px; }
.vd-blatt th, .vd-blatt td { border: 1px solid #d6dae1; padding: 5px 7px; text-align: left; }
.vd-blatt th { background: #f1f3f6; }
.vd-blatt code { font-family: ui-monospace, Consolas, monospace; font-size: 12px; background: #f1f3f6; padding: 0 3px; }
.vd-blatt-fuss {
  margin-top: 18mm; padding-top: 8px; border-top: 1px solid #d6dae1;
  font-family: var(--font-body); font-size: 10.5px; color: #6a7280;
}
.vd-ansicht { margin-left: auto; }
.vd-ansicht .seg-b {
  background: none; border: none; color: var(--muted); font: inherit; font-size: 11.5px;
  padding: 3px 9px; border-radius: 6px; cursor: pointer;
}
.vd-ansicht .seg-b.on { background: var(--accent-soft); color: var(--accent); }
.vd-ansicht .seg-b:hover:not(.on) { color: var(--text); }
.vd-fehlt {
  margin: 12px 14px; padding: 9px 12px; font-size: 12.5px;
  border: 1px solid var(--accent-dim); background: var(--accent-soft);
  border-radius: var(--radius-sm); color: var(--text);
}

/* Felder im Detailpanel: .kf-row stylt kein textarea, also eine eigene Zeile. */
.vd-feld { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.vd-feld label { font-size: 12px; font-weight: 600; color: var(--muted); }
.vd-quelle { font-weight: 400; font-size: 11px; color: var(--faint); margin-left: 6px; }
.vd-feld input, .vd-feld textarea {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); padding: 8px 10px; font: inherit; font-size: 13px; outline: none;
  width: 100%; box-sizing: border-box;
}
.vd-feld textarea { resize: vertical; min-height: 62px; }
.vd-feld input:focus, .vd-feld textarea:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--ring); }

/* Signaturlinks (Stufe 2). Der Zustand steht als Wort da: „Widerrufen" und
   „Abgelaufen" sehen sonst gleich aus wie „Gültig". */
.vd-sig-liste { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.vd-sig-link { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px; }
.vd-sig-an { color: var(--text); overflow-wrap: anywhere; }
.vd-sig-mini { font-size: 11px; color: var(--faint); }
/* Der Widerruf sitzt rechts in der Zeile — er ist die Ausnahme, nicht die
   naechstliegende Handlung. */
.vd-sig-weg { margin-left: auto; }
.vd-sig-aus { font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.vd-sig-fehler { color: var(--danger); }
.vd-sig-fertig {
  border: 1px solid rgba(78,197,140,.35); background: rgba(78,197,140,.07);
  border-radius: var(--radius-sm); padding: 11px 13px; margin-bottom: 12px; font-size: 12.5px;
}
.vd-sig-fertig .vd-wert > span { min-width: 108px; }
.vd-sig-warn {
  border: 1px solid var(--accent-dim); background: var(--accent-soft);
  border-radius: var(--radius-sm); padding: 10px 12px; font-size: 12.5px; line-height: 1.5;
}
#sl-url, #sl-codewert { font-family: var(--font-mono); font-size: 12px; }

.vn-felder { margin-top: 6px; }
.vn-titel { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin: 10px 0 6px; }
.vn-hinweis { font-size: 12px; color: var(--muted); line-height: 1.5; margin-top: 8px; }
/* Der Grund unter dem Empfaengerfeld (88): er erklaert ein LEERES Feld,
   also faellt er auf — sonst liest ihn niemand und traegt blind ein. */
.ds-an-grund { font-size: 12px; color: var(--p-hoch); line-height: 1.45; }
.vn-hinweis b { color: var(--text); }
/* Der Aufgehoben-Hinweis im Versanddialog (110.5): dieselbe Bauart, aber in
   der Farbe der erledigten Vorgaenge — wer sendet, soll es sehen, ohne zu
   suchen. Kein Warnrot: es ist kein Fehler, es zu senden. */
.vn-hinweis.vn-aufgehoben { color: var(--s-arbeit); }
.vn-hinweis.vn-aufgehoben b { color: var(--s-arbeit); }

/* ---------- Markierungen (Phase 3) ----------
   Eine Rückfrage ist eine offene Sache und sticht heraus; eine Info ist ein
   Merker und bleibt ruhig. Die Kante trägt die Aussage, die Marke im Kopf
   nennt sie im Klartext — Farbe allein reicht nicht. */
.msg.markiert-info { box-shadow: inset 3px 0 0 var(--s-neu); }
.msg.markiert-rueckfrage { box-shadow: inset 3px 0 0 var(--accent); border-color: var(--accent-dim); }

.mark-chip {
  flex: none; font-size: 10.5px; text-transform: uppercase; letter-spacing: .3px;
  padding: 1px 7px; border-radius: 999px; border: 1px solid var(--line);
  color: var(--muted); background: var(--raised);
}
.mark-chip.info { color: var(--s-neu); border-color: rgba(91,168,234,.45); }
.mark-chip.rueckfrage { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); font-weight: 600; }
/* Privat: gepunktet, damit man auf einen Blick sieht, dass es niemand sonst
   sieht — dasselbe Mittel wie beim WhatsApp-Profilnamen. */
.mark-chip.privat { border-style: dotted; }

.msg-merken {
  flex: none; background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--faint); cursor: pointer; font: inherit; font-size: 11px;
  padding: 1px 6px; opacity: 0; transition: opacity .12s;
}
.msg:hover .msg-merken, .msg-merken:focus-visible, .msg-merken.hat { opacity: 1; }
.msg-merken:hover { color: var(--accent); border-color: var(--accent-dim); }
.msg-merken.hat { color: var(--accent); }
@media (hover: none) { .msg-merken { opacity: .65; } }

/* Merkfeld: klein, direkt am Knopf. Kein Dialog — Markieren ist ein Handgriff. */
.mark-feld {
  position: absolute; z-index: 300; width: 300px; padding: 10px;
  background: var(--raised); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow-lg); display: flex; flex-direction: column; gap: 8px;
}
.mf-arten { display: flex; gap: 7px; }
.mf-art {
  flex: 1; padding: 7px 8px; border-radius: 8px; cursor: pointer; font: inherit;
  font-size: 12.5px; border: 1px solid var(--line); background: var(--surface); color: var(--text);
}
.mf-art:hover { border-color: var(--accent-dim); }
.mf-art.gesetzt { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.mf-notiz {
  width: 100%; background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; color: var(--text); font: inherit; font-size: 12.5px;
  padding: 7px 9px; resize: vertical;
}
.mf-fuss { display: flex; align-items: center; gap: 9px; }
.mf-scope { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); cursor: pointer; }
.mf-fuss .btn { margin-left: auto; }

/* „Gemerkt"-Ansicht */
.chip-luecke { width: 14px; }
.gm-karten { display: grid; gap: 10px; }
.gm-karte {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 11px 13px; display: flex; flex-direction: column; gap: 7px;
}
.gm-karte.rueckfrage { border-left: 3px solid var(--accent); }
.gm-karte.info { border-left: 3px solid var(--s-neu); }
.gm-kopf { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.gm-kopf .when { margin-left: auto; font-family: var(--font-mono); font-size: 11.5px; color: var(--faint); }
.gm-privat { font-size: 11px; color: var(--muted); }
.gm-wo { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }
.gm-notiz { font-size: 13px; color: var(--text); }
.gm-vorschau { font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.gm-fuss { display: flex; align-items: center; gap: 9px; }
.gm-fuss .gm-von { font-size: 11.5px; color: var(--faint); }
.gm-fuss .btn { margin-left: 0; }
.gm-fuss .btn:first-of-type { margin-left: auto; }

/* Zeitleiste: was der Benutzerfilter verbirgt. Bewusst auffaellig — eine
   Leistung, die es gibt und die man nicht sieht, ist schlimmer als eine Zeile
   mehr auf dem Schirm. */
.tl-verborgen {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 14px; padding: 9px 12px; font-size: 12.5px;
  border: 1px solid var(--accent-dim); background: var(--accent-soft);
  border-radius: var(--radius-sm); color: var(--text);
}
.tl-verborgen .btn { margin-left: auto; }

/* Sicherheitsnetz gegen waagerechten Ueberlauf. Ein einzelnes Raster, dessen
   Kinder nicht schrumpfen koennen, zieht sonst die ganze Seite breiter als das
   Fenster — und alles darunter sieht randlos aus, obwohl es richtig gebaut ist.
   Das ist hier schon dreimal passiert (.detail-grid, .dash-cols, .wp-grid). */
html, body { overflow-x: hidden; }

/* ---------- Bericht: Aufriss je Kunde ----------
   Eine Berichtszeile zeigt auf Klick ihre einzelnen Leistungen — im selben
   Zeitraum wie die Zeile darüber. */
tr.rep-kunde { cursor: pointer; }
tr.rep-kunde:hover > td { background: var(--surface); }
tr.rep-kunde.offen > td { background: var(--accent-soft); }
tr.rep-aufriss > td { padding: 0 0 12px; background: var(--surface); }
.ra-lade { padding: 12px 14px; color: var(--muted); font-size: 12.5px; }
.ra-kopf {
  padding: 9px 14px; font-size: 12.5px; color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}
.ra-kopf b { color: var(--text); }
.ra-tab { width: 100%; border-collapse: collapse; }
.ra-tab td { padding: 6px 10px; font-size: 12.5px; border-bottom: 1px solid var(--line-soft); }
.ra-tab tr:last-child td { border-bottom: none; }
.ra-datum { color: var(--faint); white-space: nowrap; width: 1%; }
.ra-art { color: var(--muted); white-space: nowrap; width: 1%; }
.ra-notiz { color: var(--text); }
.ra-ticket { white-space: nowrap; width: 1%; }
.ra-ticket a { color: var(--accent); text-decoration: none; }
.ra-ticket a:hover { text-decoration: underline; }
.ra-dauer { text-align: right; white-space: nowrap; width: 1%; color: var(--text); }
.ra-bill { text-align: center; width: 1%; }
.ra-bill.yes { color: var(--ok); font-weight: 600; }
.ra-bill.no { color: var(--faint); }

/* Auf schmalen Schirmen weicht die Leistungsart — Datum, Text und Dauer
   tragen die Aussage. */
@media (max-width: 700px) {
  .ra-art { display: none; }
}

/* Kopfzeile der Zeitleiste: Anzahl und Leserichtung. */
.tl-kopf {
  display: flex; align-items: center; gap: 10px; margin: 0 0 8px;
}
.tl-kopf .tl-zahl { font-size: 12px; color: var(--muted); }
.tl-kopf .btn { margin-left: auto; }

/* ---------- Adresse einer Nachricht (Markierungen, Phase 1) ----------
   Die angesprungene Nachricht wird kurz hervorgehoben. Bewusst nicht dauerhaft
   gefaerbt: die Hervorhebung ist ein Hinweis („hier bist du gelandet"), kein
   Zustand der Nachricht. */
.msg.msg-ziel {
  animation: msgZiel 2.4s ease-out;
  border-color: var(--accent-dim);
}
@keyframes msgZiel {
  0%   { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
  70%  { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
  100% { background: transparent; box-shadow: none; }
}
/* Wer die Bewegungsreduzierung eingeschaltet hat, bekommt keinen Verlauf —
   aber trotzdem eine sichtbare Kante, sonst weiss er nicht, wo er ist. */
@media (prefers-reduced-motion: reduce) {
  .msg.msg-ziel { animation: none; background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
}

/* Kopiertaste. Zurueckhaltend: erscheint beim Ueberfahren der Nachricht, damit
   die Zeitleiste nicht mit Knoepfen zugesetzt ist. */
.msg-link {
  flex: none; background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--faint); cursor: pointer; font: inherit; font-size: 11px;
  padding: 1px 6px; opacity: 0; transition: opacity .12s;
}
.msg:hover .msg-link, .msg-link:focus-visible { opacity: 1; }
.msg-link:hover { color: var(--accent); border-color: var(--accent-dim); }
/* Auf Sensorbildschirmen gibt es kein Ueberfahren. */
@media (hover: none) {
  .msg-link { opacity: .65; }
}

/* Zugeklappte Mail im Ticket. Anders als bei WhatsApp EINZELN, nicht als
   Gruppe: man schlaegt eine bestimmte Mail auf, nicht „alle frueheren". Die
   Zeile zeigt Richtung, Absender, Textanfang und Datum — genug, um zu
   entscheiden, ob man sie braucht. */
.msg-mail-zu { margin: 3px 0; }
/* Mehrere zugeklappte Mails stehen untereinander. Gestrichelte Rahmen ergaben
   dabei eine Leiter aus Strichen — deshalb durchgezogen und ruhig, die Trennung
   macht der Abstand. */
.msg-mail-zu summary {
  display: flex; align-items: center; gap: 9px; padding: 8px 11px;
  font-size: 12.5px; color: var(--muted); cursor: pointer; list-style: none;
  border: 1px solid var(--line-soft); border-left: 2px solid var(--line);
  border-radius: var(--radius-sm); background: var(--surface);
}
.msg-mail-zu summary::-webkit-details-marker { display: none; }
.msg-mail-zu summary:hover { border-color: var(--accent-dim); color: var(--text); }
.msg-mail-zu summary .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }
.msg-mail-zu summary b { font-weight: 600; color: var(--text); flex: none; }
/* Der Textanfang darf schrumpfen, Name und Datum nicht — sonst rutscht auf dem
   Handy das Datum aus der Zeile. */
/* Drei Stufen, damit die Zeile auf einen Blick lesbar ist: Name hell (--text),
   Textanfang mittel (--muted), Datum leise (--faint, jetzt 5,34:1). */
.msg-mail-zu .mmz-vorschau {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--muted);
}
.msg-mail-zu .mmz-tag {
  flex: none; font-size: 10.5px; text-transform: uppercase; letter-spacing: .3px;
  padding: 1px 6px; border-radius: 999px; border: 1px solid var(--line);
}
.msg-mail-zu .mmz-tag.mail_in  { color: var(--s-neu); border-color: rgba(91,168,234,.4); }
.msg-mail-zu .mmz-tag.mail_out { color: var(--accent); border-color: var(--accent-dim); }
.msg-mail-zu summary .when { flex: none; margin-left: auto; color: var(--faint); font-family: var(--font-mono); font-size: 11.5px; }
.msg-mail-zu[open] summary { margin-bottom: 9px; color: var(--text); }
/* Zugeklappt trägt die Zeile die Markierung selbst — die Kante der Nachricht
   steckt sonst im eingeklappten Rumpf und ist unsichtbar. */
.msg-mail-zu.hat-info > summary { border-left-color: var(--s-neu); }
.msg-mail-zu.hat-rueckfrage > summary { border-left-color: var(--accent); border-left-width: 3px; }
/* Die Marke darf nicht schrumpfen; weichen soll der Textanfang. */
.msg-mail-zu summary .mark-chip { flex: none; }
.msg-mail-zu[open] > .msg { margin-bottom: 9px; }

/* Auf dem Handy ist fuer den Textanfang kein Platz — Name und Datum genuegen. */
@media (max-width: 620px) {
  .msg-mail-zu .mmz-vorschau { display: none; }
}

/* ============ User-Chip + Logout ============ */
.user-chip { position: relative; }
.user-chip .uname { font-weight: 500; }
.chip-btn {
  display: inline-flex; align-items: center; gap: 9px;
  background: transparent; border: none; cursor: pointer;
  color: var(--muted); font-family: var(--font-body); font-size: 13px; font-weight: 500;
  padding: 4px 6px; border-radius: var(--radius-sm); transition: background .12s;
}
.chip-btn:hover { background: var(--raised); color: var(--text); }
.chip-btn .caret { width: 14px; height: 14px; opacity: .7; }
.user-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 70;
  min-width: 190px; padding: 5px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: 0 14px 38px rgba(0,0,0,.5);
  display: flex; flex-direction: column;
}
.user-menu[hidden] { display: none; }   /* sonst überschreibt display:flex das hidden-Attribut */
.user-menu button {
  text-align: left; background: transparent; border: none; cursor: pointer;
  color: var(--text); font-family: var(--font-body); font-size: 13px;
  padding: 9px 11px; border-radius: var(--radius-sm); transition: background .12s;
}
.user-menu button:hover { background: var(--hover); }

/* Mitarbeiter-Verwaltung */
.user-list { display: flex; flex-direction: column; gap: 7px; }
/* ---------- Die Mitarbeiterzeile ist eine GRUPPE (27.08.2026) ----------
   Vorher ein starres Flex ohne Umbruch mit bis zu zehn Kindern. Gemessen:
   am Schreibtisch ragten drei Elemente aus dem 498 px breiten Dialog, am
   Handy fünf — darunter die Modulauswahl. Abgeschnitten, nicht überlappend,
   und damit unerreichbar.

   `flex-wrap` plus Basisbreiten an den drei Gruppen: die Zeile bricht, sobald
   sie zu eng wird — bei jeder Breite, in jedem Dialog. Eine Media Query wäre
   eine Behauptung über Geräte; das hier ist eine über den Platz, der da ist.
   Dieselbe Bauart wie bei `.seg-row` seit dem 26.08. */
.user-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
/* Kopf: Avatar, Name, Zustand — das, was die Zeile identifiziert, bleibt
   zusammen und nimmt den Platz, der übrig ist. */
.user-row .ur-kopf { display: flex; align-items: center; gap: 10px; flex: 1 1 200px; min-width: 0; }
/* Die Rechte: drei Kästchen, die zusammengehören. Unter 240 px sind sie zu
   dritt nicht lesbar — dann bekommen sie eine eigene Zeile. */
.user-row .ur-rechte { display: flex; align-items: center; gap: 12px; flex: 0 1 auto; flex-wrap: wrap; }
/* Die Knöpfe rechts, und sie rutschen als Gruppe: ein Knopf allein in einer
   neuen Zeile wäre schlechter als vier zusammen.
   ---- Seit 123 (24.09.2026) sind es fünf, und die Gruppe bricht in sich um ----
   Mit „Wochenziel" war die Gruppe am Handy breiter als die Zeile, und
   „Deaktivieren" und „✕" standen jenseits der Kante — gefunden vom
   Browserlauf, Abschnitt M. Ein Knopf allein in einer zweiten Zeile ist
   schlechter als fünf in einer; einer, den man nicht treffen kann, ist gar
   keiner. */
.user-row .ur-aktionen { display: flex; align-items: center; gap: 6px; flex: 0 1 auto; flex-wrap: wrap; margin-left: auto; }
@media (max-width: 560px) {
  /* Am Handy stehen die Gruppen untereinander, jede über die volle Breite —
     und die Knöpfe verlieren ihr `margin-left: auto`, sonst klebten sie rechts. */
  .user-row .ur-kopf, .user-row .ur-rechte, .user-row .ur-aktionen { flex: 1 1 100%; margin-left: 0; }
}
.user-row .ur-name { flex: 1; font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-row .ur-state { font-size: 11px; color: var(--ok); }
.user-row .ur-state.warn { color: var(--accent); }
.user-add { display: flex; gap: 8px; margin-top: 13px; padding-top: 13px; border-top: 1px solid var(--line); }
.user-add input { flex: 1; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); font-size: 13px; padding: 8px 10px; }
.user-add input:focus { outline: none; border-color: var(--accent-dim); }
.user-add .btn { flex: none; }

/* ============================================================
   ★ Design-Polish — modernisierte, weichere Oberfläche
   ============================================================ */

/* --- Tiefe: weiche Schatten statt nur Linien --- */
.panel, .detail-head { box-shadow: var(--shadow); border-color: var(--line-soft); }
.stat, .msg, .cust-card, .reply, .sp, .timer, .demo-banner { box-shadow: var(--shadow-sm); }
.modal, .login-card, .timer-tray, .user-menu, .ac-box, .toast { box-shadow: var(--shadow-lg); }

/* --- Weiche, einheitliche Übergänge --- */
.btn, .t-row, .card, .stat.click, .cust-card.click, .nav a, .chip,
.filters select, .user-menu button, .ac-item, .tray-row, .tr-res, .badge, .avatar {
  transition: transform .15s cubic-bezier(.4,0,.2,1), box-shadow .15s,
              background .13s, border-color .13s, color .13s, filter .13s;
}
.btn:active { transform: translateY(1px) scale(.99); }

/* --- Karten heben sich beim Hover --- */
.stat.click:hover, .cust-card.click:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.card:hover { box-shadow: var(--shadow); }
.t-row:hover { background: var(--hover); }

/* --- Primär-Button: Kupfer-Verlauf mit Glow --- */
.btn.primary {
  background-image: var(--accent-grad);
  border-color: transparent;
  color: #18120A;
  box-shadow: 0 2px 12px -3px rgba(230,162,86,.5);
}
.btn.primary:hover { filter: brightness(1.07); box-shadow: 0 6px 20px -4px rgba(230,162,86,.6); }
.btn.primary:active { filter: brightness(.98); }

/* --- Eingaben: weicher Fokus-Ring --- */
input:focus, select:focus, textarea:focus { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.search input:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--ring); }

/* --- Brand 'y' im Akzent-Verlauf --- */
.brand .y {
  background: var(--accent-grad); -webkit-background-clip: text;
  background-clip: text; -webkit-text-fill-color: transparent;
}

/* --- Sidebar: aktiver Eintrag mit Akzentleiste + Verlauf --- */
.nav a { position: relative; }
.nav a.active { background: linear-gradient(90deg, var(--accent-soft), rgba(230,162,86,0) 85%); }
.nav a.active::before {
  content: ''; position: absolute; left: -12px; top: 9px; bottom: 9px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--accent);
}

/* --- Overlays: Blur hinterm Modal --- */
.modal-bg { backdrop-filter: blur(6px) saturate(1.1); background: rgba(6,9,13,.6); }
.login-bg { backdrop-filter: blur(9px) saturate(1.1); }

/* --- Avatare: feiner Innenrand --- */
.avatar { box-shadow: inset 0 0 0 1px rgba(255,255,255,.07); }

/* --- Badges als weiche Pillen --- */
.badge { border-radius: 999px; }

/* --- Topbar: feine Trennkante --- */
.topbar { box-shadow: 0 1px 0 rgba(0,0,0,.35); }

/* --- Panel-Kopf etwas luftiger --- */
.page-head h1 { letter-spacing: -.025em; }

/* --- Dezentere, modernere Scrollbars --- */
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 8px;
  border: 2px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--faint); background-clip: padding-box; }

/* ---------- Der Anfasser am Feldrand (115.3, 16.09.2026) ----------
   Die drei Striche unten rechts an einem mehrzeiligen Feld sind der
   Vorgabe-Anfasser des Browsers: hell, weil er für helle Oberflächen gemacht
   ist. Auf dunklem Grund ist er das auffälligste Element des ganzen Feldes.
   Gemessen am 16.09.2026: `::-webkit-scrollbar` ist zweimal gestaltet,
   `::-webkit-resizer` kein einziges Mal — der Anfasser war schlicht nie
   angesehen worden.

   Er wird NICHT abgeschaltet. Dreizehn Felder tragen `resize: vertical`, und
   seit 115.2 wächst ein Feld zwar von selbst mit, aber bis zu einem Deckel —
   wer mehr will, zieht weiter. Abschalten nähme das weg.

   Gezeichnet wie ein Rollbalken-Daumen, aus denselben Tokens: zwei Striche in
   `--line`, auf Zeigen `--faint`. Eine Stelle, nicht dreizehn — die
   Auszeichnung greift für jedes `textarea` mit `resize`. */
::-webkit-resizer {
  background-color: transparent;
  background-image: linear-gradient(135deg,
    transparent 0 42%, var(--line) 42% 55%,
    transparent 55% 64%, var(--line) 64% 77%,
    transparent 77% 100%);
}
textarea:hover::-webkit-resizer, textarea:focus::-webkit-resizer {
  background-image: linear-gradient(135deg,
    transparent 0 42%, var(--faint) 42% 55%,
    transparent 55% 64%, var(--faint) 64% 77%,
    transparent 77% 100%);
}

/* --- Stat-Karten: prägnanter, dashboard-typisch --- */
.stat { padding: 18px 20px; }
.stat .label { text-transform: uppercase; letter-spacing: .05em; font-size: 10.5px; color: var(--faint); }
.stat .value { font-size: 30px; }

/* --- Sidebar-Brand & Fuß feiner --- */
.brand { letter-spacing: -.03em; }
.side-foot { opacity: .85; }

/* --- Panels: Kopfzeile mit etwas mehr Gewicht --- */
.panel-head { font-weight: 600; letter-spacing: -.01em; }

/* --- Suchfeld: einladender --- */
.search input { border-radius: 10px; transition: border-color .14s, box-shadow .14s, background .14s; }
.search input:hover { border-color: var(--faint); }

/* --- Hintergrund mit dezentem warmen Schimmer (Tiefe) --- */
body {
  background:
    radial-gradient(900px 480px at 78% -10%, rgba(230,162,86,.05), transparent 55%),
    var(--bg);
  background-attachment: fixed;
}

/* ============================================================
   ★ Prioritäten farbiger — kritisch sticht ins Auge
   ============================================================ */
.prio { padding: 2px 9px; border-radius: 999px; background: rgba(255,255,255,.05); }
.prio.kritisch {
  color: #fff; background: var(--p-kritisch);
  box-shadow: 0 0 0 1px rgba(240,96,107,.4), 0 2px 9px -2px rgba(240,96,107,.65);
}
.prio.hoch    { color: #1c1308; background: var(--p-hoch); }
.prio.normal  { color: var(--p-normal);  background: rgba(91,168,234,.16); }
.prio.niedrig { color: var(--p-niedrig); background: rgba(86,97,117,.22); }

/* Kritische Listenzeilen: roter, leuchtender Spine + zarter Tint */
.t-row.crit { background: linear-gradient(90deg, rgba(240,96,107,.07), transparent 55%); }
.t-row.crit:hover { background: linear-gradient(90deg, rgba(240,96,107,.12), var(--raised) 55%); }
.t-row.crit::before { width: 4px; background: var(--p-kritisch); box-shadow: 0 0 10px rgba(240,96,107,.55); }
.t-row.crit .num { color: var(--p-kritisch); }

/* Kritische Board-Karten: roter Rahmen + Tint + Glow */
.card.crit {
  border-color: rgba(240,96,107,.42);
  background: linear-gradient(180deg, rgba(240,96,107,.09), var(--surface) 72%);
}
.card.crit::before { box-shadow: 0 0 10px rgba(240,96,107,.5); }

/* Dashboard „Kritisch": kräftig rot, sobald >0 */
.stat.alert {
  border-color: rgba(240,96,107,.45);
  background: linear-gradient(180deg, rgba(240,96,107,.10), var(--surface));
}
.stat.alert .value { color: var(--p-kritisch); }
.stat.alert .label { color: var(--p-kritisch); opacity: .85; }

/* Status-Badges einen Tick kräftiger (mehr Farbe insgesamt) */
.badge.neu        { background: rgba(91,168,234,.18); }
.badge.in_arbeit  { background: rgba(230,162,86,.18); }
.badge.warten     { background: rgba(176,138,224,.18); }
.badge.geloest    { background: rgba(78,197,140,.18); }

/* --- Reduced motion respektieren (Polish-Übergänge aus) --- */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
.user-chip .logout {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  cursor: pointer; transition: background .12s, color .12s, border-color .12s;
}
.user-chip .logout:hover { background: var(--hover); color: var(--text); border-color: var(--faint); }
.user-chip .logout svg { width: 15px; height: 15px; }
@media (max-width: 860px) { .user-chip .uname { display: none; } }

/* ============ Login-Overlay ============ */
.login-bg {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(13,17,23,.86); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
}
.login-card {
  width: 320px; max-width: calc(100vw - 40px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px 24px;
  display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
}
.login-card .brand { padding: 0 0 8px; font-size: 22px; }
.login-card h2 { font-family: var(--font-display); font-size: 16px; font-weight: 600; margin-bottom: 8px; }
.login-card label { font-size: 12px; color: var(--muted); margin-top: 6px; }
.login-card select, .login-card input {
  width: 100%; padding: 9px 11px;
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text);
  font-family: var(--font-body); font-size: 13.5px;
}
.login-card input:focus, .login-card select:focus { outline: none; border-color: var(--accent-dim); }
.login-card .btn { margin-top: 14px; justify-content: center; }
.login-err {
  background: rgba(232,96,107,.12); border: 1px solid rgba(232,96,107,.3);
  color: var(--danger); font-size: 12.5px;
  padding: 7px 11px; border-radius: var(--radius-sm);
}
/* Der Umschalter in den Einrichtungsmodus. Kein Knopf im Sinne einer Aktion —
   eine Textzeile, die die Form wechselt. Sie verraet nichts: dass jemand SIE
   anklickt, sagt dem Server nichts darueber, welches Konto ungeschuetzt ist. */
.login-wechsel { display: block; width: 100%; margin-top: 10px; background: none; border: none;
  color: var(--muted); font-size: 12px; cursor: pointer; text-align: center; padding: 4px; }
.login-wechsel:hover { color: var(--accent); text-decoration: underline; }
.login-card input[type="text"] { width: 100%; }

/* ============ Berichte ============ */
.month-nav { display: flex; align-items: center; gap: 10px; }
.month-nav .m-label {
  font-family: var(--font-display); font-weight: 600; font-size: 14px;
  min-width: 130px; text-align: center; text-transform: capitalize;
}
.btn.sm:disabled { opacity: .35; cursor: default; }

.report-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.report-table th {
  text-align: left; font-weight: 600; color: var(--faint);
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
}
.report-table th:nth-child(n+2), .report-table td:nth-child(n+2) { text-align: right; }
.report-table th:last-child, .report-table td:last-child { text-align: right; }
.report-table td { padding: 11px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.report-table tr:last-child td { border-bottom: none; }
.report-table tr:hover td { background: var(--hover); }
.report-table td:first-child { text-align: left; }
.report-table .sub { color: var(--faint); font-size: 11px; }
.report-table .bill { color: var(--accent); font-weight: 500; }
.report-table .val { font-family: var(--font-mono); font-weight: 500; }
.odoo-on  { color: var(--ok); font-family: var(--font-mono); font-size: 12px; }
.odoo-off { color: var(--faint); }
.panel-head .more { cursor: pointer; }

/* ============ Mail-Anhänge ============ */
.att-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.att {
  display: inline-flex; align-items: center; gap: 7px;
  text-decoration: none; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg); transition: border-color .12s, color .12s;
}
.att.file { padding: 7px 10px; font-size: 12.5px; max-width: 240px; }
.att.file:hover { border-color: var(--accent-dim); color: var(--text); }
.att.file svg { width: 15px; height: 15px; flex: none; color: var(--faint); }
.att.file .fn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att.file .fs { color: var(--faint); font-family: var(--font-mono); font-size: 11px; flex: none; }
.att.img { padding: 0; overflow: hidden; line-height: 0; }
.att.img img {
  width: 92px; height: 92px; object-fit: cover; display: block;
  transition: transform .15s;
}
.att.img:hover { border-color: var(--accent-dim); }
.att.img:hover img { transform: scale(1.04); }

/* ============ HTML-Mail (sandboxed) ============ */
.mail-html {
  width: 100%;
  height: 340px;
  margin-top: 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  resize: vertical;
  display: block;
}

/* ============ Filter-Chips & Sortierung ============ */
.filters .chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0 12px; height: 34px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.filters .chip:hover { background: var(--hover); color: var(--text); }
.filters .chip.on { background: var(--accent-soft); border-color: var(--accent-dim); color: var(--accent); }
.filters .chip b { font-family: var(--font-mono); font-size: 11px; }
.filters .sort { margin-left: auto; }

/* ============ Textbaustein-Auswahl (Reply) ============ */
.tpl-select {
  margin-left: auto;
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  font-family: var(--font-body); font-size: 12.5px; padding: 4px 8px; cursor: pointer;
}
.tpl-select:focus { outline: none; border-color: var(--accent-dim); }

/* ============ Einstellungen / Optionen ============ */
.settings-body { padding: 16px 18px; font-size: 13px; }
.settings-body .kv { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; color: var(--muted); }
.settings-body .kv .v { color: var(--text); }
.hint-text { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); color: var(--faint); font-size: 12.5px; line-height: 1.6; }
.flag { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.flag.on  { color: var(--ok); background: rgba(78,197,140,.13); }
.flag.off { color: var(--faint); background: var(--raised); }

.opt {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft); cursor: pointer;
}
.opt:last-child { border-bottom: none; }
.opt > span { display: flex; flex-direction: column; gap: 2px; color: var(--text); font-weight: 500; }
.opt > span small { color: var(--faint); font-weight: 400; font-size: 11.5px; }
.opt input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); flex: none; cursor: pointer; }

.tpl-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.tpl-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.tpl-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.tpl-info b { font-size: 13px; }
.tpl-info span { color: var(--faint); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-add { display: flex; flex-direction: column; gap: 8px; padding-top: 14px; border-top: 1px solid var(--line); }
.tpl-add input, .tpl-add textarea {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); font-family: var(--font-body); font-size: 13px; padding: 8px 11px;
}
.tpl-add textarea { min-height: 80px; resize: vertical; font-family: var(--font-mono); font-size: 12.5px; }
.tpl-add input:focus, .tpl-add textarea:focus { outline: none; border-color: var(--accent-dim); }
.tpl-add .btn { align-self: flex-start; }

/* Leistungsarten verwalten */
.art-map { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.art-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Zwei Ankreuzfelder in einer Zeile — sie brauchen feste Breite, sonst
   verschiebt sich „aktiv" je nach Laenge des Artikelnamens. */
.art-row .art-active { flex: none; white-space: nowrap; }
/* Hinweis unter dem Haken beim Erfassen: sagt, warum er draussen ist. */
.l-bill-hint { font-size: 11.5px; color: var(--muted); line-height: 1.5; margin: -4px 0 0 26px; }
.art-row.off { opacity: .5; }
/* Der Platz in der Auswahl (120). `flex: none` wie seine Nachbarn — das
   einzige nachgiebige Glied der Zeile bleibt `.art-prod` (108). */
.art-row .art-sort {
  flex: none; width: 56px; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text); font-family: var(--font-body);
  font-size: 13px; padding: 7px 8px; text-align: right;
}
.art-row .art-sort:focus { outline: none; border-color: var(--accent-dim); }
.art-color { flex: none; width: 26px; height: 26px; padding: 0; border: 1px solid var(--line); border-radius: 6px; background: none; cursor: pointer; }
.art-label { flex: none; width: 170px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); font-family: var(--font-body); font-size: 13px; font-weight: 500; padding: 7px 10px; }
.art-label:focus { outline: none; border-color: var(--accent-dim); }
.art-row .art-prod {
  flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text);
  font-family: var(--font-body); font-size: 12.5px; padding: 7px 10px; color-scheme: dark;
}
.art-row .art-prod:focus { outline: none; border-color: var(--accent-dim); }
.art-row .art-prod:disabled { opacity: .5; cursor: not-allowed; }
.art-active { flex: none; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap; }
.art-add { display: flex; align-items: center; gap: 10px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.art-add input[type=text] { flex: 1; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); font-family: var(--font-body); font-size: 13px; padding: 8px 11px; }
.art-add input[type=color] { flex: none; width: 26px; height: 26px; padding: 0; border: 1px solid var(--line); border-radius: 6px; background: none; cursor: pointer; }
@media (max-width: 560px) { .art-row { flex-wrap: wrap; } .art-label { width: auto; flex: 1; } .art-row .art-prod { flex-basis: 100%; } }

/* ============ Kompakt-Modus ============ */
body.compact .main { padding: 16px 18px 28px; }
body.compact .t-row { padding-top: 7px; padding-bottom: 7px; }
body.compact .stat { padding: 14px 16px; }
body.compact .panel-head { padding-top: 11px; padding-bottom: 11px; }
body.compact .card { padding: 9px 11px; }
body.compact .stat-grid, body.compact .dash-cols { gap: 10px; }
body.compact .page-head { margin-bottom: 14px; }

/* ============ Odoo-Autovervollständigung (Neues Ticket) ============ */
.ac-wrap { position: relative; }
.ac-hint { font-size: 11px; font-weight: 600; color: var(--faint); margin-left: 6px; }
.ac-hint.on { color: var(--ok); font-family: var(--font-mono); }
.ac-box {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 5;
  margin-top: 4px; max-height: 240px; overflow-y: auto;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: 0 10px 30px rgba(0,0,0,.45);
}
.ac-item {
  display: flex; flex-direction: column; gap: 1px;
  padding: 8px 11px; cursor: pointer; border-bottom: 1px solid var(--line-soft);
}
.ac-item:last-child { border-bottom: none; }
.ac-item:hover { background: var(--accent-soft); }
.ac-name { font-size: 13px; color: var(--text); font-weight: 500; }
.ac-mail { font-size: 11.5px; color: var(--faint); font-family: var(--font-mono); }
.ac-msg { padding: 9px 11px; font-size: 12px; color: var(--faint); }
.ac-msg.err { color: var(--danger); }

/* ============ Zeiterfassung — Live-Timer ============ */
@keyframes tc-blink { 50% { opacity: .25; } }

/* Topbar-Button + Dropdown-Panel */
.timer-area { position: relative; }
.timer-btn {
  display: inline-flex; align-items: center; gap: 7px;
  height: 34px; padding: 0 12px;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  font-family: var(--font-body); font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.timer-btn:hover { background: var(--hover); color: var(--text); border-color: var(--faint); }
.timer-btn svg { width: 16px; height: 16px; }
.timer-btn.active { color: var(--text); }
.timer-btn.running { border-color: rgba(232,96,107,.4); background: rgba(232,96,107,.10); color: var(--text); }
.timer-btn .tb-num { font-weight: 600; margin-right: 6px; }
.timer-btn .tb-time { color: var(--danger); font-weight: 600; }

.timer-tray {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 70;
  width: 340px; max-width: 92vw;
  max-height: calc(100vh - 80px); overflow: hidden auto;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 16px 44px rgba(0,0,0,.5);
}
.tray-head { padding: 12px 14px; font-family: var(--font-display); font-weight: 600; font-size: 13px; border-bottom: 1px solid var(--line); }
.tray-start { position: relative; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); }
.tray-free { width: 100%; margin-bottom: 8px; justify-content: center; }
.tray-start input {
  width: 100%; padding: 9px 11px; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); font-family: var(--font-body); font-size: 13px;
}
.tray-start input:focus { outline: none; border-color: var(--accent-dim); }
.tray-results {
  margin-top: 6px; max-height: 220px; overflow-y: auto;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.tr-res { display: flex; gap: 8px; align-items: baseline; padding: 8px 11px; cursor: pointer; border-bottom: 1px solid var(--line-soft); font-size: 12.5px; }
.tr-res:last-child { border-bottom: none; }
.tr-res:hover { background: var(--accent-soft); }
.tr-res .rs-subj { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.tr-res .rs-zu { color: var(--faint); font-size: 10px; text-transform: uppercase; flex: none; }
.tr-res.tr-new { border-top: 1px solid var(--line); color: var(--accent); font-weight: 500; }
.tr-res.tr-new:hover { background: var(--accent-soft); }
.tr-res .rs-new { color: var(--accent); }

/* Kundenkarten klickbar */
.cust-card.click { cursor: pointer; transition: border-color .12s, transform .12s; }
.cust-card.click:hover { border-color: var(--accent-dim); transform: translateY(-1px); }
.cust-card .cust-new { color: var(--accent); font-weight: 500; }

/* Zeiteintrag löschen (✕ erscheint beim Hover der Zeile) */
.time-del {
  flex: none; width: 18px; height: 18px; padding: 0;
  border: none; background: transparent; color: var(--faint);
  font-size: 11px; cursor: pointer; opacity: 0; transition: opacity .12s, color .12s;
}
.time-row:hover .time-del { opacity: 1; }
.time-del:hover { color: var(--danger); }

/* ============ Politur / Overflow ============ */
.detail-head .meta span { overflow-wrap: anywhere; min-width: 0; }
.detail-head h1 { overflow-wrap: anywhere; }
.lbl-opt { color: var(--faint); font-weight: 400; font-size: 11px; }
.table-wrap { overflow-x: auto; margin: -2px; padding: 2px; }
.report-table { min-width: 560px; }
/* Modal: einheitliche Feldhöhe & dunkler Datepicker */
.modal-body input[type="datetime-local"] { color-scheme: dark; }
.modal-body input:disabled { opacity: .5; cursor: not-allowed; }
/* lange Betreffzeilen in Listen/Karten sauber kürzen */
.t-row .subj, .card .subj { overflow: hidden; text-overflow: ellipsis; }
.card .subj { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; white-space: normal; }

.tray-list { max-height: 300px; overflow-y: auto; }
.tray-row { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); }
.tray-row:last-child { border-bottom: none; }
.tray-row.run { background: rgba(232,96,107,.06); }
.tray-row .tr-ico {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 28px; height: 28px; border-radius: 50%; cursor: pointer; font-size: 11px;
  background: var(--raised); border: 1px solid var(--line); color: var(--text);
}
.tray-row .tr-ico:hover { border-color: var(--faint); }
.tray-row .tr-main { flex: 1; min-width: 0; cursor: pointer; display: flex; flex-direction: column; gap: 1px; }
/* Ticketnummer · Kunde in einer Kopfzeile, der Titel darunter.
   Nummer und Kunde sind die unterscheidenden Merkmale und werden NICHT
   gekuerzt: bei aehnlichen Titeln sind sie das Einzige, woran man erkennt, fuer
   wen die Zeit laeuft. Sind sie zu lang, brechen sie um (`flex-wrap` und
   `overflow-wrap`) — umbrechen verliert nichts, kuerzen schon.
   Der Titel darf kuerzen, er ist der lange Teil. */
/* Kunde im Trefferbild der Ticketsuche (Stopp-Dialog) — damit man beim Waehlen
   schon sieht, wen man waehlt, und nicht erst danach. */
.l-res-kunde { color: var(--muted); font-size: 11.5px; margin-left: auto; padding-left: 8px; white-space: nowrap; }
.tray-row .tr-kopf { display: flex; flex-wrap: wrap; align-items: baseline; gap: 1px 6px; min-width: 0; }
.tray-row .tr-num { font-size: 12px; font-weight: 600; flex: none; }
.tray-row .tr-kunde { font-size: 11.5px; color: var(--muted); overflow-wrap: anywhere; }
.tray-row .tr-kunde::before { content: '· '; color: var(--faint); }
.tray-row .tr-main:hover .tr-num { color: var(--accent); }
.tray-row .tr-subj { font-size: 11.5px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tray-row .tr-time { font-size: 13px; font-weight: 600; color: var(--muted); flex: none; }
.tray-row .tr-time.run { color: var(--danger); }
.tray-row .tr-stop {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 26px; height: 26px; border: none; border-radius: 50%;
  background: var(--danger); color: #fff; cursor: pointer; font-size: 10px; transition: transform .1s;
}
.tray-row .tr-stop:hover { transform: scale(1.12); }
.tray-empty { padding: 22px 14px; text-align: center; color: var(--faint); font-size: 12.5px; line-height: 1.6; }
.tray-head { display: flex; align-items: center; justify-content: space-between; }
.tray-close { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 13px; padding: 0 2px; line-height: 1; }
.tray-close:hover { color: var(--text); }
.tray-row.open { background: var(--raised); }
.tray-row .tr-notecount { flex: none; font-size: 10.5px; color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 1px 6px; white-space: nowrap; }
/* Timer verwerfen (dezent, wird beim Hover der Zeile kräftiger) */
.tray-row .tr-kill {
  flex: none; width: 22px; height: 22px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--faint);
  font-size: 12px; cursor: pointer; border-radius: 50%;
  opacity: .5; transition: opacity .12s, color .12s, background .12s;
}
.tray-row:hover .tr-kill { opacity: 1; }
.tray-row .tr-kill:hover { color: var(--danger); background: var(--hover); }

/* Aufgeklappter Timer: Name + Zwischennotizen */
.tr-detail { padding: 10px 12px 12px; border-bottom: 1px solid var(--line-soft); background: var(--raised); display: flex; flex-direction: column; gap: 8px; }
.tr-detail input {
  width: 100%; padding: 8px 10px; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text); font-family: var(--font-body); font-size: 12.5px;
}
.tr-detail input:focus { outline: none; border-color: var(--accent-dim); }
.tr-name { font-weight: 600; }
.tr-open { align-self: flex-start; background: none; border: none; color: var(--accent); cursor: pointer; font-size: 11.5px; padding: 0; }
.tr-notes { display: flex; flex-direction: column; gap: 4px; max-height: 160px; overflow-y: auto; }
.tr-note { display: flex; align-items: baseline; gap: 8px; font-size: 12px; padding: 4px 6px; border-radius: 6px; background: var(--bg); }
.tr-note .tn-time { flex: none; color: var(--muted); font-size: 11px; }
.tr-note .tn-text { flex: 1; min-width: 0; color: var(--text); overflow-wrap: anywhere; }
.tr-note .tn-del { flex: none; background: none; border: none; color: var(--faint); cursor: pointer; font-size: 11px; padding: 0 2px; }
.tr-note .tn-del:hover { color: var(--danger); }
.tr-note-empty { color: var(--faint); font-size: 11.5px; padding: 2px 4px; }

/* Timer-Abschnitte im Tray */
.tr-segs { display: flex; flex-direction: column; gap: 5px; max-height: 180px; overflow-y: auto; }
.tr-seg { display: flex; align-items: center; gap: 8px; }
.tr-seg .ts-time { flex: none; width: 92px; font-size: 11px; color: var(--muted); }
.tr-seg .ts-time b { color: var(--danger); font-weight: 600; }
.tr-detail .tr-seg .ts-text { width: auto; flex: 1; min-width: 0; }
.tr-hint { font-size: 11px; color: var(--faint); line-height: 1.5; }

/* Feldgruppe im Modal: Label + Control eng zusammen, Gruppen mit Abstand. */
.modal-body .fld { display: flex; flex-direction: column; gap: 5px; }
.modal-body .fld label { margin: 0; }

/* Segment-Editor im Stopp-Dialog */
.tm-segs { display: flex; flex-direction: column; gap: 8px; }
/* ---------- Die Zeitzeile bricht um, statt die Notiz zu erdrücken (26.08.2026) ----------
   Hier stand eine starre Zeile: zwei Zeitfelder mit `flex: none` und die Notiz
   als Rest. Auf dem Handy gemessen (Bildschirmfoto vom 26.08., 18:35): die
   Notiz war rund drei Zeichen breit und brach senkrecht um — „Bes / pre / chu /
   ng". Der Grund ist nicht die Rechnung, sondern iOS: `input[type=time]`
   rendert dort breiter als die vorgegebenen 92 px, und `flex: none` verbietet
   das Schrumpfen. Was übrig bleibt, bekommt die Notiz.

   `flex-wrap` plus eine BASIS an der Notiz statt einer Media Query: die Zeile
   bricht, sobald sie zu eng wird — bei jeder Fensterbreite, in jedem Dialog und
   auch dann, wenn ein Browser die Zeitfelder eines Tages anders misst. Eine
   feste Grenze in Pixeln wäre eine Behauptung über Geräte; das hier ist eine
   über den Platz, der wirklich da ist. */
.seg-row { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; }
.seg-row .seg-t { width: 92px; flex: 0 0 auto; margin-top: 2px; }
.seg-row .seg-dash { margin-top: 9px; }
/* 220 px ist die Breite, unter der ein Notizfeld zum Rätsel wird — darunter
   nimmt es lieber die ganze nächste Zeile. */
.seg-row .seg-text { flex: 1 1 220px; min-width: 0; min-height: 36px; line-height: 1.45; resize: none; overflow: hidden; }
/* GRABSTEIN (R12, 18.08.2026): `.seg-fixed` war die feste Zeitanzeige des
   zusammengefuehrten Abschnitts im Stopp-Dialog. Sie ist durch zwei
   `input type="time"` ersetzt — vorher liess sich die Zeit nach dem Umlegen
   des Schalters nicht mehr aendern (Christians „geht dann Timer umstellen
   nicht mehr"). Siehe app.js, openTimerSaveModal/renderSegs. */
.seg-dash { color: var(--faint); flex: none; }
.seg-del { flex: none; margin-top: 7px; background: none; border: none; color: var(--faint); cursor: pointer; font-size: 13px; padding: 0 2px; }
.seg-del:hover { color: var(--danger); }

/* Timer-Box im Ticket-Detail */
.timer {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; margin-bottom: 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
}
.timer.running, .timer.paused { flex-direction: column; align-items: stretch; }
.timer-actions { display: flex; gap: 8px; }
.timer-actions .btn { flex: 1; justify-content: center; }
.timer.idle .hint { color: var(--faint); font-size: 11.5px; }
.timer.running { border-color: rgba(232,96,107,.4); background: rgba(232,96,107,.07); }
.timer.paused  { border-color: rgba(224,154,74,.4); background: rgba(224,154,74,.07); }
.timer .t-live { display: flex; align-items: center; gap: 8px; flex: 1; }
.timer .t-live .pulse { width: 9px; height: 9px; border-radius: 50%; background: var(--danger); animation: tc-blink 1.4s ease-in-out infinite; }
.timer .t-live .pulse.off { background: var(--accent); animation: none; }
.timer #timer-inline { font-size: 18px; font-weight: 600; letter-spacing: .02em; }
.timer.running #timer-inline { color: var(--danger); }
.timer.paused #timer-inline { color: var(--accent); }
.timer .t-state { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .05em; }

/* Eintragsliste: abrechenbar-Marker */
.time-row .bill { flex: none; width: 18px; text-align: center; font-family: var(--font-mono); font-size: 11px; }
.time-row .bill.yes { color: var(--ok); }
.time-row .bill.no  { color: var(--faint); }
.time-empty { color: var(--faint); font-size: 12px; padding: 6px 0; }
.time-bill-line { display: flex; align-items: center; gap: 8px; margin-top: 9px; font-size: 12px; color: var(--muted); cursor: pointer; }

/* Schmaler Modal-Dialog. Checkbox-Styling steht zentral oben (.switch-line …). */
.modal.sm { width: 380px; }

/* ============================================================
   Stempeluhr (Topbar) + Zeiterfassung/Überstunden (View)
   ============================================================ */
.stamp-area { position: relative; }
.stamp-btn {
  display: inline-flex; align-items: center; gap: 7px;
  height: 34px; padding: 0 12px;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted);
  font-family: var(--font-body); font-size: 13px; font-weight: 500; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.stamp-btn:hover { background: var(--hover); color: var(--text); border-color: var(--faint); }
.stamp-btn svg { width: 16px; height: 16px; }
.stamp-btn.running { border-color: rgba(78,197,140,.45); background: rgba(78,197,140,.10); color: var(--text); }
.stamp-btn .mono { font-family: var(--font-mono); font-weight: 600; color: var(--ok); }
.stamp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px rgba(78,197,140,.18); animation: stampPulse 1.6s ease-in-out infinite; }
@keyframes stampPulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.btn.lg { padding: 11px 20px; font-size: 14px; }

.ze-loading { color: var(--faint); padding: 20px 0; }
.ze-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; margin-bottom: 20px; }
.ze-grid > * { min-width: 0; }
@media (max-width: 820px) { .ze-grid { grid-template-columns: 1fr; } }
.ze-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px;
}
.ze-clock { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.ze-clock.on { border-color: rgba(78,197,140,.4); }
.ze-clock-state { font-size: 13px; color: var(--muted); }
.ze-clock-live { font-family: var(--font-mono); font-size: 30px; font-weight: 600; color: var(--text); letter-spacing: .02em; }
.ze-clock.on .ze-clock-live { color: var(--ok); }
.ze-kpi-label { font-size: 12px; color: var(--muted); font-weight: 500; margin-bottom: 6px; }
.ze-kpi-value { font-family: var(--font-mono); font-size: 34px; font-weight: 700; letter-spacing: -.01em; }
.ze-kpi-value.pos { color: var(--ok); }
.ze-kpi-value.neg { color: var(--danger); }
.ze-kpi-sub { font-size: 12px; color: var(--muted); margin: 8px 0 14px; line-height: 1.6; }

/* Stempelungen und Korrekturbuchungen sind eigene Abschnitte, kein Teil des
   Wochenplaners darueber. Der Abstand macht das sichtbar; vorher standen sie
   direkt an der Karte und wirkten wie deren Fortsetzung. */
.ze-cols {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px; margin-top: 22px;
}
.ze-cols > * { min-width: 0; }
@media (max-width: 820px) { .ze-cols { grid-template-columns: 1fr; gap: 20px; } }
.ze-h2 {
  font-family: var(--font-display); font-size: 13px; font-weight: 600;
  margin: 0 0 8px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em;
}
/* Der Schalter fuer stornierte Zeilen sitzt IN der Ueberschrift — dort, wo man
   fragt „warum sehe ich das nicht?". Klein und ruhig: er ist ein Filter, keine
   Aktion. */
.ze-h2 { display: flex; align-items: center; gap: 8px; }
.ze-h2-zusatz { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--faint); }
.ze-storno-schalter { margin-left: auto; text-transform: none; letter-spacing: 0; font-size: 11px; }
.ze-table { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
/* ---- Stempelungen nach Tag und Woche (23.08.2026) ----
   Vier Stempelungen vom 18.08. standen einzeln untereinander, obwohl sie ein
   Tag sind. Die Liste zeigte Eintraege, gedacht wird in Einheiten.

   Die Summen kommen aus `Ueberstunden.balance().days[].worked` und werden hier
   nur angezeigt — zwei Orte, die eine Tagessumme bilden, laufen auseinander,
   und diese hat Geldbezug. */
.ze-woche { margin: 0 0 10px; }
.ze-woche > summary {
  display: flex; align-items: center; gap: 10px; cursor: pointer; list-style: none;
  padding: 6px 8px; border-radius: var(--radius-sm);
  color: var(--muted); font-size: 12px; font-weight: 600;
  border-bottom: 1px solid var(--line);
}
.ze-woche > summary::-webkit-details-marker { display: none; }
.ze-woche > summary::after { content: '›'; margin-left: 4px; color: var(--faint); }
.ze-woche[open] > summary::after { transform: rotate(90deg); display: inline-block; }
.ze-woche > summary:hover { color: var(--text); background: var(--raised); }
.ze-w-label { color: var(--text); }
.ze-w-sum { margin-left: auto; color: var(--accent); font-size: 12.5px; }
.ze-w-n { color: var(--faint); font-weight: 400; font-size: 11.5px; }

.ze-tag { margin: 6px 0 10px; }
.ze-tag-kopf {
  display: flex; align-items: baseline; gap: 8px;
  padding: 3px 8px; color: var(--muted); font-size: 11.5px;
}
/* ---------- Der Tag ist jetzt ein <details> (25.08.2026) ----------
   Die Kopfzeile ist das <summary> und bleibt immer lesbar — dort steht die
   Summe, und genau deshalb darf der Tag zugeklappt sein. Der Standardpfeil
   faellt weg (die Wochen haben schon einen); geklappt wird durch Klick auf die
   ganze Zeile. */
.ze-tag > summary.ze-tag-kopf { cursor: pointer; list-style: none; }
.ze-tag > summary.ze-tag-kopf::-webkit-details-marker { display: none; }
.ze-tag > summary.ze-tag-kopf::marker { content: ''; }
.ze-tag > summary.ze-tag-kopf:hover { background: var(--raised); }
.ze-tag[open] > summary.ze-tag-kopf { border-bottom: 1px solid var(--line); }
.ze-tag-tag { font-weight: 600; color: var(--text); }
/* Die Tagessumme rechtsbuendig auf derselben Achse wie die Dauer der Zeilen —
   sonst sucht das Auge zwischen Summe und Posten. */
.ze-tag-sum { margin-left: auto; color: var(--text); font-size: 12px; }
/* Ein Tag ohne Zeile in der Zeiterfassung: der Strich ist blass, der Grund
   steht im Titel. Keine selbst gerechnete Zahl. */
.ze-tag-sum.ze-tag-ohne { color: var(--faint); cursor: help; }
.ze-tag-n { color: var(--faint); font-size: 11px; flex: none; }
@media (max-width: 560px) {
  .ze-tag-n { display: none; }
  .ze-w-n { display: none; }
}

.ze-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-bottom: 1px solid var(--line-soft); font-size: 12.5px; }
.ze-row:last-child { border-bottom: none; }
.ze-day { color: var(--text); white-space: nowrap; }
.ze-arrow { color: var(--faint); }
.ze-dur { margin-left: auto; font-family: var(--font-mono); color: var(--muted); }
.ze-adj { margin-left: auto; font-family: var(--font-mono); font-weight: 600; white-space: nowrap; }
.ze-adj.pos { color: var(--ok); }
.ze-adj.neg { color: var(--danger); }
.ze-reason { flex: 1; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ze-del { flex: none; background: none; border: none; color: var(--faint); cursor: pointer; font-size: 13px; padding: 2px 4px; border-radius: 4px; }
.ze-del:hover { color: var(--danger); background: var(--raised); }
.ze-edit { flex: none; background: none; border: none; color: var(--faint); cursor: pointer; font-size: 12px; padding: 2px 4px; border-radius: 4px; }
.ze-edit:hover { color: var(--accent); background: var(--raised); }
/* Storniert: bleibt sichtbar, zaehlt nicht. Durchgestrichen und gedaempft — nicht
   versteckt, sonst waere das Storno nur ein hoefliches Loeschen. */
.ze-row.storniert { opacity: .62; }
.ze-row.storniert .ze-day, .ze-row.storniert .ze-dur { text-decoration: line-through; }
.ze-storno-tag { flex: none; font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--danger); border: 1px solid var(--danger); border-radius: 4px; padding: 1px 5px; cursor: help; }
.ze-storno-grund { padding: 0 12px 8px 12px; font-size: 11.5px; color: var(--muted); border-bottom: 1px solid var(--line-soft); }
/* Abwesenheit — eigene Sache, kein Termin. Vier Arten, vier Farben. */
.ze-abw-art { flex: none; font-size: 11px; padding: 1px 7px; border-radius: 4px; font-weight: 600; }
.ze-abw-art.urlaub { background: #2E7D5622; color: #4FB07C; }
.ze-abw-art.krank { background: #B4515122; color: #D9736F; }
.ze-abw-art.frei { background: #5B7FA822; color: #7FA6CC; }
.ze-abw-art.unbezahlt { background: #7A6A9022; color: #A093B8; }
.kal-abw { --c: #4FB07C; opacity: .95; }
.kal-abw.krank { --c: #D9736F; }
.kal-abw.frei { --c: #7FA6CC; }
.kal-abw.unbezahlt { --c: #A093B8; }
/* Wochenplaner: Soll, Feiertag, Abwesenheit und der ueberfaellige Fall */
.wp-marke { font-size: 10px; letter-spacing: .02em; padding: 1px 5px; border-radius: 3px; margin: 2px 0; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.wp-marke.fei { background: var(--accent-soft, #5BA8EA22); color: var(--accent); }
.wp-marke.abw { background: #2E7D5622; color: #4FB07C; }
.wp-marke.kein { background: var(--raised); color: var(--faint); }
/* „laeuft": eine offene Stempelung haelt den Tag aus dem Saldo. Kein stiller
   Zustand — er steht in Akzentfarbe da, weil hier etwas zu tun ist. */
.wp-marke.offen { background: var(--accent-soft, #E09A4A22); color: var(--accent); }
.wp-soll { font-size: 10.5px; color: var(--faint); font-family: var(--font-mono); }
.wp-cell.fei, .wp-cell.abw { background: var(--raised); }
.wp-warn { margin-top: 8px; padding: 8px 10px; border-radius: 6px; background: var(--raised);
  border-left: 3px solid var(--warn, #D9A047); font-size: 12px; color: var(--muted); line-height: 1.5; }
.wp-warn.ueberfaellig { border-left-color: var(--danger); }
.wp-warn code { font-family: var(--font-mono); font-size: 11px; color: var(--text); }
.wp-warn .btn { margin-left: 6px; }
/* Wochenplaner */
.wp-nav { display: inline-flex; align-items: center; gap: 6px; font-weight: 400; }
.wp-nav .wp-label { font-family: var(--font-mono); font-size: 12px; color: var(--muted); min-width: 130px; text-align: center; }
/* minmax(0, 1fr) statt 1fr: `1fr` ist `minmax(auto, 1fr)`, damit kann eine
   Spalte nicht unter die Inhaltsbreite ihrer breitesten Zeile schrumpfen. Sieben
   Kacheln mit „Mo 27.07." plus Innenabstand waren zusammen breiter als der
   Container — der Planer lief rechts heraus und schob die ganze Seite mit, was
   die Abschnitte darunter randlos wirken liess. Gleiche Ursache und gleiche
   Loesung wie bei .detail-grid und .dash-cols. */
.wp-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.wp-grid > * { min-width: 0; }
.wp-cell { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 6px; text-align: center; min-width: 0; }
.wp-cell.we { background: rgba(120,140,170,.06); }
.wp-cell.today { border-color: var(--accent-dim); }
.wp-cell.empty { opacity: .55; }
/* Tag und Datum duerfen umbrechen — lieber zwei Zeilen als ein Ueberlauf. */
.wp-cell .wp-d { font-size: 11.5px; color: var(--muted); font-weight: 600; overflow-wrap: anywhere; }
.wp-cell .wp-d span { color: var(--faint); font-weight: 400; }
.wp-cell .wp-worked { font-size: 13px; color: var(--text); margin: 6px 0 3px; }
.wp-cell .wp-ot { font-size: 12px; font-weight: 600; }
.wp-cell .wp-ot.pos { color: var(--ok); }
.wp-cell .wp-ot.neg { color: var(--danger); }
.wp-cell .wp-ot.none { color: var(--faint); }
.wp-sum { margin-top: 12px; font-size: 13px; color: var(--muted); }
.wp-sum b { font-family: var(--font-mono); font-size: 15px; }
.wp-sum b.pos { color: var(--ok); } .wp-sum b.neg { color: var(--danger); }
.wp-sum .wp-hint { display: block; margin-top: 3px; font-size: 11.5px; color: var(--faint); }
/* Die Bestandteile des Wochensaldos: er ist seit dem 11.08.2026 Anwesenheit PLUS
   Korrekturen, und eine Summe aus zwei Sachen muss sagen, aus welchen. */
.wp-sum .wp-teile { display: block; margin-top: 2px; font-size: 11.5px; color: var(--muted); }
.wp-sum .wp-teile.kuenftig { color: var(--faint); font-style: italic; }
/* Die Korrektur am Tag — sichtbar, aber nicht lauter als die Anwesenheit
   darueber: sie ist ein Vermerk am Tag, nicht seine Hauptzahl. */
.wp-cell .wp-korr { font-size: 10.5px; font-weight: 600; color: var(--accent); font-family: var(--font-mono); }
/* Kuenftig wirksam: sichtbar, aber erkennbar NICHT im Saldo — dieselbe
   Unterscheidung wie die kursive Zeile unter der Wochensumme. */
.wp-cell .wp-korr.kuenftig { color: var(--faint); font-weight: 500; font-style: italic; }
.ze-kuenftig { color: var(--faint); font-style: italic; }
@media (max-width: 620px) { .wp-grid { grid-template-columns: repeat(4, 1fr); } }
.ze-empty { padding: 14px 12px; color: var(--faint); font-size: 12.5px; }

/* ---------- Zeitleiste ---------- */
.tl-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin-bottom: 16px; }
.tl-controls .seg { margin-bottom: 0; }
.page-head .seg { margin-bottom: 0; }
.seg .seg-btn { background: none; border: none; color: var(--muted); cursor: pointer; padding: 6px 14px; border-radius: 6px; font-family: var(--font-body); font-size: 13px; font-weight: 500; }
.seg .seg-btn.active { background: var(--accent); color: #1a1206; font-weight: 600; }
.tl-nav { display: flex; align-items: center; gap: 8px; }
.tl-nav input[type=date] {
  background: var(--surface); border: 1px solid var(--line); color: var(--text); color-scheme: dark;
  border-radius: var(--radius-sm); padding: 6px 10px; font-family: var(--font-body); font-size: 13px;
}
.tl-filters { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.tl-filters select {
  background: var(--surface); border: 1px solid var(--line); color: var(--text); color-scheme: dark;
  border-radius: var(--radius-sm); padding: 6px 10px; font-family: var(--font-body); font-size: 13px; cursor: pointer;
}
.tl-daygroup { margin-bottom: 16px; }
.tl-dayhead { display: flex; align-items: center; justify-content: space-between; margin: 0 0 6px; padding-bottom: 4px; border-bottom: 1px solid var(--line-soft); }
.tl-dayhead .tl-dayjump { background: none; border: none; color: var(--text); font-family: var(--font-display); font-weight: 600; font-size: 13.5px; cursor: pointer; padding: 0; }
.tl-dayhead .tl-dayjump:hover { color: var(--accent); }
.tl-dayhead span { font-family: var(--font-mono); font-size: 12px; color: var(--muted); }
.tl-summary { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
.tl-kpi {
  display: flex; flex-direction: column; gap: 3px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 16px; min-width: 120px;
}
.tl-kpi span { font-size: 11px; color: var(--muted); }
.tl-kpi b { font-family: var(--font-mono); font-size: 18px; font-weight: 700; color: var(--text); }
.tl-kpi b.pos { color: var(--ok); }

.tl-list { display: flex; flex-direction: column; gap: 6px; }
.tl-row {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--c, var(--line));
  border-radius: var(--radius-sm); padding: 9px 12px;
}
.tl-time { flex: none; width: 96px; font-family: var(--font-mono); font-size: 12px; color: var(--muted); }
.tl-time span { color: var(--faint); margin: 0 2px; }
.tl-body { flex: 1; min-width: 0; }
.tl-bar { height: 4px; border-radius: 3px; background: var(--c, var(--accent)); opacity: .55; margin-bottom: 6px; }
.tl-info { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tl-title { font-weight: 600; font-size: 13px; color: var(--text); }
.tl-kunde { font-size: 12px; color: var(--muted); }
.tl-art {
  font-size: 11px; font-weight: 600; color: var(--c, var(--muted));
  border: 1px solid var(--c, var(--line)); border-radius: 999px; padding: 1px 8px; opacity: .9;
}
.tl-notiz { font-size: 12px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-dur { flex: none; font-family: var(--font-mono); font-size: 12.5px; color: var(--text); }
.tl-bill { flex: none; width: 16px; text-align: center; font-family: var(--font-mono); font-size: 12px; }
.tl-bill.yes { color: var(--ok); }
.tl-bill.no { color: var(--faint); }
.tl-del { flex: none; background: none; border: none; color: var(--faint); cursor: pointer; padding: 2px 4px; border-radius: 4px; }
.tl-del:hover { color: var(--danger); background: var(--raised); }
.tl-del:disabled { opacity: .3; cursor: not-allowed; }
.tl-edit { flex: none; background: none; border: none; color: var(--faint); cursor: pointer; padding: 2px 4px; border-radius: 4px; font-size: 12px; }
.tl-edit:hover { color: var(--accent); background: var(--raised); }
.tl-locked { flex: none; width: 18px; text-align: center; font-size: 11px; opacity: .6; }
/* Read-only Zuordnung im Bearbeiten-Modus */
.l-fixed { font-size: 13px; color: var(--text); background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 11px; }

/* ============ Berichte: Geld-Sektion ============ */
.money-panel {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); overflow: hidden; margin-bottom: 16px;
}
.money-hero {
  padding: 22px 24px 20px;
  background: linear-gradient(135deg, rgba(224,154,74,.16), rgba(224,154,74,.02) 70%);
  border-bottom: 1px solid var(--line);
}
.money-label { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); }
.money-big {
  font-family: var(--font-display); font-weight: 600; color: var(--text);
  font-size: 42px; line-height: 1.05; margin-top: 6px; display: flex; align-items: baseline; gap: 8px;
}
.money-big .cur { font-size: 24px; color: var(--accent); }
.money-big small { font-size: 13px; font-weight: 500; color: var(--faint); letter-spacing: .04em; }
.money-sub { margin-top: 8px; font-size: 13px; color: var(--muted); }
/* Was in der Zahl NICHT steckt (111a). Klein und blass: es ist kein
   Hinweis, den man einmal liest und wegklickt, sondern eine Eigenschaft
   der Zahl darueber — und die steht dort, solange die Zahl dort steht. */
.money-fuss { margin-top: 6px; font-size: 11.5px; color: var(--faint); }
/* Aufteilung unter der Umsatzkennzahl: offen vs. schon in Rechnung. Ohne sie
   stand „0 €" neben „8 h abrechenbar" und niemand wusste, wo das Geld ist. */
/* Aufriss: was schon auf einer Rechnung liegt, muss man sehen. */
.ra-fakt { color: var(--accent); font-size: 11.5px; }
.ra-tab tr.fakturiert { opacity: .72; }
.ra-tab .ra-sync { text-align: right; white-space: nowrap; }
.ra-fuss {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 10px; padding: 10px 12px; font-size: 12px; line-height: 1.5;
  border: 1px solid var(--accent-dim); background: var(--accent-soft);
  border-radius: var(--radius-sm); color: var(--muted);
}
.ra-fuss span { flex: 1; min-width: 240px; }
.ra-fuss b { color: var(--text); }

.money-teilung { display: flex; gap: 18px; margin-top: 8px; font-size: 12.5px; color: var(--muted); }
.money-teilung b { color: var(--text); font-weight: 600; }

.money-split { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--line); }
.money-cell { background: var(--surface); padding: 14px 18px; display: flex; flex-direction: column; gap: 3px; }
.money-cell .mc-top { font-size: 11.5px; color: var(--faint); }
.money-cell b { font-family: var(--font-display); font-size: 20px; font-weight: 600; color: var(--text); }
.money-cell .mc-bot { font-size: 11.5px; color: var(--muted); }
.money-cell.firma b { color: var(--ok); }
.money-cell.privat b { color: var(--accent); }
.money-top { padding: 16px 20px 18px; border-top: 1px solid var(--line); }
.money-top .mt-head { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--faint); margin-bottom: 10px; }
.mt-row { display: flex; align-items: center; gap: 12px; padding: 4px 0; }
.mt-row .mt-name { flex: none; width: 160px; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mt-row .mt-bar { flex: 1; height: 8px; background: var(--raised); border-radius: 999px; overflow: hidden; }
.mt-row .mt-bar span { display: block; height: 100%; background: var(--accent-grad); border-radius: 999px; }
.mt-row .mt-val { flex: none; width: 92px; text-align: right; font-size: 12.5px; color: var(--text); }
@media (max-width: 620px) {
  .money-split { grid-template-columns: 1fr; }
  .money-big { font-size: 34px; }
  .mt-row .mt-name { width: 110px; }
}

/* ---------- Modal: Segmented control, Ticket-Suche, Grid ---------- */
.seg { display: inline-flex; background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px; gap: 3px; margin-bottom: 12px; }
.seg button {
  background: none; border: none; color: var(--muted); cursor: pointer;
  padding: 6px 14px; border-radius: 6px; font-family: var(--font-body); font-size: 13px; font-weight: 500;
}
.seg button.active { background: var(--accent); color: #1a1206; font-weight: 600; }
.grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
.l-results { margin-top: 6px; background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm); max-height: 200px; overflow-y: auto; }
.l-res { display: flex; gap: 8px; align-items: baseline; padding: 8px 11px; cursor: pointer; font-size: 12.5px; border-bottom: 1px solid var(--line-soft); }
.l-res:last-child { border-bottom: none; }
.l-res:hover { background: var(--accent-soft); }
.l-res.muted { color: var(--faint); cursor: default; }
.l-chosen { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; font-size: 12.5px; color: var(--ok); background: rgba(78,197,140,.10); border: 1px solid rgba(78,197,140,.3); border-radius: var(--radius-sm); padding: 7px 10px; }
.l-clear { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 13px; padding: 0 2px; }
.l-clear:hover { color: var(--danger); }
.lbl-opt { font-weight: 400; color: var(--faint); font-size: 11px; text-transform: none; }
#l-kunde:disabled { opacity: .45; cursor: not-allowed; }
.ab-text { width: 100%; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); font-family: var(--font-mono); font-size: 12px; padding: 12px; resize: vertical; line-height: 1.5; }
/* Arbeitsbericht (hübsche Darstellung) */
.modal.lg { width: 640px; max-width: 94vw; }
.ab-doc { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 18px 20px; max-height: 60vh; overflow-y: auto; }
.ab-h1 { font-family: var(--font-display); font-size: 20px; font-weight: 600; color: var(--text); }
.ab-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.ab-totals { display: flex; gap: 18px; font-size: 12.5px; color: var(--muted); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); padding: 9px 0; margin: 12px 0 16px; }
.ab-totals b { color: var(--text); font-family: var(--font-display); }
.ab-totals .pos b { color: var(--ok); }
.ab-cust { display: flex; justify-content: space-between; align-items: baseline; font-weight: 600; font-size: 14.5px; color: var(--text); margin: 16px 0 8px; border-bottom: 1px solid var(--line-soft); padding-bottom: 4px; }
.ab-cust-h { font-weight: 500; font-size: 12px; color: var(--faint); font-family: var(--font-mono); }
.ab-item { margin: 0 0 12px 2px; }
.ab-item.intern { opacity: .65; }
.ab-item-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 11.5px; color: var(--muted); }
.ab-item-head .ab-dur { color: var(--text); font-family: var(--font-mono); }
.ab-art { border: 1px solid var(--c, var(--line)); color: var(--c, var(--muted)); border-radius: 999px; padding: 0 8px; font-size: 10.5px; font-weight: 600; }
.ab-ticket { color: var(--accent); }
.ab-tag { background: var(--raised); border-radius: 4px; padding: 0 6px; font-size: 10px; text-transform: uppercase; color: var(--faint); }
.ab-topic { font-weight: 600; font-size: 13px; color: var(--text); margin: 5px 0 1px; }
.ab-note { font-size: 13px; color: var(--muted); margin: 3px 0; white-space: pre-wrap; }
.ab-subs { margin: 1px 0 4px 20px; padding: 0; }
.ab-subs li { font-size: 12.5px; color: var(--muted); margin: 1px 0; }
.ab-empty { color: var(--faint); text-align: center; padding: 20px; }

/* ============ Suite-Launcher ============ */
.suite-launcher { max-width: 900px; margin: 0 auto; padding: 8px 4px 40px; }
.suite-hero { display: flex; align-items: center; gap: 18px; margin: 20px 2px 32px; }
/* Seit 13.08.2026 ein BILD statt der Buchstaben „MI" auf oranger Kachel.
   Kein `background`, kein `box-shadow`, kein `border-radius`: das Logo bringt
   seine Form selbst mit und hat einen durchsichtigen Rand — eine Kachel
   darunter wäre ein zweiter Rahmen um einen, der schon da ist. */
.suite-hero-mark {
  height: 60px; width: auto; flex: none;
  display: block;
}
.suite-hero h1 { font-family: var(--font-display); font-size: 26px; font-weight: 600; letter-spacing: -.02em; }
.suite-hero p { color: var(--muted); margin-top: 4px; font-size: 14px; }

.suite-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.suite-tile {
  display: flex; flex-direction: column; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px;
  text-decoration: none; color: var(--text);
  min-height: 168px;
  transition: transform .14s, border-color .14s, background .14s, box-shadow .14s;
}
a.suite-tile:hover {
  transform: translateY(-3px);
  border-color: var(--accent-dim); background: var(--raised); box-shadow: var(--shadow);
}
.suite-tile.soon { opacity: .58; }
.suite-tile-icon {
  width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent); margin-bottom: 14px;
}
.suite-tile-icon svg { width: 22px; height: 22px; }
.suite-tile.soon .suite-tile-icon { background: var(--raised); color: var(--muted); }
.suite-tile-head { display: flex; align-items: center; gap: 8px; }
.suite-tile-name { font-family: var(--font-display); font-size: 17px; font-weight: 600; }
.suite-badge {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint); border: 1px solid var(--line); border-radius: 20px; padding: 2px 8px;
}
.suite-tile-desc { color: var(--muted); font-size: 13px; margin-top: 4px; }
.suite-tile-foot {
  margin-top: auto; padding-top: 14px; width: 100%;
  font-family: var(--font-mono); font-size: 11px; color: var(--faint);
  border-top: 1px solid var(--line-soft);
}
a.suite-tile:hover .suite-tile-foot { color: var(--muted); }

/* ============ Rechnung-Modul ============ */
.btn.small { padding: 5px 11px; font-size: 12.5px; }

.rech-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin: 4px 2px 22px; flex-wrap: wrap; }
.rech-title { font-family: var(--font-display); font-size: 24px; font-weight: 600; letter-spacing: -.02em; }
.rech-sub { color: var(--muted); font-size: 13.5px; margin-top: 3px; }
.rech-monthnav { display: flex; align-items: center; gap: 10px; }
.rech-month { font-family: var(--font-display); font-weight: 600; min-width: 130px; text-align: center; }

.rech-table { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.rech-row { display: grid; grid-template-columns: 2.2fr 1fr 1fr 1.4fr auto; align-items: center; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.rech-row:last-child { border-bottom: none; }
.rech-th { background: var(--raised); color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; font-weight: 600; padding: 9px 16px; }
.rech-cell { min-width: 0; }
.rech-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.rech-eur { text-align: right; color: var(--text); }
.rech-th .rech-eur, .rech-th div { text-align: left; }
.rech-act { text-align: right; }
.rech-warn { font-family: var(--font-mono); font-size: 10px; color: var(--p-hoch); border: 1px solid var(--p-hoch); border-radius: 20px; padding: 1px 7px; margin-left: 6px; white-space: nowrap; }
.rech-total { background: var(--raised); font-weight: 600; }

.rech-detail { grid-column: 1 / -1; }
.rech-detail-inner { padding: 4px 16px 16px; background: var(--bg); border-bottom: 1px solid var(--line-soft); }
.rech-lines { list-style: none; margin: 10px 0; display: flex; flex-direction: column; gap: 6px; }
.rech-lines li { display: flex; justify-content: space-between; gap: 14px; font-size: 13px; padding: 7px 10px; background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius-sm); }
.rech-lines li span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rech-lines li span:last-child { color: var(--muted); white-space: nowrap; }
.rech-warnbox { font-size: 12.5px; color: var(--p-hoch); background: rgba(230,162,86,.08); border: 1px solid rgba(230,162,86,.3); border-radius: var(--radius-sm); padding: 9px 12px; margin: 8px 0; }
.rech-actions { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-top: 12px; flex-wrap: wrap; }
.rech-sum { color: var(--muted); font-size: 13px; }
.rech-sum b { color: var(--text); }
.rech-inv { margin-top: 16px; }
.rech-inv-h { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--faint); font-weight: 600; margin-bottom: 6px; }
.rech-hint { color: var(--faint); font-size: 12px; margin: 14px 2px 0; }

/* ============ Modul-Topbar (Suite-Shell) ============ */
.modbar {
  display: flex; align-items: center; gap: 18px;
  height: 58px; flex: none; padding: 0 20px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.modbar-left { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1; }
.modbar-right { display: flex; align-items: center; gap: 12px; margin-left: auto; flex: none; }

/* Suite-Switch (Hub + Dropdown) */
.suite-switch { position: relative; display: flex; align-items: center; }
.suite-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 10px 7px 11px; border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: var(--raised); border: 1px solid var(--line); border-right: none;
  color: var(--text); text-decoration: none; font-weight: 600; font-size: 13px; white-space: nowrap;
}
.suite-btn svg { width: 15px; height: 15px; color: var(--accent); }
.suite-btn:hover { background: var(--hover); }
.suite-caret {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 34px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--raised); border: 1px solid var(--line); color: var(--muted); cursor: pointer;
}
.suite-caret:hover { background: var(--hover); color: var(--text); }
.suite-caret svg { width: 14px; height: 14px; }
.suite-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60; min-width: 220px;
  background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg); padding: 5px; display: flex; flex-direction: column; gap: 2px;
}
.suite-menu-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none; font-size: 13.5px; font-weight: 500;
}
.suite-menu-item:hover { background: var(--hover); }
.suite-menu-item.disabled { color: var(--faint); cursor: default; }
.smi-ico { display: inline-flex; width: 18px; color: var(--accent); }
.suite-menu-item.disabled .smi-ico { color: var(--faint); }
.smi-ico svg { width: 18px; height: 18px; }
.smi-soon { margin-left: auto; font-family: var(--font-mono); font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); border: 1px solid var(--line); border-radius: 20px; padding: 1px 6px; }

.modbar-div { width: 1px; height: 26px; background: var(--line); flex: none; }

.modbar-title { font-family: var(--font-display); font-size: 18px; font-weight: 600; letter-spacing: -.02em; line-height: 1.05; white-space: nowrap; flex: none; }
.modbar-title small { display: block; font-family: var(--font-mono); font-size: 9px; font-weight: 400; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); margin-top: 1px; }

.modbar-tabs { display: flex; align-items: center; gap: 3px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.modbar-tabs::-webkit-scrollbar { display: none; }
.modbar-tab {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 7px 13px; border-radius: var(--radius-sm);
  color: var(--muted); text-decoration: none; font-size: 13.5px; font-weight: 500;
  transition: background .12s, color .12s;
}
.modbar-tab:hover { background: var(--raised); color: var(--text); }
.modbar-tab.active { background: var(--accent-soft); color: var(--accent); }
.tab-count { font-family: var(--font-mono); font-size: 10.5px; background: var(--raised); color: var(--muted); padding: 0 6px; border-radius: 20px; }
.modbar-tab.active .tab-count { background: rgba(224,154,74,.22); color: var(--accent); }

/* Rechte Bereichsaktionen */
.modbar-right .search { flex: 0 0 auto; width: 38px; max-width: 260px; transition: width .18s ease; }
.modbar-right .search:focus-within { width: 260px; }
.modbar-right .search input { cursor: pointer; }
.modbar-right .search:focus-within input { cursor: text; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  border-radius: var(--radius-sm); background: var(--raised); border: 1px solid var(--line);
  color: var(--muted); text-decoration: none; cursor: pointer;
}
.icon-btn:hover { background: var(--hover); color: var(--text); }
.icon-btn svg { width: 17px; height: 17px; }
.api-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); flex: none; }
.api-dot.off { background: var(--danger); }

/* Suite-Launcher: schlanke Bar (nur globale Aktionen) */
.modbar.is-suite { background: transparent; border-bottom-color: transparent; }

/* Rechnungs-Lifecycle-Zeilen (Stufe 2) */
.rech-inv .inv-row { display: flex; align-items: center; gap: 10px; }
.inv-row .inv-name { flex: 0 0 auto; min-width: 108px; }
.inv-row .inv-state { flex: 1; color: var(--muted); font-size: 12px; white-space: nowrap; }
.inv-row .inv-amt { flex: 0 0 auto; color: var(--text); }
.inv-row .inv-btns { flex: 0 0 auto; display: flex; gap: 6px; margin-left: 6px; }
.inv-row .inv-btns .btn.small { padding: 4px 9px; font-size: 12px; }

/* ============ Rechnung-Modul (Redesign) ============ */
.rk-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin: 4px 2px 20px; flex-wrap: wrap; }
.rk-title { font-family: var(--font-display); font-size: 23px; font-weight: 600; letter-spacing: -.02em; }
.rk-sub { color: var(--muted); font-size: 13.5px; margin-top: 3px; }
.rk-monthnav { display: flex; align-items: center; gap: 10px; }
.rk-month { font-family: var(--font-display); font-weight: 600; min-width: 128px; text-align: center; }
.btn.icon { width: 34px; padding: 0; justify-content: center; }

.rk-kpi { margin-bottom: 18px; }

/* Master-Detail */
.rk-split { display: grid; grid-template-columns: 340px 1fr; gap: 16px; align-items: start; }
@media (max-width: 920px) { .rk-split { grid-template-columns: 1fr; } }
.rk-list { display: flex; flex-direction: column; gap: 7px; }
.rk-litem {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; cursor: pointer;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font: inherit;
  transition: border-color .12s, background .12s;
}
.rk-litem:hover { border-color: var(--accent-dim); background: var(--raised); }
.rk-litem.active { border-color: var(--accent); background: var(--accent-soft); }
.rk-li-main { flex: 1; min-width: 0; }
.rk-li-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rk-li-sub { color: var(--muted); font-size: 12px; margin-top: 3px; }
.rk-nopartner { color: var(--p-hoch); }
.rk-li-amt { font-family: var(--font-mono); font-size: 13px; white-space: nowrap; }
.rk-li-right { display: flex; align-items: center; gap: 12px; flex: none; }

/* Detail-Panel */
.rk-detail { min-height: 200px; }
.rk-detail .panel-head .ph-right { margin-left: auto; font-weight: 400; color: var(--muted); font-size: 12px; font-family: var(--font-mono); }
.rk-tbl-wrap { overflow-x: auto; padding: 6px 8px; }
table.rk-tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.rk-tbl th { text-align: left; color: var(--muted); font-weight: 600; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; padding: 9px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.rk-tbl th.num, .rk-tbl td.num { text-align: right; font-family: var(--font-mono); white-space: nowrap; }
.rk-tbl td { padding: 11px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.rk-tbl tbody tr:last-child td { border-bottom: none; }
.rk-tbl tbody tr:hover { background: var(--raised); }
.rk-tbl tfoot td { font-weight: 700; border-top: 1px solid var(--line); border-bottom: none; }
.rk-tbl .rk-muted { color: var(--faint); }
.rk-tbl.rk-kv td:first-child { color: var(--muted); }
.rk-warnbox { margin: 4px 14px; font-size: 12.5px; color: var(--p-hoch); background: rgba(230,162,86,.08); border: 1px solid rgba(230,162,86,.3); border-radius: var(--radius-sm); padding: 9px 12px; }
.rk-detail-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px 17px; border-top: 1px solid var(--line); flex-wrap: wrap; }
.rk-hint { color: var(--faint); font-size: 12px; }

/* Toolbar (Tab Rechnungen) */
.rk-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
/* Die Leiste ueber der Rechnungsliste, wenn Belege ausgeblendet sind. Sie
   steht ueber der Liste und nicht in einer Ecke: eine gekuerzte Liste muss
   sagen, dass sie gekuerzt ist. */
.rk-versteckt-leiste { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px; padding: 8px 12px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--raised); color: var(--faint); font-size: 12.5px; }
.rk-search { position: relative; flex: 0 1 320px; }
.rk-search input { width: 100%; }
.seg { display: inline-flex; gap: 4px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px; }

/* Status-Badges (Rechnung) */
.badge.rk-draft   { color: var(--s-offen);   background: rgba(154,168,192,.14); }
.badge.rk-posted  { color: var(--s-neu);     background: rgba(91,168,234,.14); }
.badge.rk-paid    { color: var(--s-geloest); background: rgba(78,197,140,.14); }
.badge.rk-cancel  { color: var(--s-zu);      background: rgba(92,102,120,.16); }
/* ---------- Aufgehoben und Gutschrift (104.3, 15.09.2026) ----------
   „Aufgehoben" muss sich auf den ersten Blick von „Gebucht" unterscheiden —
   daran haengt der Mahnfehler: eine aufgehobene Rechnung, die neben den
   offenen steht und gleich aussieht, wird irgendwann angemahnt. Deshalb die
   WARNFARBE und nicht das Grau der abgebrochenen: abgebrochen ist
   folgenlos, aufgehoben ist ein Vorgang mit zwei Belegen.
   Die Gutschrift selbst traegt dieselbe Familie, heller — sie ist die
   andere Seite desselben Vorgangs, nicht sein Problem. */
/* ---------- Aufgehoben ist ERLEDIGT, nicht dringend (109, 15.09.2026) ----------
   Hier stand die Warnfarbe — dieselbe wie an der Gutschrift, nur kraeftiger.
   Christians Einwand: „die zwei Abzeichen sehen gleich aus, also muss man
   lesen, um zu unterscheiden. Ich will es sehen, nicht lesen."

   Die Rollen sind verschieden:
     Gutschrift   ein GUELTIGER Beleg, der aufhebt        → bleibt farbig
     Aufgehoben   ein ERLEDIGTER Beleg, der aufgehoben wurde → gedaempft

   Die Daempfung wird BENUTZT und nicht erfunden: `.rk-cancel` traegt sie
   seit je (`--s-zu`, das Grau der geschlossenen Vorgaenge). Ein erledigter
   Beleg ist ein erledigter Beleg — dass „Abgebrochen" und „Aufgehoben"
   dieselbe Daempfung tragen, ist richtig; sie unterscheiden sich im WORT,
   nicht im Gewicht. */
.badge.rk-reversed   { color: var(--s-zu);     background: rgba(92,102,120,.16); }
.badge.rk-gutschrift { color: var(--s-arbeit); background: rgba(232,168,72,.14); }

/* ---------- Die Belegzeile, zweizeilig (108) ----------
   NUR fuer die Rechnungsliste (`.rk-beleg`): `.rk-li-sub` gehoert auch den
   Angeboten, Kontakten, Geraeten und dem Lager — eine Aenderung daran waere
   eine Aenderung an vier Listen, von denen drei niemand gemessen hat. */
.rk-beleg .rk-li-name { font-family: var(--font-mono); font-size: 13px; }
.rk-beleg .rk-li-sub { display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; margin-top: 5px; }
/* Der Kundenname weicht als EINZIGER — er kuerzt. Die Nummer steht oben
   allein und kann gar nicht mehr gedraengt werden. */
.rk-li-kunde { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Die ganze Zeile tritt zurueck, nicht nur das Abzeichen (109): abgehakt
   sieht man an der Zeile, nicht an einem Woertchen darin. Die NUMMER bleibt
   dabei voll lesbar — sie ist der Grund, warum jemand die Zeile ansieht. */
.rk-litem.erledigt { background: var(--surface); }
.rk-litem.erledigt .rk-li-kunde,
.rk-litem.erledigt .rk-li-amt { color: var(--faint); }
.rk-litem.erledigt .rk-li-name { color: var(--muted); }
.rk-litem.erledigt.active .rk-li-name { color: var(--text); }
/* Der Bezug steht NEBEN dem Abzeichen, nicht darin: er ist eine Auskunft
   ueber den anderen Beleg, kein Zustand dieses hier. */
.rk-bezug { font-size: 11.5px; color: var(--faint); white-space: nowrap; }
.rk-bezug-warn { color: var(--s-arbeit); }

/* Buttons: danger */
.btn.danger { background: rgba(240,96,107,.14); border-color: rgba(240,96,107,.4); color: var(--p-kritisch); }
.btn.danger:hover { background: rgba(240,96,107,.22); border-color: var(--p-kritisch); }

/* Bestätigungs-Modal */
/* ---------- Ein Dialog kann sich nicht aussperren (116, 16.09.2026) ----------
   `overflow-y: auto` ist die ganze Behebung. Vorher stand hier nichts, und
   `.rk-modal` hat — anders als `.modal` — weder Hoehengrenze noch rollenden
   Rumpf. Gemessen am 16.09.2026, Fenster 600 px, mit einem weit gezogenen
   Textfeld: ab 23 Zeilen stand die Knopfleiste ausserhalb des Bildes (bei
   900 px ab 38), und nichts rollte — die Knoepfe waren unerreichbar. Escape
   half, aber Escape wirft weg, was man geschrieben hat.

   Das bestand schon vor 115: dreizehn Felder tragen `resize: vertical`, wer
   weit genug zieht, sperrt sich aus.

   ---- Warum nicht die Bauart von `.modal` ----
   Hoehengrenze plus getrennter Kopf/Rumpf/Fuss waere die gruendlichere
   Loesung. Sie braucht aber ein rollendes `.rk-modal-body` in jedem Dialog,
   und gemessen haben von 20 Dialogen der Familie B genau ZWEI eines — 18
   haetten Markup bekommen muessen, fuer einen Fall, den eine Deklaration
   schliesst.

   ---- `place-items: center` BLEIBT, und das ist gemessen ----
   Der naheliegende Reflex waere `start center`: zentrierter Inhalt, der
   hoeher ist als sein Behaelter, wird in aelteren Flexbox-Fassungen oben
   abgeschnitten. Im Raster tritt das hier NICHT ein — gemessen: bei 60
   Zeilen steht die Kasten-Oberkante bei 20 px und ist erreichbar, bei 12
   Zeilen steht der Dialog weiter mittig (Oberkante 103 = genau die Mitte).
   `start center` haette also 20 Dialoge sichtbar verschoben, ohne etwas zu
   beheben. `browser/dialog-erreichbar.js` misst beides nach. */
.rk-modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: grid; place-items: center; overflow-y: auto; z-index: 200; padding: 20px; }
.rk-modal { width: min(460px, 94vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: 22px 24px; }
.rk-modal h3 { font-family: var(--font-display); font-size: 18px; font-weight: 600; margin-bottom: 8px; }
.rk-modal-body { color: var(--muted); font-size: 14px; line-height: 1.55; }
.rk-modal-body b { color: var(--text); }
.rk-modal-warn { margin-top: 14px; font-size: 13px; color: var(--p-hoch); background: rgba(230,162,86,.08); border: 1px solid rgba(230,162,86,.3); border-radius: var(--radius-sm); padding: 10px 12px; }
.rk-modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.rk-kpi { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 680px) { .rk-kpi { grid-template-columns: 1fr; } }
.rk-detail-foot .btn { margin-right: 0; }
/* ---------- Der Nebenweg (65.2.2, 07.09.2026) ----------
   „Aus der Liste nehmen" steht bei einem gebuchten Beleg neben
   „Stornieren (Gutschrift)" — und die beiden tun voellig
   Verschiedenes: Stornieren erzeugt einen ZWEITEN Beleg, Ausblenden
   aendert am Beleg gar nichts.

   Die Absetzung ist deshalb ORT, nicht nur Farbe: eigene Gruppe ganz
   rechts, durch einen Trenner abgesetzt, ohne `danger`. Und der
   Beitext sagt, was sonst zu raten waere — wer „aus der Liste nehmen"
   fuer „loeschen" haelt, sucht den Beleg spaeter in Odoo und haelt ihn
   fuer verloren. */
.rk-foot-neben { display: flex; align-items: center; gap: 8px;
  margin-left: auto; padding-left: 14px; border-left: 1px solid var(--line); }
.rk-foot-neben .rk-hint { font-size: 11px; }
.rk-detail-sub { padding: 0 17px 16px; }
.rk-detail-sub .btn { color: var(--muted); }

/* ============ Kontakte-Modul ============ */
.kk-ico { width: 32px; height: 32px; border-radius: 8px; flex: none; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent); }
.kk-ico svg { width: 17px; height: 17px; }
.rk-litem .kk-ico + .rk-li-main { margin-left: 0; }
.kf { padding: 16px 17px; display: grid; grid-template-columns: 1fr 1fr; gap: 13px 16px; }
.kf-row { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); min-width: 0; }
.kf-row.full { grid-column: 1 / -1; }

/* ---------- Die Anschrift ist kein historischer Wert (101c, 15.09.2026) ----------
   Steht zwischen den Adressfeldern und dem Speichern-Knopf, also dort, wo die
   Entscheidung faellt. Der Ton ist der der Hinweise (`--p-hoch`), nicht der der
   Warnungen: es ist kein Fehler, einen umfirmierten Kunden umzubenennen. */
.kf-anschrift-warn {
  font-size: 12px; line-height: 1.5; color: var(--muted);
  border-left: 3px solid var(--p-hoch); padding: 2px 0 2px 10px;
}
.kf-anschrift-warn b { color: var(--text); }
.kf-anschrift-warn ul { margin: 6px 0 6px; padding-left: 18px; }
.kf-anschrift-warn li { margin: 2px 0; }
/* Rueckmeldung unter der Steuernummer: welches Etikett das Dokument benutzt.
   Ohne sie merkt man einen Tippfehler erst auf der Rechnung beim Kunden. */
.kf-steuer-hint { font-size: 11px; color: var(--faint); line-height: 1.45; }
.kf-steuer-hint.warn { color: var(--accent); }
.kf-row > span { font-weight: 500; }
.kf-row input:not([type=checkbox]), .kf-row select {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); padding: 8px 10px; font: inherit; font-size: 13px; outline: none;
}
.kf-row input:focus, .kf-row select:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--ring); }
.kf-row.kf-check { grid-column: 1 / -1; flex-direction: row; align-items: center; gap: 9px; color: var(--text); font-size: 13px; }
.kf-row.kf-check input { width: 18px; height: 18px; }

/* ---- Kontakt-Hub: verknüpfte Tickets/Rechnungen ---- */
.kk-links { padding: 10px 17px 4px; display: flex; flex-direction: column; gap: 14px; }
.kk-links-load, .kk-links-empty { color: var(--faint); font-size: 12.5px; }
.kk-link-h { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); font-weight: 600; margin-bottom: 6px; }
.kk-link { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: var(--surface); text-decoration: none; color: var(--text); font-size: 13px; margin-bottom: 5px; }
a.kk-link:hover { border-color: var(--accent-dim); background: var(--raised); }
.kk-link .kk-link-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.kk-link .mono { flex: none; }

/* ============ Rechnungs-Vorschau (gebrandet, druckbar) ============ */
.rk-preview-bg { position: fixed; inset: 0; z-index: 300; background: rgba(0,0,0,.6); display: flex; flex-direction: column; }
.rk-preview-toolbar { display: flex; align-items: center; gap: 10px; padding: 12px 18px; background: var(--surface); border-bottom: 1px solid var(--line); flex: none; }
.rk-preview-scroll { flex: 1; overflow: auto; padding: 28px 16px; display: flex; justify-content: center; }
/* >>>>>> DOKUMENT-CSS ANFANG — nicht verschieben <<<<<<
   Alles zwischen diesen Markern wird von services/dokument-html.js aus dieser
   Datei HERAUSGESCHNITTEN und in das gerenderte PDF eingebettet. Damit gibt es
   EIN Stylesheet fuer Vorschau und Dokument: was hier geaendert wird, aendert
   beides. Genau darum geht der Weg ueber HTML statt ueber Zeichenkoordinaten.

   Regeln hier drin muessen ohne den Rest der Suite funktionieren:
     - keine var(--…) ausser mit Rueckfallwert (Chromium kennt :root hier nicht)
     - keine Farben aus dem dunklen Thema — das Dokument ist weisses Papier
     - nichts, was nur im Browser gilt (:hover, cursor, transition)
   Was zum Dialog gehoert und nicht aufs Papier, steht AUSSERHALB. */
/* ---------- Der Rand oben gilt auf JEDER Seite (78.3, 08.09.2026) ----------
   Gemessen am erzeugten PDF von Beleg #53, ueber Gotenberg:

     Seite 1   Tabellenkopf-Oberkante  133,9 mm   (der Briefkopf steht darueber)
     Seite 2   Tabellenkopf-Oberkante    0,0 mm   <- direkt an der Blattkante

   Der Rand kam bis heute allein aus `padding: 14mm …` am `.inv-doc`. Padding
   gilt an der ELEMENTKANTE, nicht je gedruckter Seite — auf Seite 1 wirkt es,
   ab Seite 2 nicht mehr. Ein `@page`-Regelwerk gab es INNERHALB der
   Dokument-Marker gar nicht, und Gotenberg wird mit `margin: 0` gerufen.

   ---- Es gibt ein zweites `@page`, und es ist kein Konflikt ----
   Weiter unten in dieser Datei steht `@page { size: A4; margin: 0 }` —
   AUSSERHALB der Marker, also nur fuer den Browserdruck (`window.print()`).
   Ins PDF wird es nicht eingebettet; dort gilt allein die Regel hier.

   Im Browser gelten beide, und das spaetere gewinnt: `margin: 0` steht nach
   diesem Block, der Browserdruck bleibt also unveraendert. Das ist gemessen,
   nicht angenommen — aber es haengt an der REIHENFOLGE in dieser Datei. Wer
   die Bloecke umsortiert, aendert das Verhalten von `window.print()`, ohne
   eine Regel anzufassen. Deshalb steht es hier.

   Auf Seite 1 faellt es nicht auf, weil dort der Briefkopf steht. Deshalb war
   es unsichtbar — und in der VORSCHAU gibt es keine Seite 2, also gab es das
   Problem dort nie (77.5).

   ---- Warum `:first` auf 0 und nicht das padding weg ----
   Das padding bleibt stehen: es traegt den Rand in der Vorschau, die kein
   `@page` kennt. Naehme man es weg und ersetzte es durch `@page margin-top`,
   verloere die Vorschau ihren oberen Rand — ein NEUER Unterschied zwischen
   Vorschau und PDF, waehrend 77 genau diese Liste aufnimmt. Also: Seite 1
   behaelt ihren Rand aus dem padding, `:first` neutralisiert die
   Blattmarge dort, und die Folgeseiten bekommen sie.

   ---- Der Preis, gedruckt gemessen, mit Positivkontrolle ----
   Es ist NICHT „kostet keine Seite". Es kostet 7 mm je Folgeseite:

     heute                     2 Seiten, Kopf S.2 0,0 mm, Fussluecke 43,9 mm
     mit dieser Regel          2 Seiten, Kopf S.2 6,9 mm, Fussluecke 37,0 mm
     POSITIVKONTROLLE (74)     3 SEITEN — der Messer unterscheidet

     Zwischenstand, gemessen und verworfen: mit 14 mm sass der Kopf bei
     14,0 mm und die Fussluecke bei 29,9 mm. Christian: zu weit nach unten,
     die Haelfte. 7 mm ist die Halbierung der BLATTMARGE, nicht des
     padding — Seite 1 bleibt unveraendert bei 133,9 mm.

   An Beleg #53 reicht der Vorrat. An einem knapperen Beleg kann dieselbe
   Regel eine Seite kosten. Das ist die Aussage, die bei 74 falsch
   verallgemeinert wurde, hier andersherum gesagt.

   ---- Und wie gross der Vorrat ist, als ZAHL (78.6) ----
   Gesucht, nicht gerechnet: die Positionszahl waechst, bis die dritte Seite
   entsteht — mit und ohne die Regel.

     ohne diese Regel   die dritte Seite entsteht ab 39 Positionen
     mit dieser Regel   die dritte Seite entsteht ab 38 Positionen
     ------------------------------------------------------------
     Preis              EINE einzeilige Position Vorrat

   Eine einzeilige Position ist rund 8,2 mm hoch (74) — die 7 mm Blattmarge
   passen darunter. Wer die Regel aendert, misst mit
   `tests/browser/mess-druckwege.js` nach, statt zu rechnen.

   Hier steht EINE Zahl. Mit 14 mm waren es 37 Positionen und damit zwei
   Vorrat; diese Fassung ist verworfen, und ihre Zahl steht deshalb nicht
   daneben — eine alte Zahl neben einer neuen ist eine Stelle, an der jemand
   die falsche liest.

   ---- Was NICHT mitgenommen wird ----
   Der untere Rand. Auf Seite 1 endet die unterste Flaeche bei 292,1 mm, also
   4,9 mm vor der Kante. Gemessen: `margin-bottom` auf allen Seiten kostet ein
   drittes Blatt, nur auf Folgeseiten faellt die Fussluecke auf 0. Der Fuss
   bleibt, wie er ist (49.3). Der Befund steht offen in CONTEXT.md. */
@page { margin-top: 7mm; }
@page :first { margin-top: 0; }

/* Nur fuer die Messung vor dem Drucken: dieselbe Hoehenregel wie im Druck.
   Begruendung an `rkFussVorDruck` in app.js. */
.inv-doc.inv-druckmass { min-height: 0; }
/* ---------- Hintergrund weiss, Schrift schwarz (69.2.2) ----------
   Der Haupttext war `#1b1f27` (16,51:1) und ist jetzt `#000` (21:1).

   Die drei Grautoene BLEIBEN: `#3D434E` (Positionen, 9,95:1),
   `#6A7280` (Absender und Hinweise, 4,85:1), `#4A5160` (Fusszeile,
   7,96:1). Sie tragen Struktur, keine Zierde — alles auf Schwarz
   macht die Rechnung nicht klarer, sondern flacher, und alle drei
   reichen ueber 4,5:1.

   Das Zierwort „RECHNUNG" (`#b9bec7`, 1,87:1) bleibt ebenfalls: es ist
   Dekoration, kein Text, und wird nicht gelesen sondern gesehen. */
.inv-doc { display: flex; flex-direction: column; width: 210mm; min-height: 290mm; background: #fff; color: #000; box-shadow: 0 10px 40px rgba(0,0,0,.45); padding: 14mm 18mm 10mm; box-sizing: border-box; font-size: 12px; line-height: 1.5; align-self: flex-start; }
.inv-doc .empty { color: #6a7280; }

/* ---------- Die archivierte Datei statt eines Neubaus (101a, 15.09.2026) ----------
   Ein versendeter Beleg wird nicht mehr gebaut, sondern geholt — was hier
   steht, IST schon ein Blatt. Deshalb kein `.inv-doc`: dessen weisser Grund,
   Seitenrand und Schatten laegen um ein PDF herum, das beides selbst mitbringt.
   Die Breite bleibt die des Blattes, damit die Ansicht nicht springt, wenn man
   zwischen einem Entwurf und einer versendeten Rechnung wechselt. */
.inv-archiv { display: flex; flex-direction: column; width: 210mm; align-self: flex-start; }
.inv-archiv-hinweis {
  margin: 0 0 10px; padding: 10px 14px; border-radius: 8px;
  background: var(--surface); color: var(--text); border: 1px solid var(--line);
  font-size: 13px; line-height: 1.5;
}
/* Hoehe fest: ein PDF im iframe hat keine Hoehe, die der Rahmen erben koennte
   — ohne Angabe blieben 150 px uebrig. 290 mm ist die Hoehe des Blattes
   daneben, damit die Ansicht beim Wechsel nicht springt. */
.inv-archiv-blatt { width: 100%; height: 290mm; border: 0; background: #fff;
  box-shadow: 0 10px 40px rgba(0,0,0,.45); }
/* Kopf: Firmenzeile rechts oben, Farbbalken, großes RECHNUNG */
/* ---------- Der Briefkopf ist gespiegelt (07.09.2026) ----------
   Bis heute standen Logo und Firmenname beide RECHTS: `flex-end` mit
   dem Logo davor. Jetzt der Name am linken Rand, das Logo am rechten.

   `space-between` und nicht nur die Reihenfolge getauscht — sonst
   staenden beide links und rechts bliebe leer. Die Kanten fallen damit
   mit dem Innenrand von `.inv-doc` zusammen (18 mm), also mit der
   Absenderzeile darunter: eine Kante, die 2 mm anders liegt als der
   Text darunter, faellt auf (Lehre aus 19.2). */
.inv-corpline { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11px; font-weight: 700; color: #1b1f27; }
.inv-logo { max-height: 40px; max-width: 160px; }
.inv-rule { height: 3px; margin: 4px 0 10mm; }
.inv-word { font-family: var(--font-display, 'Space Grotesk', sans-serif); font-size: 40px; font-weight: 700; letter-spacing: .06em; color: #b9bec7; line-height: 1.1; margin-bottom: 8mm; }
/* Empfänger links, Metadaten rechts */
.inv-addr-meta { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; margin-bottom: 10mm; }
.inv-sender { font-size: 9px; color: #6a7280; text-decoration: underline; margin-bottom: 5mm; }
.inv-recip div:first-child { font-weight: 700; }
.inv-metabox { font-size: 11px; min-width: 62mm; }
.inv-metabox > div { display: flex; justify-content: space-between; gap: 14px; }
.inv-metabox > div > span { font-weight: 700; }
.inv-nr { font-weight: 800; font-size: 13px; letter-spacing: .02em; margin-bottom: 5mm; }
/* Bezeichnung: direkt unter der Nummer und als TITEL erkennbar — fett und
   größer als der Fließtext. „RECHNUNG NR. 2026-0004" allein sagt nicht, was
   abgerechnet wird; die Zeile darunter tut es, also darf sie nicht wie eine
   Fußnote aussehen. */
.inv-betreff {
  font-size: 14px; font-weight: 700; color: #1b1f27;
  margin: -3mm 0 5mm; letter-spacing: .01em;
}
/* ---------- Die vier Absatzabstaende, festgeschrieben (77.6b, 13.09.2026)
   ----------
   Vier Abstaende an drei Klassen kamen bisher NICHT aus diesem Block,
   sondern aus der Browservorgabe fuer `<p>` (1em = 12px bei 12px Schrift,
   gemessen): `.inv-intro` oben, `.inv-pay`/`.inv-close`/`.inv-gruss` unten.

   Das PDF bekam sie, die Vorschau nicht — dort raeumt `* { margin: 0 }`
   (style.css:125, ausserhalb dieses Blocks) auf. Vier der fuenfzehn
   Unterschiede zwischen Vorschau und PDF hingen daran (77.5).

   Warum EINZELN und nicht `.inv-doc p { margin-block: 1em }`: gemessen am
   12.09.2026 nimmt die pauschale Regel vier Unterschiede und erzeugt vier
   neue — sie gewinnt gegen die Stellen, an denen dieser Block den Abstand
   SELBST setzt (`.inv-pay { margin-top: 5mm }`). Vier einzelne Werte nehmen
   vier und erzeugen keinen.

   Warum px und nicht mm, obwohl der Block sonst mm nimmt (siehe `.inv-gruss`
   weiter unten): diese vier sind keine gewaehlte Papierdistanz, sondern die
   Vorgabe, die der PDF-Weg seit je hatte. Hier steht, was A traegt — und
   zwar in der Einheit, in der es gemessen wurde. */
.inv-intro { margin-bottom: 5mm; margin-top: 12px; }
/* Positionstabelle: dunkler Kopf, Preisspalten in Akzentfarbe, Unterpunkte ohne Beträge */
.inv-tbl { width: 100%; border-collapse: collapse; }
/* Der ganze Tabellenkopf einfarbig (69.3.1): weisse Schrift auf #4a4a4a
   sind 8,86:1. Zwei der fuenf Zellen trugen bis zum 07.09.2026 die
   Akzentfarbe als Inline-Style — 4,82:1 und eine Fundstelle, die eine
   Suche verfehlt. Beides ist fort; der Grabstein steht in
   shared/inv-doc.js. */
.inv-tbl th { text-align: left; font-size: 11px; color: #fff; background: #4a4a4a; padding: 6px 8px; }
.inv-tbl td { padding: 6px 8px; border-bottom: 1px solid #e5e7eb; vertical-align: top; }
.inv-tbl .r { text-align: right; white-space: nowrap; }
.inv-tbl .c { text-align: center; white-space: nowrap; }
/* Positionstext: Kopfzeile, Themen, Unterpunkte. Dieselbe Gliederung wie im
   Arbeitsbericht — wer den kennt, erkennt die Rechnung wieder. */
.inv-pos-kopf { font-weight: 600; }
.inv-pos-frei { color: #3d434e; }
/* ---------- GRABSTEIN: Der Textblock atmet (74, 07.09.2026) ----------
   Hier standen `margin-top: 3px` an den Gliederungspunkten und `8px` an der
   Themenzeile. Sie sind wieder fort, und der Befund, der sie veranlasst hat,
   steht weiter: gemessen im Druckmedium haelt der Fliesstext ueber der
   Tabelle 5,0 mm zwischen zwei Absaetzen, die Gliederungspunkte 0,0 mm. Die
   Gruppengrenze — „Umsetzung" ueber „Vorbereitung" — sass mit 0,8 mm auf dem
   letzten Punkt der Vorgruppe. Das ist zu dicht, und es bleibt zu dicht.

   ---- Warum es trotzdem zurueckgenommen wurde ----
   Am ECHTEN Beleg #53 gemessen, am Dienst:

     vor 74     2 Seiten, Fussluecke 43,9 mm
     mit 74     3 SEITEN, Fussluecke 274,9 mm  -> der Fuss steht allein
                                                  auf der dritten Seite

   Genau das, was 49.3 abgestellt hat. Und der Riegel in
   `services/fuss-luecke.js` greift dabei NICHT: er verwirft eine Luecke, die
   eine Seite erzeugt — hier war der Beleg schon OHNE Luecke dreiseitig.

   ---- Der Fehler in der Messung davor ----
   Der Preis wurde an einem selbstgebauten Beleg gemessen: 8 Positionen,
   6 Themen, 18 Punkte, Reserve 9 -> 7 Fuellpositionen. Daraus wurde „kostet
   keine Seite" — eine Aussage ueber EINEN Beleg, gehalten fuer eine ueber
   alle. Dieselbe Verwechslung, gegen die `test-standsperre.js` fuenf
   Tabellen einzeln prueft, und die 58.2 benannt hat.

   Beleg #53 traegt 12 Themen und 23 Punkte — mehr als der Messbeleg. Und
   „Reserve" war die falsche Groesse: die Fussluecke ist freier Platz auf der
   LETZTEN Seite, nicht Platz fuer mehr Inhalt. Was wirklich zaehlt, ist der
   Abstand bis zur naechsten UNTEILBAREN Position (`break-inside: avoid`,
   23.2.2). Eine Position, die nicht mehr passt, wandert ganz.

   ---- Die Stufen, gemessen an Beleg #53 ----
     0px / 3px  (vor 74)   2 Seiten, 43,9 mm
     0px / 5px             2 Seiten, 41,3 mm
     1px / 4px             2 Seiten, 41,3 mm   <- traegt noch
     1px / 6px             3 Seiten
     0px / 8px             3 Seiten            <- schon die Themen allein
     3px / 8px  (74)       3 Seiten

   1px/4px sind 0,26 und 1,06 mm. Was traegt, ist nicht mehr zu sehen; was
   zu sehen ist, traegt nicht. Ein Abstand ist an diesem Beleg der falsche
   Hebel — die Gruppengrenze braucht etwas, das keine Hoehe kostet.

   Die Messwerkzeuge bleiben: `tests/browser/mess-textblock.js`,
   `mess-textblock-varianten.js`, `bild-textblock.js`. Sie haben den Befund
   gefunden; nur ihr Messbeleg war zu freundlich. */
/* ---------- Die Gruppengrenze traegt Staerke, nicht Abstand (74.2, 08.09.2026)
   Der Befund aus 74 steht im Grabstein darueber: „Umsetzung" sass mit 0,8 mm
   auf dem letzten Punkt von „Vorbereitung", und die Ebene, die die Gliederung
   traegt, war die am schwersten zu sehende. Der Weg ueber den ABSTAND ist
   erschoepft — was traegt, ist nicht mehr zu sehen.

   Staerke kostet keine Hoehe. Gemessen am ECHTEN Beleg #53 (nicht an einem
   gebauten — Reihe D), mit einer Positivkontrolle, die 74 nachstellt und
   drei Seiten liefern MUSS, damit der Lauf ueberhaupt etwas unterscheidet:

     heute (Thema 400)                2 Seiten, 43,9 mm, Fuss bei 286,0 mm
     600                              2 Seiten, 43,9 mm, Fuss bei 286,0 mm
     700                              2 Seiten, 43,9 mm, Fuss bei 286,0 mm
     GEGENPROBE zurueck auf 400       2 Seiten, 43,9 mm, Fuss bei 286,0 mm
     POSITIVKONTROLLE (74)            3 SEITEN, 274,9 mm

   Kein Wert verschoben. Fettere Schrift KANN umbrechen, wo vorher nicht
   umgebrochen wurde — hier tut sie es nicht, und das ist gemessen.

   ---- Warum 600 und keine andere ----
   Gemessen mit `getComputedStyle` am erzeugten Beleg, nicht aus dem
   Stylesheet geschlossen (`.inv-pos-thema` hatte gar keine Angabe, und ein
   `<th>` ist ohne Regel fett). Der Beleg traegt VIER Staerken:

     400   Fliesstext, Anschrift, Unterpunkte, und bisher die Themenzeile
     600   der Positionstitel (.inv-pos-kopf) und „gez." (.inv-gez)
     700   Empfaenger, Tabellenkopf, Summenzeile, Briefkopfname
     800   die Rechnungsnummer — die staerkste Stelle im Dokument

   600 ist die einzige, die passt. 700 machte die Themenzeile STAERKER als
   ihre eigene Kopfzeile darueber und drehte die Ordnung um; 800 gehoert der
   Nummer. Ein fuenfter Wert (500) kaeme im Dokument sonst nirgends vor.

   ---- Was das kostet, ausgesprochen ----
   Positionstitel und Themenzeile sind jetzt GLEICH stark und gleich
   schwarz. Sie unterscheiden sich noch durch die Stelle (erste Zeile der
   Zelle) und den Inhalt (Datum, Ticketnummer). Die Ordnung ist damit
   flacher — drei Ebenen auf zwei Staerken statt auf drei. Der Gewinn ist
   die Trennung nach UNTEN: gegen die grauen 400er-Punkte steht die
   Themenzeile jetzt sichtbar ab, und genau die Grenze war der Befund.

   Nur die Themenzeile. Die Unterpunkte bleiben unveraendert — waere alles
   hervorgehoben, waere nichts hervorgehoben. */
.inv-pos-thema { margin-top: 3px; font-weight: 600; }
.inv-pos-subs { margin: 1px 0 0; padding-left: 14px; list-style: none; }
.inv-pos-subs li { position: relative; padding-left: 9px; color: #3d434e; }
.inv-pos-subs li::before {
  content: '·'; position: absolute; left: 0; top: -1px; font-weight: 700; color: #8b929f;
}
/* Eine Position darf beim Drucken nicht mitten durchgerissen werden.
   Gemessen am erzeugten PDF (01.09.2026, 23.2.2): bei neun Fuellpositionen
   plus einer hohen reisst die hohe Position ohne diese Regel auseinander,
   mit ihr nicht. Die Wirkung haengt am Grenzfall — bei zwoelf gleich hohen
   Positionen faellt keine an die Seitengrenze, und dann misst eine
   Gegenprobe zwischen „mit" und „ohne" nichts als sich selbst. */
.inv-tbl tr { break-inside: avoid; page-break-inside: avoid; }
/* Der Tabellenkopf wiederholt sich auf Folgeseiten. Chromium tut das fuer
   `thead` von sich aus — die Regel schreibt den Standard fest, statt sich auf
   ihn zu verlassen. Belegt durch die Gegenprobe: mit `table-row-group` steht
   der Kopf nur noch auf Seite 1. */
.inv-tbl thead { display: table-header-group; }
/* ---------- Der Summenblock bleibt beisammen (01.09.2026, 23.2.2) ----------
   Netto, Gesamtsumme und der §19-Satz gehoeren zusammen gelesen. Im HTML sind
   sie GESCHWISTER (`inv-doc.js` 328 und 333), kein gemeinsamer Kasten —
   deshalb reicht `break-inside` an `.inv-sum` nicht: es haelt Netto und
   Gesamtsumme, laesst den §19-Satz aber allein auf die Folgeseite wandern.
   Gemessen bei 26 Fuellpositionen mit 12 bis 16 Unterpunkten in der letzten:
   ohne die Regel faellt der Block in jedem dieser fuenf Faelle auseinander,
   mit ihr in keinem.

   Was hier BEWUSST fehlt: `.inv-tbl { break-after: avoid }`. Die Regel klingt
   richtig — „nach der Tabelle kein Umbruch" —, aber gemessen hebt sie die
   Wirkung der beiden darueber wieder auf: derselbe Bestand faellt mit ihr
   wieder auseinander. Sie stand im Auftrag und ist an der Messung
   gescheitert.

   Dass der Block bei sehr vollen Seiten hinter die letzte Position auf die
   naechste Seite rutscht, bleibt so. Das ist kein Fehler, sondern der einzige
   Platz, an dem er ganz hinpasst. */
.inv-sum {
  break-inside: avoid; page-break-inside: avoid;
  break-after: avoid; page-break-after: avoid;
}
.inv-ku-note { break-before: avoid; page-break-before: avoid; }

.inv-row-sec td { font-weight: 700; }
.inv-row-sub td { border-bottom: none; padding: 2px 8px; font-size: 11px; color: #3d434e; }
.inv-sum { margin: 10px 0 0 auto; width: 62mm; }
.inv-sum > div { display: flex; justify-content: space-between; padding: 5px 8px; }
/* ---------- Der Summenstrich ist rot (69.6.1, 07.09.2026) ----------
   Die dicke Linie ueber der Gesamtsumme, in der Verzierungsfarbe. Sie
   traegt keinen Text, also darf sie satt sein.

   Die duennen Trennlinien ZWISCHEN den Positionen bleiben grau
   (`.inv-tbl td`, #e5e7eb) — sonst haette der Beleg zwanzig rote
   Striche. Rot markiert den Abschluss, nicht jede Zeilentrennung.

   Der Rueckfall auf `#1b1f27` steht hier, weil die Variable nur im
   erzeugten Beleg gesetzt wird: in der Suite-Vorschau, die dieselbe
   Klasse nutzt, gilt weiter das alte Dunkelgrau. */
.inv-sum-total { border-top: 2px solid var(--inv-acc, #1b1f27); font-weight: 700; font-size: 14px; }
.inv-ku-note { margin-top: 6mm; color: #6a7280; font-size: 10.5px; }
/* ---------- ACHTUNG, ABSTAENDE HIER: die Raender fallen NICHT zusammen
   (87, 13.09.2026) ----------
   `.inv-doc` ist ein FLEX-Container — das folgt aus einer ganz anderen
   Entscheidung, naemlich dem Fuss, der an `margin-top: auto` nach unten
   gedrueckt wird. In einem Flex-Container gibt es kein Margin-Collapsing.

   Der Abstand zwischen zwei Bloecken ist hier also die SUMME aus unten und
   oben, nicht das Groessere. Gemessen am echten Beleg:

     Rueckfragen -> Gruss   9,18 mm  =  3,18 unten  +  6 oben

   Wer mit Zusammenfallen rechnet, setzt den Wert doppelt — und das Ergebnis
   sieht plausibel aus, weil es ja groesser wird.

   Und die zwei Sorten Wert an diesen Zeilen sind NICHT dasselbe:
     margin-bottom: 12px   die Browservorgabe fuer <p>, in 77.6b an vier
                           Stellen festgeschrieben, damit Vorschau und PDF
                           dasselbe zeigen. Nicht gewaehlt — uebernommen.
     margin-top: 5mm/6mm   der GEWAEHLTE Abstand dieses Blocks.
   Wer den Abstand aendern will, aendert den gewaehlten. */
.inv-pay { margin-top: 5mm; margin-bottom: 12px; }  /* unten: 77.6b, siehe .inv-intro */
.inv-close { margin-top: 5mm; margin-bottom: 12px; }  /* unten: 77.6b */
/* Ein Absatz zwischen dem Rueckfragen-Satz und dem Gruss (18.08.2026).
   Vorher trennte sie ein einzelnes <br>, und am Beleg klebten sie aneinander.
   In mm, nicht in em: dieser Block wird als PDF gedruckt, und der Abstand soll
   auf dem Papier stimmen und nicht an der Schriftgroesse haengen. */
/* ---------- Mehr Luft vor der Grussformel (87, 13.09.2026) ----------
   Christian: zwischen „Bei Rueckfragen …" und „Mit freundlichen Gruessen"
   soll mehr Platz. 6mm -> 12mm, und zwar aus zwei Gruenden zugleich: es ist
   das Doppelte des heutigen Wertes UND ein Wert, den der Dokumentblock
   schon kennt (er kommt dort bereits einmal vor). Kein sechster erfundener
   Abstand.

   Gemessen im Schlussteil, roh in mm — DIE RAENDER FALLEN HIER NICHT
   ZUSAMMEN, weil `.inv-doc` ein Flex-Container ist (der Fuss haengt an
   `margin-top: auto`). Der Abstand ist also die SUMME:

     §19-Satz  -> Zahlungsziel    5,00 mm
     Zahlung   -> Rueckfragen     8,15 mm   (3,18 unten + 5 oben)
     Rueckfrage-> Gruss           9,18 mm   (3,18 unten + 6 oben)  -> 15,18
     Gruss     -> Fuss            3,18 mm

   Getragen wird der Abstand von DIESEM `margin-top`, nicht von der
   `margin-bottom: 12px` des Rueckfragen-Satzes: die 12px sind die
   Browservorgabe, die 77.6b festgeschrieben hat (an vier Stellen gleich),
   die 6mm sind der gewaehlte Abstand dieses Blocks. Geaendert wird der
   gewaehlte.

   „Ihre Rechnungsstelle" bleibt, wie es ist — Absicht. */
.inv-gruss { margin-top: 12mm; margin-bottom: 12px; }  /* unten: 77.6b */
.inv-gez { font-weight: 600; }
/* Fußzeile: Spalten mit Icon-Kreis, Akzentlinie oben */
/* ============================================================
   Der Fussbereich des Belegs (67.5, 07.09.2026)
   ============================================================
   Christian hat den alten Fuss (orange, CHRYM) und den neuen verglichen:
   „Symbole schoener, Schrift deutlicher, die Aufteilung wirkt unruhig."

   Die Diagnose zur Unruhe war nicht die Textausrichtung — die Spalten sind
   linksbuendig, und das ist richtig. Es waren VIER GETRENNTE kurze Linien
   ueber vier Spalten; im alten Fuss hielt ein durchlaufender Balken sie
   zusammen. Die Ordnung kommt von der Linie, die Lesbarkeit vom
   linksbuendigen Text — im alten hatte man das eine, im neuen das andere.

   ---- Gemessen am erzeugten PDF, VOR der Aenderung ----
     Blockbreite            174,0 mm      Blockhoehe   33,5 mm
     Schrift, alle Zeilen     7,1 pt
     die IBAN                 6,8 pt      <- die KLEINSTE Zeile
     Symbole              12x12 px = 3,2 mm, in der Akzentfarbe
     Spalte 2 braucht        39,2 mm, hat 38,1 mm   -> 1,1 mm fehlen

   Drei Befunde, drei Aenderungen:

   1  Die IBAN war mit 6,8 pt die kleinste Zeile des Dokuments — und die
      einzige, die ABGETIPPT wird. An ihr haengt die Groesse, nicht am
      Firmennamen. Sie ist jetzt so gross wie der Rest (7,1 pt), mit
      engerer Laufweite; `white-space: nowrap` bleibt.

      Die GESAMTgroesse bleibt bei 7,1 pt, und das ist gemessen so
      entschieden. Ein Versuch mit 7,9 pt machte die Umbrueche
      SCHLECHTER (4 statt 3), weil der Platz schon vorher nicht reichte:

        7,1 pt  mit Symbolen   3 Umbrueche   Bedarf 176,0 / 167,6 mm
        7,5 pt  mit Symbolen   4             183,4 / 167,6
        7,9 pt  mit Symbolen   4             190,7 / 167,6
        7,1 pt  OHNE Symbole   0             140,0 / 167,6

      Mit Symbolen passt es bei KEINER Groesse. Die Entscheidung, ob sie
      bleiben, liegt bei Christian (67.3) — sie ist die Voraussetzung
      dafuer, dass die Schrift wachsen kann.

   2  Die Symbole waren FARBIG (Akzentblau) und damit auffaelliger als der
      graue Text daneben. Sie tragen keine Information, die der Text nicht
      traegt — vier beschriftete Spalten sagen schon, was sie sind. Sie
      sind jetzt groesser (fuer die Gliederung) und in der Textfarbe (damit
      sie nicht mit der IBAN konkurrieren). Ein Symbol, das auffaelliger
      ist als die IBAN daneben, zieht den Blick an die falsche Stelle.

   3  `www.mittelfranken-informatik.de` und `info@…` brachen nach
      „mittelfranken-". Eine Adresse, die mitten im Wort bricht, ist schwer
      abzutippen. Die Spalte bekommt den Platz, den sie braucht — der
      `gap` sinkt von 12 auf 8 px und gibt 3 x 4 px frei, und die zweite
      Spalte waechst mit `flex: 1.12`.
   ============================================================ */
.inv-foot4 { display: flex; gap: 8px; margin-top: auto; padding-top: 12mm;
  /* Ohne Rueckfallwert (69.2.0): `--inv-acc` kommt aus dem Dokument, und
     eine zweite Farbe hier waere eine zweite Wahrheit. Fehlt sie, ist die
     Linie unsichtbar — sichtbarer Mangel statt stiller Ersatzfarbe. */
  border-top: 2px solid var(--inv-acc); }
/* Die Linie sitzt am BLOCK, nicht an den Spalten — eine durchlaufende
   statt vier kurzer. Der `padding-top` des Blocks bleibt darueber, damit
   der Abstand zum Inhalt derselbe ist wie vorher. */
/* ---------- Die Spalte ist eine SAEULE, nicht eine Zeile (67.7) ----------
   `flex-direction: column` stellt das Symbol UEBER den Text statt daneben.
   Damit kostet es keine Breite mehr — und Breite war das Knappe: die vier
   Symbole nahmen daneben 36 mm von 167,6 verfuegbaren.

   Zentriert, wie Christian es entschieden hat: das Symbol mittig ueber
   seiner Spalte, der Text darunter zentriert. Mein Einwand steht
   dahinter — mehrzeilige Bloecke wirken zentriert unruhiger, weil keine
   gemeinsame linke Kante da ist — aber er hat den Bildvergleich gesehen
   und ich nicht. */
.inv-fcol { flex: 1; padding-top: 8px; display: flex; flex-direction: column;
  align-items: center; text-align: center; gap: 6px; color: #000;
  font-size: 11px; line-height: 1.5; }
/* ---------- Die Symbole, eine Stufe groesser (67.7) ----------
   26 px Kreis statt 22, 15 px Glyph statt 12 — Christians Punkt war, dass
   sie zu klein waren. Ueber dem Text ist der Platz dafuer da; daneben
   haette dieselbe Stufe 4,4 mm Breite gekostet und die Webadresse
   brechen lassen (gemessen am 07.09.2026).

   Die Farbe ist wieder die Akzentfarbe: das Grau aus 67.5 hob sich
   schlechter ab. Neben dem Text war das ein Problem — ein Symbol, das
   auffaelliger ist als die IBAN daneben, zieht den Blick an die falsche
   Stelle. UEBER dem Text konkurriert es nicht mehr mit ihr, sondern
   ueberschreibt seine Spalte. */
/* ---------- Die Symbole sind NEUTRAL (69.5.2, 07.09.2026) ----------
   Sie waren kurz rot — „Verzierung, also Akzentfarbe". Das war falsch:
   vier rote Kreise unter einer roten Linie sind zu viel Rot auf einem
   Blatt. Rot gehoert auf die LINIEN; sie gliedern, ohne sich zu
   wiederholen.

   Sie tragen die Farbe des Fusstexts — ein Symbol, das seinen Text
   begleitet, traegt dessen Farbe, sonst stehen die beiden nebeneinander
   statt zusammen. Der Fusstext ist seit 69.6.2 schwarz (war #4A5160,
   7,96:1), also sind es die Symbole auch: 21:1. Graue Symbole ueber
   schwarzem Text waeren derselbe Fehler wie die zwei roten Spalten im
   grauen Kopf. */
.inv-fico { flex: none; width: 26px; height: 26px; border: 1.5px solid #000; border-radius: 50%; color: #000; display: flex; align-items: center; justify-content: center; font-size: 14px; }
.inv-fico svg { width: 15px; height: 15px; }
.inv-frows { min-width: 0; overflow-wrap: break-word; }
/* Die Bankspalte traegt die IBAN — 26 Zeichen zusammenhaengend, die laengste
   Kette im ganzen Dokument. Bei vier gleich breiten Spalten brach sie mitten
   in der Nummer um („…6106" / „29"), und wer sie abtippt, stolpert genau da.
   Deshalb mehr Platz fuer diese Spalte und ein Umbruchverbot fuer die Zeile:
   die drei anderen Spalten brechen ohnehin an Wortgrenzen, das faellt nicht
   auf, eine zerlegte IBAN schon. */
/* GRABSTEIN: `.inv-fbank { flex: 1.32 }` (67.6) — die Bankspalte brauchte
   den Zuschlag, solange die Symbole 36 mm nahmen. Seit sie weg sind,
   folgt sie wie die anderen dem gemessenen Bedarf (1,03). Die Klasse
   bleibt am Markup: sie benennt die Spalte, auch ohne Sonderbreite. */
/* ---------- Die Breiten folgen dem BEDARF (67.1, 07.09.2026) ----------
   Vorher waren alle vier gleich breit (`flex: 1`), nur die Bankspalte
   hatte 1.32. Gemessen bei 7,1 pt, je Spalte Text + Symbol:

     Spalte 1  Hoshchuk & Riedel GbR              braucht 31,0 mm
     Spalte 2  info@mittelfranken-informatik.de   braucht 43,3 mm
     Spalte 3  VR Bank im suedlichen Franken      braucht 39,7 mm
     Spalte 4  Steuernummer 203/154/00040         braucht 40,8 mm

   Die Faktoren stehen im Verhaeltnis dieser Zahlen. Sie loesen die
   Umbrueche NICHT vollstaendig — mit Symbolen braucht der Block 176 mm
   und hat 167,6 — aber sie verteilen den Mangel dorthin, wo er am
   wenigsten kostet: ein Firmenname bricht besser als eine Mailadresse.
   Die Symbolfrage liegt bei Christian (67.3). */
/* Spalte 1 bekommt KEINEN kleineren Faktor: der erste Versuch mit 0.84
   gab ihr 35,2 mm, sie braucht 40,0 — und „Hoshchuk & Riedel GbR" brach.
   Der Mangel von 8,4 mm (176,0 Bedarf gegen 167,6 verfuegbar) laesst sich
   nicht wegverteilen; er kann nur dorthin, wo er am wenigsten kostet. */
/* ---------- Die Breiten folgen dem BEDARF (67.6, 07.09.2026) ----------
   Ohne Symbole und bei 8,3 pt gemessen, je Spalte die laengste Zeile:

     1  Hoshchuk & Riedel GbR              32,5 mm   Faktor 0,80
     2  info@mittelfranken-informatik.de   45,4 mm          1,12
     3  VR Bank im suedlichen Franken      41,5 mm          1,03
     4  Steuernummer 203/154/00040         42,7 mm          1,05
                                          -------
                                          162,1 mm bei 167,6 verfuegbar

   Gleiche Breiten (`flex: 1` fuer alle) waeren 41,9 mm je Spalte — zu
   wenig fuer Spalte 2 und 4, zu viel fuer Spalte 1. Genau das hat die
   Webadresse brechen lassen, obwohl der Platz insgesamt reichte: nicht
   der Mangel war das Problem, sondern die Verteilung.

   `.inv-fbank` (Spalte 3) verliert damit ihren Sonderfaktor 1.32 — sie
   braucht ihn nicht mehr, seit die Symbole weg sind. */
.inv-foot4 .inv-fcol:nth-child(1) { flex: 0.80; }
.inv-foot4 .inv-fcol:nth-child(2) { flex: 1.12; }
.inv-foot4 .inv-fcol:nth-child(3) { flex: 1.03; }
.inv-foot4 .inv-fcol:nth-child(4) { flex: 1.05; }
/* Die IBAN war mit 6,8 pt die KLEINSTE Zeile des Dokuments — und die
   einzige, die abgetippt wird (67.5.3). Sie ist jetzt so gross wie der
   Rest; die engere Laufweite haelt sie in einer Zeile, `nowrap` bleibt
   der Riegel dagegen, dass sie beim Abtippen stolpert. */
.inv-fiban { white-space: nowrap; font-size: inherit; letter-spacing: -.02em; }


/* >>>>>> DOKUMENT-CSS ENDE <<<<<< */

/* Vorschlaege fuer die Bezeichnung — Dialog, nicht Dokument. */
.rb-vorschlaege { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -4px; }

.rk-modal-wide { width: min(680px, 95vw); }
/* ---------- Drucken ----------
   Die Druckfassung soll AUSSEHEN wie die Vorschau. Zwei Dinge standen dem im
   Weg:

   1. Browser lassen Hintergrundfarben beim Drucken weg. Der dunkle
      Tabellenkopf und die Akzentspalten waren dadurch weiße Schrift auf
      weißem Papier — die Spaltentitel fehlten praktisch. `print-color-adjust`
      erzwingt sie.

   2. `@page` ohne Rand: dann setzt Chrome KEINEN eigenen Kopf mit Datum und
      Seitentitel („28.07.26, 09:29 — MFI-Suite") darüber. Das Dokument bringt
      seine A4-Ränder selbst mit (14/18/10 mm), doppelte Ränder wären falsch.
      Deshalb bleibt hier das Innenmaß des Dokuments stehen, statt es wie
      vorher auf 0 zu setzen. */
@page { size: A4; margin: 0; }

@media print {
  /* ---- Der Druckdeckel (01.09.2026, 23.2.1) ----
     `html, body { height: 100% }` steht weiter oben (style.css:127) und traegt
     das Bildschirmlayout: die App fuellt das Fenster, die Bereiche darin rollen
     selbst. Im DRUCK ist dieselbe Regel ein Rahmen auf Fensterhoehe.

     Gemessen am 01.09.2026: ein Dokument von 1860 px in einem body von 1000 px
     ergab EINE Seite — der Rest verschwand ohne Meldung. Derselbe Inhalt ueber
     Gotenberg (wo `.inv-doc` direkt im body steht und diese Regel nicht greift)
     ergab zwei saubere A4-Seiten.

     Nicht die Ursache, gemessen und ausgeschlossen: `display: flex` an
     `.inv-doc` (vier Proben, alle einseitig), die `@page`-Regel (greift), die
     Gotenberg-Parameter. */
  html, body { height: auto; overflow: visible; }
  /* ---- Der Seitengrund (03.09.2026, 49.2a) ----
     Der Hintergrund des Wurzelelements deckt das ganze BLATT, nicht nur den
     Inhalt — Chromium malt ihn als Seitengrund. Auf dem Bildschirm ist das
     `var(--bg)`, also rgb(10,13,19). Im Druck stand es damit unter jedem
     Blatt, und wo das weisse Dokument endete, kam es zum Vorschein.

     Gemessen am 03.09.2026 an den erzeugten PDFs, beide Wege:
       Vorschau (window.print), lange Rechnung: Weiss bis 226,5 von 297,0 mm
       Vorschau, kurze Rechnung:                Weiss bis 235,7 von 297,0 mm
       Gotenberg, beide:                        Weiss bis 297,1 mm
     Bei Gotenberg gibt es KEINE dunkle Fuellung — dort steht in
     dokument-html.js `html, body { background: #fff }`. Das ist die Vorlage,
     und diese Zeile holt sie in den Vorschau-Weg.

     Nicht `min-height` am Dokument: die weisse KISTE endet auf beiden Wegen
     mit dem Inhalt (`min-height: 0` weiter unten), und das ist richtig so —
     sie soll den Umbruch nicht erzwingen. Was fehlte, war der Grund darunter.
     Der Deckel aus 23.2.1 bleibt gefallen: `height: auto` steht eine Zeile
     hoeher und wird hier nicht angefasst. */
  html, body { background: #fff; }
  /* Der Fussbereich steht am Blattende, nicht unter der Grussformel. Den
     Abstand rechnet `fussAnsBlattende` in shared/inv-doc.js aus; hier
     steht nur, dass eine von Hand gesetzte Marge im Druck gilt. */
  body > .app, body > #toast-root { display: none !important; }
  #modal-root .rk-preview-toolbar { display: none !important; }
  #modal-root .rk-preview-bg { position: static; background: #fff; }
  #modal-root .rk-preview-scroll { overflow: visible; padding: 0; display: block; }
  #modal-root .inv-doc {
    box-shadow: none;
    /* Genau A4, mit den eigenen Rändern des Dokuments — wie in der Vorschau. */
    width: 210mm; min-height: 0; padding: 14mm 18mm 10mm; margin: 0;
  }
  /* Farben und Flächen mitdrucken, sonst verschwindet der Tabellenkopf. */
  #modal-root .inv-doc, #modal-root .inv-doc * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* ============ Lager ============ */
.lager-tbl td { vertical-align: middle; }
.lager-note { color: var(--faint); font-size: 11.5px; margin-top: 2px; }
.lager-low { background: rgba(240,96,107,.06); }
.li-qty { display: inline-flex; align-items: center; gap: 8px; }
.li-adj { width: 24px; height: 24px; border-radius: 6px; border: 1px solid var(--line); background: var(--raised); color: var(--text); cursor: pointer; font-size: 15px; line-height: 1; }
.li-adj:hover { background: var(--hover); border-color: var(--accent-dim); }

/* ============ Planung: Projekte ============ */
.pr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }
.pr-card { display: block; text-decoration: none; color: var(--text); background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--pc, var(--accent)); border-radius: var(--radius); padding: 15px 16px; transition: border-color .12s, transform .12s, background .12s; }
.pr-card:hover { transform: translateY(-2px); background: var(--raised); }
.pr-card-top { display: flex; align-items: center; gap: 8px; }
.pr-card-top b { flex: 1; font-family: var(--font-display); font-size: 15px; }
.pr-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--pc, var(--accent)); flex: none; }
.pr-kunde { color: var(--muted); font-size: 12.5px; margin-top: 4px; }
.pr-desc { color: var(--faint); font-size: 12px; margin-top: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pr-prog { height: 6px; background: var(--raised); border-radius: 4px; margin-top: 12px; overflow: hidden; }
.pr-prog-bar { height: 100%; background: var(--pc, var(--accent)); }
.pr-meta { color: var(--faint); font-size: 11px; margin-top: 6px; font-family: var(--font-mono); }
/* Der Menueknopf an der Projektkarte. Gleiche Form wie `.kb-col-menu` im Board —
   es ist dieselbe Geste, und zwei Formen dafuer waeren zwei Dinge zum Lernen. */
/* Farbe an der Kanban-Karte: derselbe Traeger (`--c`) wie an einer Aufgabe.
   Ohne Farbe wird `.hat-farbe` nicht gesetzt und die Karte sieht aus wie
   bisher — „keine Farbe" ist kein grauer Strich, sondern kein Strich. */
.kb-card.hat-farbe { border-left: 3px solid var(--c); padding-left: 8px; }
/* Die Spalte, die „erledigt" bedeutet, sagt es im Kopf. Ein Haken, kein Satz. */
.kb-col-fertig { color: var(--ok, #4EC58C); font-size: 12px; margin-left: 2px; }
.pr-menu { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 16px; padding: 0 2px; line-height: 1; flex: none; }
.pr-menu:hover { color: var(--text); }
/* Archiviert: sichtbar zurueckgenommen, aber lesbar. Nicht ausgegraut bis zur
   Unkenntlichkeit — wer das Archiv EINBLENDET, will es auch lesen koennen. */
.pr-card.archiviert { opacity: .62; border-left-style: dashed; }
.pr-filter { margin-bottom: 12px; }

/* ============ Planung: Kanban ============ */
.kb-back { color: var(--accent); text-decoration: none; }
.kb-board { display: flex; gap: 14px; overflow-x: auto; align-items: flex-start; padding-bottom: 10px; }
.kb-col { flex: 0 0 288px; max-width: 288px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); display: flex; flex-direction: column; max-height: calc(100vh - 200px); }
.kb-col-head { display: flex; align-items: center; gap: 8px; padding: 11px 13px; border-bottom: 1px solid var(--line-soft); font-weight: 600; font-size: 13.5px; }
.kb-col-name { flex: 1; }
.kb-col-count { font-family: var(--font-mono); font-size: 11px; color: var(--muted); background: var(--raised); border-radius: 20px; padding: 1px 7px; }
.kb-col-menu { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 16px; padding: 0 4px; }
.kb-cards { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 9px; min-height: 40px; }
.kb-cards.over { background: var(--accent-soft); outline: 2px dashed var(--accent-dim); outline-offset: -4px; }
.kb-card { background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 11px; cursor: pointer; }
.kb-card:hover { border-color: var(--accent-dim); }
.kb-card.drag { opacity: .4; }
.kb-card.done { opacity: .6; }
.kb-card.done .kb-card-title { text-decoration: line-through; }
.kb-card-title { font-size: 13.5px; font-weight: 500; }
.kb-card-meta { display: flex; align-items: center; gap: 6px; margin-top: 7px; flex-wrap: wrap; }
.kb-tk { font-family: var(--font-mono); font-size: 10px; background: var(--surface); border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px; color: var(--muted); }
.kb-due { font-family: var(--font-mono); font-size: 11px; color: var(--p-hoch); }
.kb-av { margin-left: auto; width: 22px; height: 22px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); display: grid; place-items: center; font-size: 10px; font-weight: 700; }
.kb-card-tags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }
.kb-add { margin: 0 10px 10px; padding: 8px; border: 1px dashed var(--line); background: none; color: var(--muted); border-radius: var(--radius-sm); cursor: pointer; font: inherit; font-size: 12.5px; }
.kb-add:hover { border-color: var(--accent-dim); color: var(--text); }
.pl-tag { font-size: 10.5px; background: var(--accent-soft); color: var(--accent); border-radius: 20px; padding: 1px 8px; }

/* ============ Planung: Kalender ============ */
/* `minmax(0, 1fr)` und nicht `1fr`: `1fr` ist `minmax(auto, 1fr)`, und `auto`
   hat den Inhalt als Untergrenze. Die Chips stehen auf `nowrap`, also war die
   Untergrenze einer Spalte die Breite des laengsten Titels — 348 px je Zelle.
   Erst mit `0` darf die Spalte kleiner werden als ihr Inhalt. */
.kal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.kal-wd { background: var(--raised); text-align: center; padding: 7px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 600; }
.kal-cell { background: var(--surface); min-height: 104px; padding: 5px 6px; cursor: pointer; }
.kal-cell.out { background: var(--bg); }
.kal-cell.out .kal-daynum { color: var(--faint); }
.kal-cell.today .kal-daynum { background: var(--accent); color: #1a1205; border-radius: 50%; width: 22px; height: 22px; display: grid; place-items: center; }
.kal-cell:hover { background: var(--raised); }
.kal-daynum { font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.kal-day-items { display: flex; flex-direction: column; gap: 3px; }
.kal-chip { font-size: 11px; padding: 3px 7px; border-radius: 5px; background: var(--raised); border-left: 3px solid var(--c, var(--accent)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.35; }
/* Feiertage (Bayern) */
.kal-cell.kal-hol { background: rgba(230,162,86,.05); }
.kal-holiday { font-size: 10px; color: var(--accent); margin: 1px 0 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kal-chip-hol { border-left-color: var(--accent); color: var(--accent); cursor: default; }
.kal-dh-hol { color: var(--accent); font-weight: 400; font-size: 10.5px; }
/* Wessen Termin: Initialen-Chip (CR/DH) in Chips und Zeitraster-Blöcken */
.kal-who { display: inline-flex; align-items: center; justify-content: center; min-width: 17px; height: 15px; border-radius: 8px; font-size: 9px; font-weight: 700; padding: 0 3px; margin-right: 4px; vertical-align: text-bottom; }
.kal-chip.kal-todo { border-left-color: var(--c, #8A94A6); color: var(--muted); }
.kal-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.kal-list.tag { grid-template-columns: 1fr; max-width: 600px; }
.kal-daycol { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.kal-daycol-head { padding: 9px 12px; border-bottom: 1px solid var(--line-soft); font-weight: 600; font-size: 12.5px; }
.kal-daycol-head.today { color: var(--accent); }
.kal-daycol-items { padding: 8px; display: flex; flex-direction: column; gap: 5px; min-height: 40px; }
.kal-daycol-items .kal-chip { cursor: pointer; }
.kal-empty { color: var(--faint); font-size: 12px; padding: 4px; }

/* ---- Kalender: To-do-Leiste + Drag&Drop ---- */
.kal-wrap { display: flex; gap: 16px; align-items: flex-start; }
.kal-main { flex: 1; min-width: 0; }
.kal-side { flex: none; width: 252px; position: sticky; top: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 12px; max-height: calc(100vh - 140px); overflow-y: auto; }
.kal-side-h { font-weight: 700; font-size: 13px; margin-bottom: 10px; display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.kal-side-hint { font-size: 10.5px; color: var(--faint); font-weight: 400; }
.kal-tditem { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 7px 9px; margin-bottom: 5px; background: var(--bg); border: 1px solid var(--line); border-left: 3px solid var(--c, var(--line)); border-radius: 8px; cursor: grab; font-size: 12.5px; }
.kal-tditem:hover { background: var(--raised); }
.kal-tditem.drag { opacity: .4; }
.kal-tdtext { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kal-tddue { flex: none; font-size: 10.5px; color: var(--muted); }
.kal-tddue.over { color: var(--p-kritisch); font-weight: 600; }
.kal-dropover { outline: 2px dashed var(--accent-dim); outline-offset: -2px; }
.kal-chip[draggable="true"], .kal-ev[draggable="true"] { cursor: grab; }
.kal-chip.drag, .kal-ev.drag { opacity: .4; }
/* To-do-Block im Zeitraster: Höhe wächst mit dem Text (max 2 Zeilen), nicht abgeschnitten */
.kal-tdblock { border-style: dashed; white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-height: 1.35; }
.kal-tdblock b { display: inline; margin-right: 4px; }
#kal-todos-btn.kal-btn-on { border-color: var(--accent-dim); color: var(--accent); background: var(--accent-soft); }
/* ---------- Eine Eigenschaft, drei Symptome (19.08.2026) ----------
   `.kal-wrap` ist ein Flexkasten mit `align-items: flex-start`. Wird er hier zur
   SPALTE, ist die Querachse waagerecht — und `flex-start` laesst die Kinder auf
   ihren Inhalt schrumpfen. Gemessen bei 390 px: die Tagesansicht war 134 px
   breit (34 % des Fensters), das Monatsraster 1695 px (435 %). `.kal-main` hat
   `flex: 1`, aber das wirkt in der Spalte auf die HOEHE, nicht auf die Breite.

   `stretch` ist die ganze Reparatur. Zwei Zahlen, die weit auseinanderlagen,
   hatten eine gemeinsame Ursache — und die stand nicht an den Ansichten. */
@media (max-width: 980px) {
  .kal-wrap { flex-direction: column; align-items: stretch; }
  .kal-side { position: static; width: 100%; }
}

/* Ein gesperrter Inhalt SAGT das. Nicht rot wie ein Fehler — es ist keiner —,
   aber deutlich genug, dass niemand ihn fuer „noch nicht erfasst" haelt. */
.vd-gesperrt {
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: var(--raised); border-left: 3px solid var(--accent-dim);
  color: var(--muted); font-size: 13px; line-height: 1.5;
}
.vd-gesperrt b { color: var(--text); font-weight: 600; }

/* ============ Das Kundenblatt (20.08.2026) ============ */
/* Zwei Spalten am Schreibtisch, eine am Handy. Die Abschnitte sind gleich
   gebaut, damit man sie ueberfliegt statt sie zu lesen. */
.kb-raster { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 14px; align-items: start; }
.kb-zahl {
  margin-left: auto; color: var(--muted); font-size: 11.5px; font-weight: 400;
  background: var(--raised); border-radius: 10px; padding: 1px 8px;
}
.kb-zeilen { display: flex; flex-direction: column; }
.kb-zeile {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 6px 2px;
  border-bottom: 1px solid var(--line-soft); font-size: 12.5px;
  color: var(--text); text-decoration: none;
}
.kb-zeile:last-child { border-bottom: none; }
a.kb-zeile:hover { background: var(--hover); }
.kb-mono { font-family: var(--font-mono); font-size: 11px; color: var(--muted); flex: none; min-width: 74px; }
/* ---- Die Notiz bekommt eine eigene Zeile (31.08.2026) ----
   Seit die Art in einer eigenen Zelle steht, nimmt sie in diesem schmalen
   Panel rund 70 px. Im Bild blieb von „Server neu aufgesetzt, RAID geprueft,
   Backup zurueckgespielt" noch „Server neu a…" — zwoelf Zeichen, an denen
   niemand die Leistung erkennt.

   Also umbricht die Zeile, und die Notiz laeuft ueber die volle Breite. Zwei
   Zeilen bekommt aber nur, wer eine Notiz HAT: ohne sie wird das Element gar
   nicht erzeugt, und die Zeile bleibt einzeilig wie bisher. „Leer ist leer"
   heisst hier: kein leeres Element, das trotzdem Hoehe braucht.

   Gefunden hat das ein Blick auf das Bild — der sechste solche Fall. */
.kb-haupt {
  /* `order` und nicht die DOM-Reihenfolge: im ersten Versuch stand die Notiz
     vor den Zahlen, und mit `flex-basis: 100%` schob sie Minuten und Satz in
     eine DRITTE Zeile — die Betraege standen dann unter dem Text statt in
     Flucht. Dieselbe Falle wie in der Artenliste, zwei Stunden vorher. */
  order: 99;
  flex-basis: 100%; min-width: 0; white-space: normal; overflow-wrap: anywhere;
  color: var(--muted); font-size: 12px; line-height: 1.45; margin-top: 1px;
}
.kb-neben { flex: none; color: var(--muted); font-size: 11.5px; }
/* Zahlen rechtsbuendig und untereinander — die Lehre aus dem Rechnungsmodul. */
.kb-rechts { margin-left: auto; text-align: right; min-width: 74px; font-family: var(--font-mono); }
.kb-mehr, .kb-fuss { margin-top: 8px; color: var(--faint); font-size: 11.5px; }
.kb-fuss { display: flex; align-items: center; gap: 10px; }
.kb-leer { color: var(--faint); font-size: 12.5px; padding: 4px 0; }
.kb-ohne { color: var(--faint); font-style: italic; font-weight: 400; }

/* „Nicht abrufbar" und „nichts vorhanden" sehen NICHT gleich aus. Ein
   weggelassener Abschnitt liest sich als „gibt es nicht", und das ist eine
   Auskunft — bei einem Kunden ohne Odoo-Partner eine falsche. */
.kb-hinweis {
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--raised); border-left: 3px solid var(--line);
  color: var(--muted); font-size: 12.5px; line-height: 1.5;
}
/* Ein gesperrter Betrag ist ein Strich mit Erklaerung im Tooltip, keine 0 —
   eine 0 waere eine Aussage ueber Geld. */
.kb-gesperrt { color: var(--faint); font-style: italic; cursor: help; }
/* Ein Sammelticket zaehlt mit, ist aber etwas anderes — ein Behaelter, kein
   Anliegen. Klein und ruhig: es soll unterscheidbar sein, nicht auffallen. */
.kb-sammel {
  flex: none; font-size: 10px; padding: 1px 6px; border-radius: 8px;
  background: var(--raised); border: 1px solid var(--line); color: var(--muted);
}

@media (max-width: 560px) {
  .kb-raster { grid-template-columns: 1fr; }
  .kb-zeile { flex-wrap: wrap; gap: 4px 8px; }
  .kb-rechts { margin-left: 0; }
}

/* ============ Nachrichten einordnen (20.08.2026) ============ */

/* ---- Farbe nach ART, nicht nur nach Zustand ----
   Bisher unterschied nur ein kleines KUNDE-Schild. Drei Toene, links als Kante:
   eingehend vom Kunden, eigene Antwort, System. Absichtlich zurueckhaltend —
   eine Zeitleiste in drei Signalfarben liest niemand zweimal. */
.msg-art-kunde  { border-left: 3px solid var(--art-ein); }
.msg-art-eigen  { border-left: 3px solid var(--art-aus); }
.msg-art-system { border-left: 3px solid var(--art-system); }

/* ---- Die drei Zustandsknoepfe ----
   Ein Klick, kein Menue. Sie stehen still im Kopf und werden erst beim
   Ueberfahren deutlich: an einer Nachricht, die man nur liest, sind sie Kulisse. */
.msg-zgruppe { display: inline-flex; gap: 3px; align-items: center; margin-left: 6px; }
.msg-z, .msg-ueber-btn {
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--faint); font-size: 10.5px; padding: 4px 8px; cursor: pointer;
  /* 24 px gemessen und angehoben: der erste Entwurf war 18 px hoch. „Ein Klick
     am Eintrag" heisst, dass man ihn trifft — am Handy sind es 44. */
  min-height: 24px;
  transition: color .12s, border-color .12s, background .12s;
}
@media (max-width: 560px) { .msg-z, .msg-ueber-btn { min-height: 44px; padding: 6px 10px; } }
.msg-head:hover .msg-z, .msg-head:hover .msg-ueber-btn { border-color: var(--line); }
.msg-z:hover, .msg-ueber-btn:hover { color: var(--text); background: var(--hover); }
/* Der gesetzte Zustand ist die EINZIGE Farbe hier — damit sieht man auf einen
   Blick, was eingeordnet ist, ohne die Zeile zu lesen. */
.msg-z.an { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-soft); font-weight: 600; }

/* ---- Die Ueberschrift ----
   Steht UEBER dem Kopf, weil sie beim Ueberfliegen zuerst gelesen wird — das
   ist ihr ganzer Zweck. */
.msg-ueber {
  font-weight: 600; font-size: 12.5px; color: var(--accent);
  padding: 6px 12px 0; cursor: pointer;
}
.msg-ueber:hover { text-decoration: underline dotted; }
/* Ein VORSCHLAG ist keine Eingabe (23.08.2026). Er steht blasser und kursiv da:
   wer ihn stehen laesst, hat nichts behauptet, und das soll man sehen, ohne
   den Mauszeiger zu bewegen. Kein Zusatzwort davor — „Vorschlag: …" waere ein
   Wort, das jede Zeile laenger macht, und der Ton sagt es kuerzer. */
.msg-ueber-auto { color: var(--faint); font-style: italic; }

/* ---- Die Klappzeile ----
   EINE Zeile mit den Zaehlern. Sie steht nur da, wenn es etwas zu klappen gibt. */
.tl-klapp { margin: 0 0 8px; }
.tl-klapp-btn {
  width: 100%; text-align: left; padding: 7px 12px; cursor: pointer;
  background: var(--raised); border: 1px dashed var(--line); border-radius: var(--radius-sm);
  color: var(--muted); font-size: 12px; font-family: var(--font-body);
}
.tl-klapp-btn:hover { color: var(--text); border-color: var(--accent-dim); }

/* Ausgeblendet, NICHT entfernt: die Zeile bleibt im Baum. Zusammengeklappt ist
   nicht geloescht — Suchen im Browser, Kopieren und Ausdrucken finden sie
   weiter, und der Prüfsatz misst, dass Volltextsuche und Zeitleiste sie liefern. */
.timeline.eingeklappt-aus .msg[data-zustand="erledigt"],
.timeline.eingeklappt-aus .msg[data-zustand="unwichtig"] { display: none; }
@media print { .timeline.eingeklappt-aus .msg[data-zustand] { display: block !important; } }

/* Die zwei Berechtigungen in der Mitarbeiterzeile. Klein, aber lesbar — und
   ohne Emoji: ein Haken ist die Aussage, kein Symbol daneben. */
/* ---------- Warum hier zwei Klassen stehen (27.08.2026) ----------
   `.ur-recht` allein (Spezifitaet 0-1-0) verlor gegen `.modal label`
   (0-1-1, Zeile 1131), und das setzt `display: block` und `margin-bottom`.
   Gemessen war die Folge im Bild zu sehen: die Kaestchen standen nicht bei
   ihrer Beschriftung, „Vertraege" endete als „Vertraeg" und „Auswertung" als
   „Aus" — nicht abgeschnitten, sondern vom naechsten Element ueberdeckt. Der
   Text ragte 30, 54 und 74 px ueber seine eigene Box hinaus.

   Das war der Fall, der als „obere Reihe zu breit" gemeldet wurde. Er lag
   nicht an der Dialogbreite: `.rk-modal` ist mit `min(460px, 94vw)` fuer ALLE
   Dialoge gleich, und daran wird nichts geaendert. Er lag daran, dass diese
   drei Labels nie das Layout bekamen, das fuer sie geschrieben war.

   `.user-row .ur-recht` ist 0-2-0 und gewinnt. Die geerbten Werte werden
   ausdruecklich zurueckgesetzt, nicht ueberlassen: `margin-bottom` und
   `font-weight` kommen sonst aus derselben Regel mit. */
.user-row .ur-recht {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  color: var(--muted); font-size: 11.5px; font-weight: 400; cursor: pointer;
  white-space: nowrap; margin-bottom: 0; width: auto;
}
.ur-recht {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  color: var(--muted); font-size: 11.5px; cursor: pointer; white-space: nowrap;
}
.ur-recht input { margin: 0; cursor: pointer; }
.ur-recht:hover { color: var(--text); }
@media (max-width: 560px) { .ur-recht { font-size: 11px; } }

/* ============ Kalender am Handy (19.08.2026) ============
   Gemessen bei 390x844 mit Touch, an einer Woche mit zehn Terminen. Die
   Grenze 560 px steht auch in `app.js` als `KAL_HANDY_PX`; beide werden von
   `test-kalender-handy.js` gegen denselben Wert geprueft, damit sie nicht
   auseinanderlaufen. */

/* Zwei Beschriftungen je Ansichtsknopf, das Stilblatt waehlt. */
.seg-kurz { display: none; }

@media (max-width: 560px) {
  .seg-lang { display: none; }
  .seg-kurz { display: inline; }

  /* ---- Der Kopf: ein Fehler, nicht drei ----
     Vier Ansichtsknoepfe plus `‹ Datum ›` plus zwei Knoepfe passen bei 390 px
     nicht in eine Zeile. Gemessen: der Kopf lief in ALLEN Ansichten um 366-367
     px ueber, deshalb lagen „›" und das Datum ausserhalb des Fensters. Drei
     Zeilen ueber `order` und `flex-basis`, ohne das Markup anzufassen:
       1  ‹  Datum  ›
       2  Tag | Mo–Fr | Woche | Monat
       3  ☑ To-dos | + Termin                                            */
  /* EINGEGRENZT auf `.kal-head`. Der erste Entwurf schrieb an `.rk-head` und
     `.rk-monthnav` — die stehen in ZEHN bzw. ZWEI Ansichten. Damit haette der
     Kalenderumbau die Kopfzeile von neun anderen Modulen mitverstellt, ohne
     dass eine davon gemessen wurde. Deshalb traegt nur der Kalenderkopf die
     Zusatzklasse, und alle Regeln hier haengen daran. */
  .kal-head { display: block; margin-bottom: 14px; }
  .kal-head .rk-title { font-size: 19px; }
  /* Der Untertitel nennt, was der Kalender zeigt — am Handy ist die Hoehe die
     knappere Ware, und der Modulname steht schon in der Navigation. */
  .kal-head .rk-sub { display: none; }
  .kal-head .rk-monthnav { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; margin-top: 10px; }
  .kal-head .rk-monthnav > #kal-prev, .kal-head .rk-monthnav > .rk-month, .kal-head .rk-monthnav > #kal-next { order: 1; }
  /* 44 px, nicht 40: die Knoepfe sind in diesem Umbau NEU gesetzt, also sind es
     meine. Die hausweiten 34 px bleiben unangetastet — das ist eine
     Hausentscheidung und nicht Kalendersache. */
  .kal-head .rk-monthnav > #kal-prev, .kal-head .rk-monthnav > #kal-next { flex: 0 0 44px; height: 44px; }
  .kal-head .rk-monthnav > .rk-month { flex: 1 1 auto; min-width: 0; text-align: center; font-size: 15px; }
  .kal-head .rk-monthnav > .seg { order: 2; flex: 1 0 100%; display: grid; grid-template-columns: repeat(4, 1fr); }
  .kal-head .rk-monthnav > .seg .seg-btn { padding: 12px 4px; text-align: center; min-height: 44px; }
  .kal-head .rk-monthnav > #kal-todos-btn, .kal-head .rk-monthnav > #kal-new { order: 3; flex: 1 1 0; min-width: 0; min-height: 44px; }

  /* ---- Der Monat ist eine UEBERSICHT, kein Raster ----
     1695 px seitwaerts zu schieben ist keine Uebersicht. Am Handy also kein
     Titel in der Zelle, sondern bis zu drei Punkte fuer „hier ist was" — und
     Tippen oeffnet den Tag (in `app.js`, weil es ein Weg ist und keine Form).
     Ein Tag mit fuenf Terminen zeigt drei Punkte wie ein Tag mit drei. Das ist
     der Preis einer Uebersicht; die Antwort steht einen Tipp entfernt. */
  .kal-cell { min-height: 58px; padding: 4px 3px; }
  .kal-wd { padding: 5px 2px; font-size: 9.5px; letter-spacing: 0; }
  .kal-holiday { display: none; }
  .kal-day-items { flex-direction: row; flex-wrap: wrap; gap: 3px; }
  .kal-day-items > * {
    width: 7px; height: 7px; min-width: 0; padding: 0; border-radius: 50%;
    border: none; border-left: none; font-size: 0; overflow: hidden;
    background: var(--c, var(--accent));
  }
  .kal-day-items > :nth-child(n+4) { display: none; }

  /* ---- Zeitraster: 56 px je Stunde, und kein Block unter 44 px ----
     Bei 42 px/Stunde war ein Dreiviertelstundentermin 32 px hoch. Die
     Stundenhoehe selbst setzt `app.js` (sie steht in der Geometrie der
     Bloecke); hier steht die Untergrenze eines Blocks. */
  .kal-ev { min-height: 44px; font-size: 12px; padding: 4px 8px; }
  .kal-tg { height: calc(100dvh - 300px); min-height: 360px; }

  /* ---- Woche als Liste ---- */
  .kal-liste { display: flex; flex-direction: column; gap: 10px; }
  .kal-ltag { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
  .kal-ltag.today { border-color: var(--accent-dim); }
  .kal-ltag.kal-hol { background: rgba(230,162,86,.05); }
  .kal-ltag-kopf {
    display: flex; align-items: baseline; gap: 8px; padding: 10px 12px;
    background: var(--raised); border-bottom: 1px solid var(--line-soft);
    cursor: pointer; min-height: 44px;
  }
  .kal-ltag.today .kal-ltag-wt { color: var(--accent); }
  .kal-ltag-wt { font-weight: 600; font-size: 13.5px; }
  .kal-ltag-d { color: var(--muted); font-size: 12px; }
  .kal-ltag-hol { color: var(--accent); font-size: 11.5px; }
  .kal-ltag-neu {
    margin-left: auto; flex: none; width: 44px; height: 44px; align-self: center;
    background: transparent; border: 1px solid var(--line); border-radius: var(--radius-sm);
    color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer;
  }
  .kal-ltag-items { display: flex; flex-direction: column; gap: 5px; padding: 9px 10px; }
  /* Hier ist der Titel GANZ zu lesen — das ist der ganze Zweck der Liste. */
  .kal-ltag-items .kal-chip {
    white-space: normal; overflow: visible; text-overflow: clip;
    font-size: 12.5px; padding: 8px 10px; line-height: 1.4;
  }
  .kal-ltag-items .kal-chip b { margin-right: 5px; }
  /* Ein freier Tag braucht eine Zeile, keinen Kasten. Der erste Entwurf gab ihm
     denselben Rumpf wie einem vollen Tag — bei einer ruhigen Woche stand dann
     siebenmal „frei" untereinander und der eine Termin lag unter der Kante. */
  .kal-ltag-ohne .kal-ltag-kopf { border-bottom: none; }
  .kal-ltag-frei { margin-left: auto; color: var(--faint); font-size: 12px; }
  .kal-ltag-frei + .kal-ltag-neu { margin-left: 8px; }
}

/* ============ Planung: To-dos ============ */
.td-list { display: flex; flex-direction: column; gap: 6px; }
.td-item { display: flex; align-items: flex-start; gap: 11px; padding: 10px 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 10px; }
.td-item.drag { opacity: .4; }
.td-item.done .td-text { text-decoration: line-through; color: var(--muted); }
.td-check { flex: none; width: 21px; height: 21px; margin-top: 1px; border-radius: 50%; border: 1.5px solid var(--faint); background: none; color: var(--ok); cursor: pointer; font-size: 13px; line-height: 1; display: grid; place-items: center; transition: border-color .12s, background .12s; }
.td-item:hover .td-check { border-color: var(--accent-dim); }
.td-check.on { background: rgba(78,197,140,.15); border-color: var(--ok); }
.td-main { flex: 1; min-width: 0; }
.td-text { cursor: text; font-size: 14px; word-break: break-word; }
.td-edit { width: 100%; background: var(--raised); border: 1px solid var(--accent-dim); border-radius: 6px; color: var(--text); padding: 5px 8px; font: inherit; font-size: 14px; outline: none; }
.td-meta { display: flex; align-items: center; gap: 9px; margin-top: 5px; flex-wrap: wrap; min-height: 16px; }
.td-due { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--muted); }
.td-due .td-cal { flex: none; opacity: .8; }
.td-due.over { color: var(--p-kritisch); font-weight: 600; }
.td-assignee { font-size: 11px; color: var(--accent); background: var(--accent-soft); border-radius: 20px; padding: 1px 8px; }
.td-menu { flex: none; background: none; border: none; color: var(--muted); cursor: pointer; font-size: 18px; padding: 0 4px; }
.td-menu:hover { color: var(--text); }
/* Aktionen erst bei Hover → ruhiges Bild */
.td-hov { opacity: 0; transition: opacity .12s; }
.td-item:hover .td-hov, .td-item:focus-within .td-hov { opacity: 1; }
/* Unterpunkte: Checkliste IN der Karte (MS-To-Do-Stil) */
.td-subs { margin-top: 8px; border-top: 1px solid var(--line-soft, var(--line)); padding-top: 5px; }
.td-subrow { display: flex; align-items: center; gap: 9px; padding: 4px 4px; border-radius: 6px; }
.td-subrow:hover { background: var(--bg); }
.td-subrow:hover .td-hov { opacity: 1; }
.td-subcheck { flex: none; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid var(--faint); background: none; color: var(--ok); cursor: pointer; font-size: 10px; line-height: 1; display: grid; place-items: center; }
.td-subrow:hover .td-subcheck { border-color: var(--accent-dim); }
.td-subcheck.on { background: rgba(78,197,140,.15); border-color: var(--ok); }
.td-subtext { flex: 1; min-width: 0; font-size: 13px; cursor: text; word-break: break-word; }
.td-subrow.done .td-subtext { text-decoration: line-through; color: var(--muted); }
.td-subdel { flex: none; background: none; border: none; color: var(--muted); cursor: pointer; font-size: 12px; padding: 1px 5px; }
.td-subdel:hover { color: var(--p-kritisch); }

/* ---- Lager Ergänzungen ---- */
.li-marge { color: var(--ok); font-size: 11px; }
.li-flow { font-size: 11px; color: var(--muted); }
/* ---- Kontakte: Ansprechpartner ---- */
.kk-ap { padding: 8px 17px 4px; }
.kk-ap-head { display: flex; align-items: center; justify-content: space-between; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); font-weight: 600; margin-bottom: 8px; }
.kk-ap-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: var(--surface); margin-bottom: 6px; }
.kk-ap-main { flex: 1; min-width: 0; cursor: pointer; }
.kk-ap-name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.kk-ap-role { color: var(--muted); font-size: 12px; font-weight: 400; }
.kk-ap-sub { color: var(--faint); font-size: 12px; margin-top: 2px; }
/* Der Grund bei mehreren Rechnungsadressen (93b) — derselbe Satz, den der
   Versanddialog zeigt (.ds-an-grund), an der Stelle, an der er zu beheben
   ist. Er faellt auf, weil er sonst niemandem hilft: wer ihn ueberliest,
   sucht spaeter im Versanddialog nach einem leeren Feld. */
.kk-ra-grund { font-size: 12px; color: var(--p-hoch); line-height: 1.45; margin-top: 6px; }

/* ---- Kalender: Zeitraster (Outlook-Stil) ---- */
.kal-tg { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface); display: flex; flex-direction: column; height: calc(100vh - 210px); }
.kal-tg-head, .kal-tg-ad { display: grid; background: var(--raised); }
.kal-tg-head { border-bottom: 1px solid var(--line); }
.kal-tg-ad { border-bottom: 1px solid var(--line); min-height: 30px; }
.kal-tg-corner { padding: 6px; font-size: 10px; color: var(--faint); text-align: center; border-right: 1px solid var(--line-soft); display: flex; align-items: center; justify-content: center; }
.kal-tg-dh { padding: 8px 6px; text-align: center; font-size: 12px; font-weight: 600; border-right: 1px solid var(--line-soft); }
.kal-tg-dh.today { color: var(--accent); }
.kal-adcol { border-right: 1px solid var(--line-soft); padding: 4px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.kal-tg-scroll { flex: 1; overflow-y: auto; }
.kal-tg-grid { display: grid; position: relative; padding-top: 10px; } /* 00:00-Label nicht abschneiden */
.kal-hours { border-right: 1px solid var(--line-soft); }
.kal-h { position: relative; }
.kal-h span { position: absolute; top: -7px; right: 6px; font-size: 10px; color: var(--faint); font-family: var(--font-mono); }
.kal-col { position: relative; border-right: 1px solid var(--line-soft); cursor: pointer; }
.kal-hline { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line-soft); }
.kal-ev { position: absolute; left: 3px; right: 3px; background: var(--raised); background: color-mix(in srgb, var(--c, var(--accent)) 45%, transparent); border: 1px solid color-mix(in srgb, var(--c, var(--accent)) 55%, transparent); border-left: 3px solid var(--c, var(--accent)); border-radius: 5px; padding: 3px 7px; font-size: 11px; overflow: hidden; cursor: pointer; z-index: 1; color: var(--text); }
.kal-ev:hover { background: color-mix(in srgb, var(--c, var(--accent)) 60%, transparent); }
.kal-ev b { font-family: var(--font-mono); font-size: 10px; display: block; }
.kal-ev-t { font-weight: 600; }
.kal-ev-loc { display: block; font-size: 10px; opacity: .85; margin-top: 1px; }
/* ---------- Kurze Termine: eine Zeile statt drei (25.08.2026) ----------
   Avatar, Zeit und Titel nebeneinander; der Ort wandert in den Tooltip. Die
   Schwelle (54 px) steht in app.js und ist dort gemessen begruendet.
   `min-width: 0` am Titel ist noetig, damit `text-overflow` im Flex-Kind
   ueberhaupt greift — ohne das waechst er ueber den Kasten hinaus statt zu
   kuerzen, und der Titel waere wieder abgeschnitten, nur unsichtbar. */
/* ---------- Alles sichtbar, alles kuerzbar (25.08.2026) ----------
   Hier stand `.kal-ev-loc { display: none }` — der Ort verschwand aus kurzen
   Kacheln und stand nur im Tooltip. Die Regel „was nicht passt, in den
   Tooltip" hat an dieser Stelle zweimal das Falsche bewirkt: erst bei den
   Avataren, dann beim Ort. Sie ist gestrichen.

   **Vier gekuerzte Angaben sind besser als drei vollstaendige und eine
   unsichtbare.** „FH-Eichservice - Laubenzedel / Merkendorf" darf zu
   „FH-Eichservice…" werden — verschwinden darf es nicht. Am Handy gibt es
   keinen Tooltip, dort waere „unsichtbar" endgueltig.

   Titel und Ort teilen sich den Rest: `flex` mit unterschiedlicher Gewichtung,
   damit der Titel mehr bekommt, wenn beide kuerzen muessen. `min-width: 0` an
   beiden — ohne das greift `text-overflow` im Flex-Kind nicht, es waechst
   stattdessen ueber den Kasten hinaus. */
.kal-ev-kurz { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.kal-ev-kurz b { display: inline; flex: none; }
.kal-ev-kurz .kal-who { margin-right: 0; flex: none; }
.kal-ev-kurz .kal-ev-t { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 2 1 auto; }
.kal-ev-kurz .kal-ev-loc {
  display: inline; margin-top: 0; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; flex: 1 1 auto;
}

/* ---------- Beteiligte im Termindialog (25.08.2026) ---------- */
.ev-teiln { align-items: flex-start; }
.ev-teiln-liste { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.ev-teiln-p { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer; }
.ev-teiln-p input[disabled] { cursor: not-allowed; }
.ev-teiln-p:has(input[disabled]) { opacity: .5; cursor: not-allowed; }
/* Der Hinweis ist keine Dekoration: er sagt, dass nichts verschickt wird.
   Deshalb steht er unter der Auswahl und nicht in einem Tooltip. */
.ev-teiln-hinweis { font-size: 11px; opacity: .8; margin-top: 5px; line-height: 1.45; }

/* ---- To-dos: Notiz, Gruppen, Unterpunkt-Inline ---- */
.td-notes { color: var(--muted); font-size: 12px; margin-top: 3px; white-space: pre-wrap; }
.td-group-h { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); font-weight: 600; margin: 0 0 8px; }
.td-group .td-item { margin-bottom: 6px; }
.td-addsub { background: none; border: none; color: var(--accent); cursor: pointer; font-size: 11px; padding: 0; }
.td-subadd { margin-top: 6px; }
.td-subadd input { width: 100%; background: var(--raised); border: 1px solid var(--line); border-radius: 6px; color: var(--text); padding: 6px 9px; font: inherit; font-size: 13px; outline: none; }
.rk-modal textarea { width: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); padding: 8px 10px; font: inherit; font-size: 13px; resize: vertical; outline: none; }
.rk-modal textarea:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--ring); }

/* ---- Farb-Picker (Palette + optional eigene Farbe) ---- */
.col-pick { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.col-sw { width: 26px; height: 26px; border-radius: 50%; border: none; background: var(--sw, var(--accent)); cursor: pointer; padding: 0; }
.col-sw.active { outline: 2px solid var(--text); outline-offset: 2px; }
.col-custom { display: grid; place-items: center; color: #fff; font-size: 16px; font-weight: 700; line-height: 1; background: conic-gradient(#E0757F, #E6A256, #4EC58C, #5BA8EA, #B08AE0, #E0757F); text-shadow: 0 1px 2px rgba(0,0,0,.5); }
.col-custom-wrap { position: relative; display: inline-grid; place-items: center; }
.col-custom-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: none; opacity: 0; cursor: pointer; }

/* ---- Rechnung: Positions-Editor (Entwurf) ---- */
.rk-lines { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }
.rk-lines-h { font-weight: 700; font-size: 13px; margin-bottom: 8px; color: var(--text); }
.rk-addline { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; align-items: center; }
.rk-addline select, .rk-addline input { padding: 7px 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg); color: var(--text); font-size: 13px; }
.rk-addline #al-name { flex: 1 1 100%; min-width: 0; }
.rk-addline #al-qty { flex: 0 0 60px; }
.rk-addline #al-price { flex: 0 0 90px; }
.rk-addline .btn { flex: 0 0 auto; white-space: nowrap; margin-left: auto; }
.rk-lines-note { font-weight: 400; font-size: 11px; color: var(--faint); margin-left: 6px; }
/* ---------- GRABSTEIN: eine zweite .inv-ku-note-Regel (77.6a, 13.09.2026)
   ----------
   Hier stand:

     .inv-ku-note { margin: 8px 0 0 auto; width: 62mm; font-size: 11px;
                    color: #555; text-align: right; }

   Sie stand AUSSERHALB der Dokument-Marker und gewann damit in der Suite
   gegen die Regel im Dokumentblock (Zeile 3443) — die Suite laedt die ganze
   Datei, das PDF bekommt nur den Block. Der § 19-Satz sah deshalb in der
   Vorschau anders aus als beim Kunden: rechtsbuendig statt links, 62 mm
   breit statt voll, 11px/#555 statt 10,5px/#6a7280.

   Gemessen am echten Beleg #57 (77.2/77.5): ACHT der fuenfzehn Unterschiede
   zwischen Vorschau und PDF hingen an dieser einen Regel, und ihr Entfernen
   erzeugt keinen neuen (WEG 8, NEU 0).

   Die Klasse entsteht NUR in `shared/inv-doc.js`; kein anderer Ort im Haus
   erzeugt sie (gemessen ueber das ganze Repo). Diese Regel gestaltete also
   ausschliesslich die Dokumentvorschau — es geht nichts anderes mit.

   KEIN RUECKFALL: wer den § 19-Satz aendern will, aendert ihn im
   Dokumentblock. Dort gilt er fuer beide Wege. */

/* ---- To-dos Redesign Runde 4 (MS-To-Do-Stil): Listen-Rail, Composer, Farb-Gruppen ---- */
.td-layout { display: flex; gap: 26px; align-items: flex-start; }
.td-rail { flex: none; width: 208px; display: flex; flex-direction: column; gap: 2px; padding-top: 2px; position: sticky; top: 12px; }
.td-rail-h { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--faint); font-weight: 600; margin: 2px 11px 8px; }
/* Die Breitenbindung steht HIER, nicht je Schiene: dieselbe Klasse traegt die
   To-do-Schiene, die Wissen-Schiene und die Chatliste. Zweimal gesetzt waere es
   genau die Drift, die es bei den Formatierstellen der Ticketnummer schon gab.

   `width: 100%` braucht die CHATLISTE: dort ist der Elternteil ein Block mit
   `overflow-y: auto`, und ein <button> ist darin Shrink-to-fit — er liess sich
   vom Inhalt auf 484 px aufdruecken (Spalte: 250 px). In den beiden anderen
   Schienen ist der Elternteil ein Spalten-Flex, dort streckt `align-items:
   stretch` den Knopf schon auf die Spaltenbreite; die Regel ist da wirkungslos
   und stoert nicht.

   `min-width: 0` und `overflow-wrap: anywhere` brauchen ALLE drei: am
   29.07.2026 gemessen ragte die Wissen-Schiene mit dem Ordnernamen
   „Drucker und Multifunktionsgeraete Brother" um 6 px hinaus — ein einzelnes
   langes Wort kann nicht umbrechen, und ohne `min-width: 0` gibt es keine
   Grenze, gegen die es brechen muesste. */
.td-rail-item { display: flex; align-items: center; gap: 10px; padding: 9px 11px; border-radius: 9px; background: none; border: none; color: var(--text); cursor: pointer; font: inherit; font-size: 13.5px; text-align: left; width: 100%; min-width: 0; overflow-wrap: anywhere; }
.td-rail-item:hover { background: var(--surface); }
.td-rail-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.td-rail-item .td-rico { flex: none; width: 20px; text-align: center; font-size: 14px; }
.td-rail-item .td-rcnt { margin-left: auto; font-size: 11px; color: var(--muted); background: var(--bg); border: 1px solid var(--line); border-radius: 20px; padding: 0 7px; min-width: 24px; text-align: center; }
.td-rail-item.active .td-rcnt { color: var(--accent); border-color: transparent; background: rgba(0,0,0,.25); }
.td-content { flex: 1; min-width: 0; max-width: 880px; }
.td-content-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin: 4px 0 16px; flex-wrap: wrap; }

.td-viewbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.td-groupby { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
.td-groupby select { background: var(--surface); border: 1px solid var(--line); border-radius: 7px; color: var(--text); padding: 5px 8px; font: inherit; font-size: 12px; }
/* Stern-Toggle: Wichtige innerhalb der Gruppen zuerst */
.td-impsort { background: var(--surface); border: 1px solid var(--line); border-radius: 7px; color: var(--faint); cursor: pointer; font-size: 14px; line-height: 1; padding: 5px 9px; }
.td-impsort:hover { color: var(--text); }
.td-impsort.on { color: #E6A256; border-color: rgba(230,162,86,.5); background: rgba(230,162,86,.08); }

/* Composer: eine ruhige Zeile, Auswahl über Popover */
.td-composer { position: relative; display: flex; align-items: center; gap: 10px; margin: 2px 0 20px; padding: 5px 8px 5px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: 11px; }
.td-composer:focus-within { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--ring); }
.td-comp-plus { flex: none; color: var(--accent); font-size: 18px; line-height: 1; }
.td-composer #td-input { flex: 1 1 160px; min-width: 0; background: none; border: none; color: var(--text); padding: 10px 0; font: inherit; font-size: 14px; outline: none; }
/* globaler input:focus-Ring (Z. ~909) würde sonst als „gelber Kasten" IM Composer erscheinen —
   der Composer hat seinen eigenen focus-within-Ring außen. */
.td-composer #td-input:focus, .td-subadd input:focus { box-shadow: none; }
.td-comp-tools { display: flex; align-items: center; gap: 5px; flex: none; flex-wrap: wrap; }
.td-tool { display: inline-flex; align-items: center; gap: 6px; background: none; border: 1px solid transparent; border-radius: 8px; color: var(--muted); cursor: pointer; padding: 7px 8px; font: inherit; font-size: 12px; line-height: 1; }
.td-tool:hover { background: var(--raised); color: var(--text); }
.td-tool.set { border-color: var(--line); background: var(--bg); color: var(--text); }
.td-tool .td-tico { font-size: 13px; line-height: 1; }
.td-tooldot { width: 14px; height: 14px; border-radius: 50%; background: var(--c); box-sizing: border-box; }
.td-tooldot.none { background: none; border: 2px dashed var(--line); }
.td-tstar { font-size: 16px; color: var(--faint); padding: 5px 8px; }
.td-tstar.on { color: #E6A256; }

.td-pop { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 240px; background: var(--raised); border: 1px solid var(--line); border-radius: 11px; box-shadow: 0 12px 32px rgba(0,0,0,.45); padding: 6px; }
.td-pop-list { display: flex; flex-direction: column; }
.td-pop-list button { display: flex; align-items: center; gap: 9px; background: none; border: none; color: var(--text); cursor: pointer; font: inherit; font-size: 13px; padding: 8px 10px; border-radius: 7px; text-align: left; }
.td-pop-list button:hover { background: var(--surface); }
.td-pop-list .wd { margin-left: auto; color: var(--faint); font-size: 11px; }
.td-pop-list button.rm { color: var(--p-kritisch); }
.td-pop-date { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--muted); padding: 9px 10px 5px; border-top: 1px solid var(--line); margin-top: 4px; }
.td-pop-date input { background: var(--bg); border: 1px solid var(--line); border-radius: 7px; color: var(--text); padding: 5px 7px; font: inherit; font-size: 12px; }
.td-pop .td-cpick { padding: 8px; }
.td-uini { flex: none; width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; font-size: 9px; font-weight: 700; }

.td-star { background: none; border: none; color: var(--faint); font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 4px; }
.td-star.on { color: #E6A256; }
.td-star.sm { flex: none; font-size: 15px; align-self: center; }

.td-cpick { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.td-cdot { width: 18px; height: 18px; border-radius: 50%; border: 2px solid transparent; background: var(--c, #888); cursor: pointer; padding: 0; }
.td-cdot.active { border-color: var(--text); }
.td-cnone { background: var(--bg); border: 2px dashed var(--line); }
.td-cnone.active { border-color: var(--text); border-style: solid; }

.td-item { position: relative; border-left: 4px solid var(--c, transparent); transition: background .12s; }
.td-item:hover { background: var(--raised); }
.td-item.imp .td-text { font-weight: 600; }
.td-subcount { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted); background: var(--bg); border: 1px solid var(--line); border-radius: 20px; padding: 1px 8px; }
.td-prog { display: inline-block; width: 32px; height: 4px; border-radius: 4px; background: var(--line); overflow: hidden; }
.td-prog b { display: block; height: 100%; background: var(--ok); border-radius: 4px; }

.td-group { margin-bottom: 20px; }
.td-group-h { display: flex; align-items: center; gap: 8px; font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--text); font-weight: 600; margin: 0 0 10px; }
.td-gdot { width: 12px; height: 12px; border-radius: 50%; background: var(--c, transparent); border: 1px solid var(--line); }
.td-gcount { font-size: 11px; color: var(--muted); background: var(--bg); border: 1px solid var(--line); border-radius: 20px; padding: 0 7px; font-weight: 500; }

.td-donebar { margin: 22px 0 8px; border-top: 1px solid var(--line); padding-top: 12px; }
.td-donetoggle { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 13px; font-weight: 600; display: inline-flex; align-items: center; gap: 8px; padding: 0; }
.td-donetoggle:hover { color: var(--text); }
.td-donelist { opacity: .7; }

.td-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 60px 20px; color: var(--muted); text-align: center; font-size: 13px; }
.td-empty .big { font-size: 36px; opacity: .5; line-height: 1; }
.td-empty b { color: var(--text); font-weight: 600; font-size: 14px; }

/* ============ Wissen (Brain): Artikel-Liste + Editor + Live ============ */
.ws-layout { display: flex; gap: 26px; align-items: flex-start; }
.ws-rail { flex: none; width: 208px; display: flex; flex-direction: column; gap: 2px; position: sticky; top: 12px; }
.ws-content { flex: 1; min-width: 0; max-width: 980px; }
.ws-toolrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ws-search { background: var(--surface); border: 1px solid var(--line); border-radius: 9px; color: var(--text); padding: 8px 12px; font: inherit; font-size: 13px; outline: none; width: 260px; }
.ws-search:focus { border-color: var(--accent-dim); }
.ws-item { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; margin-bottom: 8px; cursor: pointer; transition: background .12s; }
.ws-item:hover { background: var(--raised); }
.ws-item-title { font-weight: 600; font-size: 14.5px; }
.ws-item-snippet { color: var(--muted); font-size: 12.5px; margin-top: 4px; }
.ws-item-snippet mark { background: rgba(230,162,86,.25); color: var(--accent); border-radius: 3px; padding: 0 2px; }
.ws-item-meta { display: flex; align-items: center; gap: 8px; margin-top: 7px; flex-wrap: wrap; }
.ws-chip { font-size: 11px; color: var(--accent); background: var(--accent-soft); border-radius: 20px; padding: 1px 8px; }
.ws-item-upd { font-size: 11px; color: var(--faint); margin-left: auto; }

/* Editor — zentriert im Bildschirm */
.ws-ed { max-width: 1150px; margin: 0 auto; }
.ws-ed-head { display: flex; align-items: center; gap: 12px; margin: 2px 0 10px; flex-wrap: wrap; }
.ws-back { color: var(--muted); text-decoration: none; font-size: 13px; }
.ws-back:hover { color: var(--text); }
.ws-presence { display: flex; gap: 5px; }
.ws-pres { width: auto; min-width: 26px; height: 26px; border-radius: 20px; display: inline-flex; align-items: center; justify-content: center; gap: 2px; font-size: 11px; font-weight: 700; padding: 0 7px; }
.ws-pres.editing { outline: 1.5px solid currentColor; }
.ws-status { margin-left: auto; font-size: 12px; color: var(--faint); }
.ws-title { width: 100%; background: none; border: none; color: var(--text); font-family: var(--font-display); font-size: 24px; font-weight: 600; letter-spacing: -.02em; outline: none; padding: 2px 0 6px; border-bottom: 1px solid transparent; }
.ws-title:focus { border-bottom-color: var(--line); box-shadow: none; }
.ws-meta { display: flex; align-items: center; gap: 8px; margin: 4px 0 10px; flex-wrap: wrap; }
.ws-mini { background: var(--surface); border: 1px solid var(--line); border-radius: 7px; color: var(--text); padding: 6px 9px; font: inherit; font-size: 12px; outline: none; width: 170px; }
.ws-mini:focus { border-color: var(--accent-dim); box-shadow: none; }
.ws-toolbar { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; }
.ws-toolbar button { background: none; border: none; border-radius: 6px; color: var(--muted); cursor: pointer; font: inherit; font-size: 12.5px; padding: 6px 9px; line-height: 1; }
.ws-toolbar button:hover { background: var(--raised); color: var(--text); }
.ws-body { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; min-height: 55vh; }
.ws-body.ws-m-md { grid-template-columns: 1fr; }
.ws-body.ws-m-prev, .ws-body.ws-m-direkt { display: block; }
/* Lesen & Direkt: angenehme Lesebreite, mittig */
.ws-body.ws-m-prev #ws-prev, .ws-body.ws-m-direkt #ws-ce { max-width: 880px; margin: 0 auto; }
.ws-body #ws-ta { display: block; width: 100%; min-height: 55vh; resize: vertical; background: var(--surface); border: 1px solid var(--line); border-radius: 10px; color: var(--text); padding: 14px 16px; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; outline: none; }
.ws-body #ws-ta:focus { border-color: var(--accent-dim); box-shadow: none; }
.ws-body.ws-m-prev #ws-ta, .ws-body.ws-m-direkt #ws-ta { display: none; }
.ws-body #ws-prev { display: none; }
.ws-body.ws-m-split #ws-prev, .ws-body.ws-m-prev #ws-prev { display: block; }
.ws-body #ws-ce { display: none; }
.ws-body.ws-m-direkt #ws-ce { display: block; outline: none; }
.ws-prev { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 14px 18px; overflow-wrap: break-word; min-height: 55vh; font-size: 14px; line-height: 1.65; }
.ws-ce:focus { border-color: var(--accent-dim); }
.ws-prev h1 { font-family: var(--font-display); font-size: 21px; margin: 14px 0 10px; }
.ws-prev h2 { font-family: var(--font-display); font-size: 17px; margin: 16px 0 8px; }
.ws-prev h3 { font-size: 14.5px; margin: 14px 0 6px; }
.ws-prev h1:first-child, .ws-prev h2:first-child { margin-top: 0; }
.ws-prev p { margin: 8px 0; }
.ws-prev ul, .ws-prev ol { margin: 8px 0; padding-left: 24px; }
.ws-prev li { margin: 3px 0; }
.ws-prev code { background: var(--bg); border: 1px solid var(--line); border-radius: 5px; padding: 1px 5px; font-family: var(--font-mono); font-size: 12px; }
.ws-prev pre { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; overflow-x: auto; margin: 10px 0; }
.ws-prev pre code { background: none; border: none; padding: 0; }
.ws-prev blockquote { border-left: 3px solid var(--accent-dim); margin: 10px 0; padding: 4px 12px; color: var(--muted); }
.ws-prev table { border-collapse: collapse; margin: 10px 0; font-size: 13px; }
.ws-prev th, .ws-prev td { border: 1px solid var(--line); padding: 5px 10px; text-align: left; }
.ws-prev th { background: var(--bg); }
.ws-prev img { max-width: 100%; border-radius: 8px; border: 1px solid var(--line); }
.ws-prev a { color: var(--accent); }
.ws-prev hr { border: none; border-top: 1px solid var(--line); margin: 14px 0; }
.ws-banner { position: sticky; bottom: 14px; margin-top: 10px; background: var(--raised); border: 1px solid var(--accent-dim); border-radius: 9px; padding: 9px 13px; font-size: 12.5px; color: var(--text); box-shadow: 0 8px 24px rgba(0,0,0,.4); }

/* KI-Assistent */
.ws-ki-hint { background: rgba(230,162,86,.1); border: 1px solid rgba(230,162,86,.4); border-radius: 8px; color: var(--text); font-size: 12.5px; padding: 9px 12px; margin-bottom: 12px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* ============ Chat + Glocke ============ */
.nav-ico { position: relative; }
.nav-badge { position: absolute; top: -3px; right: -5px; min-width: 15px; height: 15px; border-radius: 8px; background: var(--p-kritisch, #F0606B); color: #fff; font-size: 9.5px; font-weight: 700; display: flex; align-items: center; justify-content: center; padding: 0 4px; pointer-events: none; }
/* Der Zaehler sitzt am Einstellungsknopf — der braucht dafuer einen
   Bezugspunkt, den die anderen Traeger schon haben (42.3). */
.wachen-halter { position: relative; }

/* ---- Der Wachenblock in den Einstellungen (42.3) ----
   Der Grund steht im VOLLTEXT und darf umbrechen: die Glocke zeigte
   „git pull endete mit 1 —…" und schnitt genau am Grund ab (Punkt 40).
   `pre-wrap`, damit mehrzeilige Gruende ihre Zeilen behalten. */
.wachen-zeile { display: grid; grid-template-columns: auto 1fr auto; gap: 6px 10px;
  align-items: baseline; padding: 8px 0; border-top: 1px solid var(--line); }
.wachen-zeile:first-child { border-top: 0; }
.wachen-kennung { font-family: var(--font-mono, monospace); font-size: 12px; word-break: break-all; }
.wachen-seit { color: var(--faint); font-size: 11.5px; white-space: nowrap; }
.wachen-grund { grid-column: 1 / -1; white-space: pre-wrap; font-family: var(--font-mono, monospace);
  font-size: 11.5px; color: var(--faint); background: var(--raised); border-radius: 6px;
  padding: 6px 8px; overflow-x: auto; }
.wachen-zeile.kaputt .wachen-kennung { color: var(--p-kritisch, #F0606B); font-weight: 600; }

/* ---- Die Vorschau der Mailtexte (55.2) ----
   `pre` und nicht `div`: der Text ist reiner Text, und die Ausrichtung der
   Beschriftungsspalte („Betrag:      812,50 €") haengt an der Leerzeichen-
   breite. In einer Proportionalschrift stuende sie schief — und dann
   sieht man in der Vorschau etwas anderes als beim Kunden. */
.mv-vorschau { margin-top: 12px; border: 1px solid var(--line); border-radius: 8px;
  overflow: hidden; }
.mv-betreff { padding: 8px 12px; background: var(--raised); font-weight: 600;
  font-size: 12.5px; border-bottom: 1px solid var(--line); }
.mv-vorschau pre { margin: 0; padding: 12px; white-space: pre-wrap;
  font-family: var(--font-mono, monospace); font-size: 12px; line-height: 1.55; }
#mv-body textarea { width: 100%; font-family: var(--font-mono, monospace);
  font-size: 12.5px; }
.bell-area .icon-btn { cursor: pointer; color: var(--muted); padding: 8px; border-radius: 9px; display: flex; }
.bell-area .icon-btn:hover { color: var(--text); background: var(--raised); }
.bell-area .icon-btn svg { width: 18px; height: 18px; }
.bell-tray { position: absolute; right: 0; top: calc(100% + 8px); width: 340px; max-height: 420px; overflow-y: auto; background: var(--raised); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 16px 40px rgba(0,0,0,.5); z-index: 250; padding: 6px; }
/* ---- Die Kopfleiste bricht um (01.09.2026, 16.2) ----
   Gemessen am 31.08.: `.bell-h` war das EINZIGE Element, das breiter war als
   sein Elter — 380 px in einem 338 px breiten Kasten, +42. Ihre zwei Kinder
   („Benachrichtigungen" 142 px und die Tastenleiste 228) ergeben 370 in 326,
   und beide stehen auf `min-width: auto`.

   Punkt 6 vom 30.08. setzte `min-width: 0` auf `.bell-tray, .bell-item,
   .bell-row`. `.bell-h` stand nicht in der Liste — eine Behebung an einer
   AUFZAEHLUNG von Selektoren ist bauartbedingt unvollstaendig.

   Warum UMBRECHEN und nicht `min-width: 0`: die Knoepfe stehen auf
   `white-space: nowrap` und koennen nicht schrumpfen. Der Druck traefe also
   den Titel, und „BENACHRICHTIGUN…" ist eine abgeschnittene Ueberschrift.
   Kuerzere Knopftexte waeren schlimmer: eine gekuerzte Handlungsaufschrift ist
   gefaehrlicher als eine zweite Zeile.

   `align-items: baseline` statt `center`, sobald umgebrochen wird — bei zwei
   Zeilen zentriert `center` jede fuer sich und der Titel rutscht. */
.bell-h {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 4px 8px;
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--faint); font-weight: 600; padding: 8px 10px 6px;
}
/* Beide Kinder duerfen schrumpfen, bevor umgebrochen wird — sonst bricht die
   Leiste schon bei einem Pixel Ueberhang. */
.bell-h > * { min-width: 0; }
/* Die Tastenleiste bekommt `flex-wrap` an ihrer BESTEHENDEN Regel weiter
   unten — eine zweite Regel fuer dasselbe Element ist eine Gelegenheit, dass
   eine davon stimmt. */
.bell-item { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; border-radius: 8px; text-decoration: none; color: var(--text); font-size: 12.5px; }
.bell-item:hover { background: var(--surface); }
/* ---- Die Art, dieselben Toene wie in der Zeitleiste (22.08.2026) ----
   `--art-*` steht ganz oben, EINMAL. Vorher haette die Glocke sie ein zweites
   Mal erfinden muessen, und dann waere „eingehend" hier blau und dort etwas
   anderes gewesen. */
.bell-item { border-left: 3px solid transparent; }
.bell-art-ein    { border-left-color: var(--art-ein); }
.bell-art-system { border-left-color: var(--art-system); }
/* `unread` ist ein ZUSTAND und keine Art: er faerbt die Flaeche, nicht die
   Kante. Vorher nahm er die Kante fuer sich, und damit war jede ungelesene
   Meldung gleich — genau die Auskunft, die dieser Umbau aufhebt. */
.bell-item.unread { background: rgba(230,162,86,.05); box-shadow: inset 0 0 0 1px var(--accent-dim); }
.bell-item span { color: var(--muted); font-size: 12px; }
/* ---- Zwei Zeilen, Schnitt an der Wortgrenze (22.08.2026) ----
   Die laengste Meldung stand als dreizeiliger Satzfetzen da und war die
   unwichtigste. Gedeckelt wird die DARSTELLUNG, nicht der Text: `vorschau`
   kommt weiter mit 120 Zeichen aus `vorschautext()`, und der Browser bricht an
   der Wortgrenze. Eine zweite Zeichenzahl im Frontend waere eine zweite
   Wahrheit ueber dieselbe Frage — und die haette bei jeder Schriftgroesse
   anders geraten.

   `-webkit-line-clamp` ist trotz des Praefixes der Standardweg; alle Browser,
   die diese Suite bedient, koennen ihn. Faellt er aus, steht der Text
   vollstaendig da — das ist der harmlose Ausgang. */
.bell-vorschau {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; line-height: 1.35;
}
/* ---- Der Zeiger auf den vollen Grund (40.2b, 07.09.2026) ----
   Er steht nur da, wenn wirklich gekuerzt ist — gemessen in app.js, nicht
   pauschal gerendert. Deshalb `hidden` in der Vorgabe: eine Regel, die
   ihn immer zeigte, waere Laerm bei jeder kurzen Meldung.
   Kleiner und blasser als die Vorschau: er ist ein Weg, keine Aussage. */
.bell-mehr {
  color: var(--accent);
  font-size: 10.5px;
  margin-top: 1px;
}
.bell-mehr[hidden] { display: none; }
.bell-item .bell-when { color: var(--faint); font-size: 10.5px; }
.bell-empty { color: var(--faint); font-size: 12.5px; padding: 14px 10px; text-align: center; }
/* Entfernen je Eintrag. Der Eintrag bleibt ein Link auf sein Ziel, das Kreuz
   sitzt daneben statt darin — ein Knopf in einem Anker ist kein gültiges
   Markup und fängt sich den Klick des Links ein. */
/* `flex-wrap` seit 01.09.2026 (16.2): zwei Knoepfe nebeneinander sind 228 px,
   und bei 326 px Kastenbreite bleibt fuer den Titel dann nichts. Sie brechen
   lieber untereinander, als den Titel abzuschneiden. */
.bell-h-tasten { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.bell-row { display: flex; align-items: stretch; gap: 2px; }
.bell-row .bell-item { flex: 1 1 auto; min-width: 0; }
.bell-del {
  flex: none; background: none; border: none; color: var(--faint);
  cursor: pointer; font-size: 16px; line-height: 1; padding: 0 9px;
  border-radius: 8px; font-family: var(--font-sans, inherit);
}
.bell-del:hover { color: var(--danger); background: var(--surface); }

/* `--einspaltig` ist das Signal an app.js, ob Liste und Verlauf nebeneinander
   stehen. Die Grenze steht damit an EINER Stelle — im Stylesheet. Eine Zahl in
   matchMedia() waere die zweite, und die laeuft auseinander, sobald jemand den
   Umbruch verschiebt. */
.ch-layout { --einspaltig: 0; display: grid; grid-template-columns: 250px 1fr; gap: 16px; min-height: 0; }
/* Ohne das wachsen die Spalten ueber die Layouthoehe hinaus und ihr
   `overflow-y: auto` bleibt wirkungslos — der Fehler, um den es in 7.1 ging. */
.ch-layout > * { min-height: 0; }
/* `overflow-x: hidden` ausdruecklich: sobald EINE Achse `auto` ist, wird die
   andere von `visible` auf `auto` hochgestuft — die Schiene bekaeme also wieder
   einen waagerechten Bildlauf, sobald ein Kind einmal zu breit ist. Hier will
   den niemand. Gesetzt genau an dem Element, das den Ueberhang hatte, nicht
   flaechendeckend. */
.ch-rail { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 12px; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }
/* Breite und `min-width: 0` stehen an `.td-rail-item` — die Begruendung dort.
   Hier bleibt nur, was WIRKLICH chatspezifisch ist: die Zeile ist zweizeilig
   (Name plus Vorschau), deshalb oben ausrichten statt mittig. */
.ch-rail-item { align-items: flex-start; }
.ch-rail-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; text-align: left; }
/* Auch der Name kuerzt: ein langer Chatname darf die Marken nicht verdraengen. */
.ch-rail-main > b { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-rail-last { color: var(--faint); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 400; }
.ch-unread { background: var(--p-kritisch, #F0606B); color: #fff; }
/* Zaehler UND Nadel nebeneinander in einer Spalte: keins verdeckt das andere.
   `align-items: center`, damit die Nadel neben einem zweistelligen Zaehler
   nicht nach unten rutscht. */
.ch-rail-marken { flex: none; display: flex; align-items: center; gap: 5px; }
.ch-pin {
  flex: none; width: 17px; height: 17px; display: flex; align-items: center;
  justify-content: center; color: var(--faint); cursor: pointer; opacity: 0;
  border-radius: 5px;
}
.ch-pin svg { width: 13px; height: 13px; }
/* Unsichtbar, bis man auf die Zeile zeigt — angeheftet ist sie immer sichtbar,
   sonst waere die Kennzeichnung weg, sobald die Maus wegfaehrt. */
.ch-rail-item:hover .ch-pin, .ch-pin:focus-visible { opacity: 1; }
.ch-pin.an { opacity: 1; color: var(--accent); }
.ch-pin:hover { background: var(--surface); color: var(--accent); }
/* Ohne gewaehlten Chat gibt es keinen Empfaenger — dann ist die Eingabe aus. */
.ch-composer.aus { opacity: .45; pointer-events: none; }
.ch-thread { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); min-width: 0; min-height: 0; }
/* Kopfzeile und Eingabefeld wandern nicht mit: nur `.ch-msgs` scrollt. */
.ch-head { flex: none; padding: 12px 16px; border-bottom: 1px solid var(--line); font-size: 14px; display: flex; align-items: baseline; gap: 10px; }
/* Zurueck zur Liste — nur in der einspaltigen Ansicht sichtbar. */
.ch-zurueck { display: none; flex: none; align-self: center; background: none; border: none; color: var(--accent); cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px 0 0; }
.ch-head-hint { color: var(--faint); font-size: 11.5px; }
/* Anwesenheit (Punkt 4): ein Punkt vor dem Namen — gruen eingestempelt und
   taetig, gelb eingestempelt aber seit ueber einer Stunde nicht in der Suite,
   grau ausgestempelt. Farben aus den vorhandenen Zustandsfarben. */
.anw-punkt { display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle; background: var(--faint); }
.anw-punkt.gruen { background: var(--ok); }
.anw-punkt.gelb  { background: var(--p-hoch); }
.anw-punkt.grau  { background: transparent; box-shadow: inset 0 0 0 1.5px var(--faint); }
.ch-tippt { color: var(--faint); font-size: 12px; font-style: italic; }
.ch-msgs { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
.ch-msg { display: flex; gap: 9px; max-width: 78%; }
.ch-msg.mine { align-self: flex-end; flex-direction: row-reverse; }
.ch-ava { flex: none; width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; margin-top: 2px; }
.ch-bubble { background: var(--raised); border: 1px solid var(--line-soft); border-radius: 11px; padding: 7px 11px; min-width: 0; }
.ch-msg.mine .ch-bubble { background: rgba(230,162,86,.1); border-color: rgba(230,162,86,.25); }
.ch-meta { color: var(--faint); font-size: 10.5px; margin-bottom: 2px; display: flex; align-items: center; gap: 6px; }
.ch-del { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 10px; padding: 0 3px; opacity: 0; }
.ch-msg:hover .ch-del { opacity: 1; }
.ch-del:hover { color: var(--p-kritisch); }
.ch-body { font-size: 13.5px; line-height: 1.5; overflow-wrap: break-word; }
.ch-body .mention { color: var(--accent); text-decoration: none; background: rgba(230,162,86,.1); border-radius: 5px; padding: 0 4px; }
.ch-body .mention-user { color: var(--accent); font-weight: 600; }
.ch-composer { flex: none; display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line); align-items: flex-end; }
.ch-attach { flex: none; padding: 8px 11px; font-size: 15px; line-height: 1.2; }
.ch-input { flex: 1; min-width: 0; min-height: 40px; max-height: 160px; overflow-y: auto; background: var(--bg); border: 1px solid var(--line); border-radius: 10px; color: var(--text); font: inherit; font-size: 13px; padding: 9px 12px; outline: none; line-height: 1.5; }
.ch-input:focus { border-color: var(--accent-dim); }
.ch-input:empty::before { content: attr(data-placeholder); color: var(--faint); pointer-events: none; }
.ch-input .ch-chip { user-select: all; }
.ch-input-img { max-height: 64px; max-width: 140px; border-radius: 7px; border: 1px solid var(--line); vertical-align: middle; margin: 2px 3px; }
.ch-chip { display: inline-block; background: rgba(230,162,86,.12); border: 1px solid rgba(230,162,86,.3); border-radius: 6px; color: var(--accent); font-size: 12px; padding: 1px 7px; margin: 0 1px; vertical-align: baseline; white-space: nowrap; }
.ch-img { max-width: 320px; max-height: 240px; border-radius: 9px; border: 1px solid var(--line); display: block; margin: 4px 0; }
.ch-edit { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 10px; padding: 0 3px; opacity: 0; }
.ch-msg:hover .ch-edit { opacity: 1; }
.ch-edit:hover { color: var(--accent); }
.ch-edited { color: var(--faint); font-size: 10px; }
.ch-read { color: var(--faint); font-size: 10px; letter-spacing: -1px; }
.ch-read.on { color: var(--ok, #4EC58C); }
.ch-edit-ta { width: 100%; min-width: 240px; background: var(--bg); border: 1px solid var(--accent-dim); border-radius: 8px; color: var(--text); font: inherit; font-size: 13px; padding: 7px 9px; resize: vertical; }
.ch-edit-acts { display: flex; gap: 6px; margin-top: 5px; }
.ch-thread.ch-dropover { outline: 2px dashed var(--accent-dim); outline-offset: -4px; }
/* To-dos: eigene Listen */
.td-rail-menu { margin-left: auto; color: var(--faint); font-size: 13px; padding: 0 4px; border-radius: 5px; opacity: 0; cursor: pointer; }
.td-rail-item:hover .td-rail-menu { opacity: 1; }
.td-rail-menu:hover { background: var(--raised); color: var(--text); }
.td-rail-new { color: var(--muted); }
.td-rail-new:hover { color: var(--text); }
/* Kalender: Kanban-Karten */
.kal-ptask { cursor: pointer; }
.kal-chip-sub { color: var(--faint); }

/* Bestand: große Mengen-Steuerung im Detail */
.li-qty-big { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 14px 16px 4px; }
.li-qty-val { min-width: 110px; text-align: center; font-family: var(--font-display); font-size: 26px; font-weight: 600; }
.li-qty-val small { color: var(--muted); font-size: 13px; font-weight: 400; }
/* Chat auf schmalen Screens: EINE Spalte, Liste ODER Verlauf.

   Vorher lag die Chatliste als horizontale Leiste ueber dem Verlauf — mit der
   Begruendung „statt sie zu verstecken, sonst kann man keinen Chat waehlen".
   Die gilt nicht mehr, seit es den Zurueck-Pfeil gibt: gestaucht sind beide
   Bereiche unbrauchbar, und die Leiste kostete oben Platz, der dem Verlauf
   fehlte. Welcher Bereich sichtbar ist, entscheidet `zeigt-verlauf` (app.js). */
@media (max-width: 900px) {
  .ch-layout { --einspaltig: 1; grid-template-columns: 1fr; gap: 0; }
  .ch-layout.zeigt-verlauf .ch-rail { display: none; }
  .ch-layout:not(.zeigt-verlauf) .ch-thread { display: none; }
  .ch-zurueck { display: block; }
  .ch-msg { max-width: 92%; }
}
/* Kalender: Team-Chip */
.kal-who-team { background: var(--raised); }

/* Autocomplete (Artikel-Vorschläge, @-Mentions) — gestyltes Menü statt <datalist> */
.ac-menu { position: fixed; z-index: 400; max-width: 460px; background: var(--raised); border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,.5); padding: 5px; max-height: 280px; overflow-y: auto; }
.ac-it { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%; background: none; border: none; border-radius: 7px; color: var(--text); cursor: pointer; font: inherit; font-size: 13px; padding: 7px 10px; text-align: left; }
.ac-it:hover, .ac-it.active { background: var(--surface); }
.ac-it .ac-l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-it .ac-s { color: var(--faint); font-size: 11px; flex: none; }

/* @-Mentions in Notizen */
.msg-body .mention { color: var(--accent); text-decoration: none; background: rgba(230,162,86,.1); border-radius: 5px; padding: 0 4px; }
.msg-body .mention:hover { text-decoration: underline; }
.msg-body .mention-user { color: var(--accent); font-weight: 600; }

/* Anklickbare Adressen in Nachrichtentext (Stufe 1, shared/linkify.js).
   Gilt fuer Chat, WhatsApp und Mail gleich — eine Regel, kein Kanal-Sonderfall.
   `overflow-wrap` statt `word-break`: eine lange Adresse darf umbrechen, aber
   nicht mitten in einem kurzen Wort daneben. */
.msg-body .lk, .ch-body .lk {
  color: #1a6dd0; text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px; overflow-wrap: anywhere;
}
.msg-body .lk:hover, .ch-body .lk:hover { text-decoration-thickness: 2px; }
/* Der Hostname-Hinweis steht NEBEN dem Link, damit der Linktext die Adresse
   bleibt: `vistаprint.de` mit kyrillischem a ist `xn--vistprint-3yh.de`. */
.msg-body .lk-hinweis, .ch-body .lk-hinweis {
  margin-left: 4px; font-size: .82em; color: #b4531a;
  background: rgba(180,83,26,.09); border-radius: 4px; padding: 0 4px;
  white-space: nowrap;
}

/* Positions-Editor: mehrzeilige Beschreibungen + Inline-Bearbeitung */
.rk-line-name { white-space: normal; }
.rk-line-acts { white-space: nowrap; }
.rk-line-acts .btn + .btn { margin-left: 4px; }
.rk-edit-name { width: 100%; background: var(--bg); border: 1px solid var(--accent-dim); border-radius: 7px; color: var(--text); font: inherit; font-size: 12.5px; padding: 6px 8px; resize: vertical; }
.rk-edit-qty, .rk-edit-price { width: 84px; background: var(--bg); border: 1px solid var(--accent-dim); border-radius: 7px; color: var(--text); font: inherit; font-size: 12.5px; padding: 6px 8px; text-align: right; }

/* Slash-Befehle im Editor */
.ws-slash { position: absolute; z-index: 50; min-width: 235px; background: var(--raised); border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,.5); padding: 5px; max-height: 300px; overflow-y: auto; }
.ws-slash-it { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; background: none; border: none; border-radius: 7px; color: var(--text); cursor: pointer; font: inherit; font-size: 13px; padding: 7px 10px; text-align: left; }
.ws-slash-it:hover, .ws-slash-it.active { background: var(--surface); }
.ws-slash-it code { color: var(--faint); font-family: var(--font-mono); font-size: 11px; }

/* Schwebende Tabellen-Leiste im Direkt-Modus */
.ws-tblbar { position: absolute; z-index: 45; display: flex; gap: 3px; background: var(--raised); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.45); padding: 4px; }
.ws-tblbar button { background: none; border: none; border-radius: 6px; color: var(--muted); cursor: pointer; font: inherit; font-size: 11.5px; padding: 5px 8px; line-height: 1; white-space: nowrap; }
.ws-tblbar button:hover { background: var(--surface); color: var(--text); }
.ws-tblbar .ws-tbl-del:hover { color: var(--p-kritisch); }
.ws-ce table { min-width: 300px; }
.ws-ce td, .ws-ce th { min-width: 60px; }

/* Presence: wo der andere gerade tippt */
.ws-pres-sec { display: inline-flex; align-items: center; font-size: 10.5px; color: var(--accent); background: var(--accent-soft); border-radius: 20px; padding: 3px 9px; max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-prev .ws-other-edit::after { content: '✏️ ' attr(data-editor); font-size: 10.5px; font-weight: 500; color: var(--accent); background: var(--accent-soft); border-radius: 20px; padding: 2px 8px; margin-left: 10px; vertical-align: middle; }

/* Historie */
.ws-hist { display: grid; grid-template-columns: 240px 1fr; gap: 14px; height: 55vh; }
.ws-hist-list { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 4px; }
.ws-hist-row { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; color: var(--text); cursor: pointer; font: inherit; font-size: 12px; padding: 8px 10px; text-align: left; }
.ws-hist-row:hover { background: var(--raised); }
.ws-hist-row.active { border-color: var(--accent-dim); color: var(--accent); }
.ws-hist-prev { overflow-y: auto; min-height: 0; }
.ws-hist-prev .ws-prev { min-height: 0; }

@media (max-width: 980px) {
  .ws-layout { flex-direction: column; }
  .ws-rail { position: static; width: 100%; flex-direction: row; flex-wrap: wrap; }
  .ws-body { grid-template-columns: 1fr; }
  .ws-hist { grid-template-columns: 1fr; }
}

@media (max-width: 980px) {
  .td-layout { flex-direction: column; gap: 14px; }
  .td-rail { position: static; width: 100%; flex-direction: row; flex-wrap: wrap; }
  .td-rail-h { display: none; }
  .td-rail-item .td-rcnt { margin-left: 6px; }
}

/* ============ Handy-Ansicht (Hochformat) ============
   MUSS am DATEIENDE stehen: gleiche Spezifität wie die Basis-Regeln —
   nur die Quelltext-Reihenfolge lässt diese Overrides gewinnen. */
@media (max-width: 700px) {
  .modbar { flex-wrap: wrap; height: auto; padding: 8px 10px; gap: 6px 10px; }
  .modbar-left { order: 2; flex: 1 1 100%; gap: 8px; }
  .modbar-right { order: 1; width: 100%; gap: 6px; justify-content: flex-end; }
  .modbar-title, .modbar-div { display: none; }
  #hd-search { display: none !important; }
  #btn-new-ticket { font-size: 0; gap: 0; padding: 8px 9px; }
  #btn-new-ticket svg { width: 16px; height: 16px; }
  #timer-btn-label { display: none; }
  .timer-btn { padding: 8px 9px; }
  /* Trays: fest, volle Breite — nichts läuft aus dem Bildschirm */
  .timer-tray, .bell-tray { position: fixed; left: 8px; right: 8px; top: 100px; width: auto; max-width: none; max-height: calc(100dvh - 120px); }
  .suite-menu { position: fixed; left: 8px; right: 8px; top: 100px; min-width: 0; }
  /* Breite Inhalte (Rechnungs-Vorschau, Tabellen) dürfen seitlich gescrollt werden */
  .main { padding: 12px 10px 40px; overflow-x: auto; }
  .rk-tbl-wrap { overflow-x: auto; }
  /* iOS zoomt bei Fokus auf Felder <16px ins Bild — 16px verhindert das komplett */
  input, select, textarea, .ch-input { font-size: 16px !important; }
  .rk-head { flex-wrap: wrap; gap: 10px; }
  .rk-toolbar { flex-wrap: wrap; gap: 8px; }
  .kal-tg { height: calc(100dvh - 320px); }
  .stat-grid { grid-template-columns: 1fr 1fr; }
  .rk-modal { width: calc(100vw - 20px); max-height: calc(100dvh - 30px); overflow-y: auto; }
  .kf { grid-template-columns: 1fr; }
  .inv-doc { width: 100%; min-height: 0; padding: 16px; }
  .td-layout, .ws-layout { flex-direction: column; }
}

/* ============ Leuchtkasten (Bilder in Verläufen) ============
   Liegt an `body`, nicht im Verlauf — deshalb kann das Mausrad darüber den
   Verlauf darunter nicht bewegen. z-index über dem Vorschlagsmenü (400), das
   sonst durch das Bild stechen würde. */
.lkasten {
  position: fixed; inset: 0; z-index: 500;
  display: flex; flex-direction: column;
  background: rgba(5,8,12,.86);
  backdrop-filter: blur(3px);
}
.lkasten-leiste {
  flex: none;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 11px;
  min-width: 0;
}
.lkasten-name {
  /* Der Name darf schmaler werden als sein Inhalt — sonst drückt ein langer
     Dateiname die Knöpfe aus dem Fenster (dieselbe Bindung wie bei 8.0). */
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; color: #e8ecf1;
}
.lkasten-zaehler { flex: none; font-size: 12px; color: #9aa4b1; font-variant-numeric: tabular-nums; }
.lkasten-knopf, .lkasten-pfeil {
  flex: none;
  background: rgba(255,255,255,.10);
  border: 1px solid rgba(255,255,255,.18);
  color: #e8ecf1;
  border-radius: 8px;
  font: inherit; font-size: 13px;
  padding: 6px 11px;
  cursor: pointer;
  text-decoration: none;
  line-height: 1.2;
}
.lkasten-pfeil { padding: 4px 11px; font-size: 19px; }
.lkasten-knopf:hover, .lkasten-pfeil:hover { background: rgba(255,255,255,.18); }
.lkasten-zu { font-size: 15px; padding: 5px 11px; }
.lkasten-buehne {
  /* `min-height: 0` ist die Bedingung dafür, dass das Bild schrumpft statt die
     Bühne zu sprengen — ohne das läuft ein hohes Bild aus dem Fenster. */
  flex: 1; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 0 14px 16px;
}
.lkasten-bild {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
  background: rgba(255,255,255,.04);
}
.lkasten-fehler {
  max-width: 420px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 18px 20px;
  color: var(--text); font-size: 13px; line-height: 1.5;
  text-align: center;
}
.lkasten-fehler b { display: block; margin-bottom: 6px; }
.lkasten-fehler p { color: var(--muted); margin: 0 0 8px; }
.lkasten-fehler-name { font-family: var(--font-mono); font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }

@media (max-width: 560px) {
  /* Auf 390 px muss der Schließknopf erreichbar bleiben. Der Name weicht, er
     ist die Information, die man am ehesten entbehrt. Dann übernimmt der Zähler
     dessen `flex: 1` — sonst fällt mit dem Namen auch der Platzhalter weg und
     die Knöpfe rutschen nach links, mit toter Fläche rechts daneben (gemessen:
     Schließknopf endete bei 288 von 390). */
  .lkasten-name { display: none; }
  .lkasten-zaehler { flex: 1; }
  .lkasten-knopf { padding: 6px 9px; }
  .lkasten-buehne { padding: 0 8px 12px; }
}

/* ============ Fälligkeit: Schnellauswahl (10.1) ============
   Fest positioniert und an `body` gehängt, damit dasselbe Menü im Composer
   (nicht überlappend) und in einem Modal (über dessen Rand hinaus) funktioniert.
   z-index über dem Modal-Hintergrund (50) und über `.td-pop` (40). */
.fl-pop {
  position: fixed; z-index: 420;
  min-width: 232px;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: 11px; box-shadow: 0 12px 32px rgba(0,0,0,.45);
  padding: 6px;
  display: flex; flex-direction: column;
}
.fl-eintrag {
  display: flex; align-items: center; gap: 12px;
  background: none; border: none; color: var(--text);
  cursor: pointer; font: inherit; font-size: 13px;
  padding: 8px 10px; border-radius: 7px; text-align: left;
}
.fl-eintrag:hover, .fl-eintrag:focus-visible { background: var(--surface); outline: none; }
.fl-text { flex: 1; min-width: 0; }
/* Das Datum steht am Eintrag — die Definition von „Nächste Woche" ist damit
   sichtbar und steht nicht nur im Code. */
.fl-datum { flex: none; color: var(--faint); font-size: 11px; font-variant-numeric: tabular-nums; }
.fl-eintrag.gewaehlt { background: var(--surface); }
.fl-eintrag.gewaehlt .fl-text::after { content: ' ·'; color: var(--accent); }
.fl-kal { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 10px; }
.fl-keins { color: var(--muted); }
.fl-versteckt { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* ============ Kleines Menü (Umbenennen/Löschen) — 10.3 ============ */
.rk-menu {
  position: fixed; z-index: 420;
  min-width: 168px;
  background: var(--raised); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,.45);
  padding: 5px;
  display: flex; flex-direction: column;
}
.rk-menu-p {
  background: none; border: none; color: var(--text);
  cursor: pointer; font: inherit; font-size: 13px;
  padding: 8px 10px; border-radius: 7px; text-align: left;
}
.rk-menu-p:hover, .rk-menu-p:focus-visible { background: var(--surface); outline: none; }
.rk-menu-p.gefahr { color: var(--p-kritisch); }

/* „Noch nicht verteilt" muss anders aussehen als „Team" — gestrichelt und
   blass gegen gefüllt und farbig. Sähen beide gleich aus, wäre die
   Unterscheidung nach zwei Wochen tot (10.2). */
.td-assignee.offen {
  color: var(--faint);
  background: none;
  border: 1px dashed var(--line);
  padding: 0 7px;
}

/* Ein gespeicherter Wert, den die Optionsliste nicht kennt (11.1). Die
   Gestaltung von <option> ist je Browser begrenzt — die Beschriftung
   „unbekannt (…)" traegt die Aussage, die Farbe ist Zugabe. */
.opt-unbekannt { color: var(--p-kritisch); font-style: italic; }

/* Ein gespeicherter Wert, den ein FILTER ausgelassen hat (N1.2): gueltig, aber
   nicht neu waehlbar. Anders als `.opt-unbekannt` — dort ist der Wert wirklich
   unbekannt, hier nur ausserhalb des Filters. */
.opt-ausserhalb { color: var(--muted); }

/* Ein ausgeschiedener Zustaendiger: aufloesbar, aber nicht wie ein aktiver.
   Sonst haelt man die Aufgabe fuer verteilt. */
.td-assignee.inaktiv { color: var(--muted); background: none; border: 1px dashed var(--line); text-decoration: line-through; }
.user-row.inaktiv .ur-name { color: var(--muted); text-decoration: line-through; }
.ur-bezuege { margin: 8px 0 0; padding-left: 18px; font-size: 12px; color: var(--muted); line-height: 1.7; }

/* Kunde und Ticket an einer Aufgabe (10.5). Der Kunde ist eine EIGENSCHAFT der
   Aufgabe, keine Schublade — deshalb ein Merker in der Zeile und keine Liste. */
.td-kunde { font-size: 11px; color: var(--muted); background: var(--raised); border-radius: 20px; padding: 1px 8px; }
.td-ticket { font-size: 11px; color: var(--accent); text-decoration: none; border-bottom: 1px dotted currentColor; }
.td-ticket:hover { border-bottom-style: solid; }
.td-kundefilter { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.td-kundefilter select { background: var(--raised); border: 1px solid var(--line); border-radius: 7px; color: var(--text); font: inherit; font-size: 12px; padding: 4px 7px; max-width: 190px; }

/* Der Strom ist unterbrochen (T1). Der Punkt blinkt, weil ein stiller roter
   Punkt in einer Kopfzeile nicht auffaellt — und genau das Auffallen ist der
   Zweck: eine Ansicht, die sich fuer live haelt ohne es zu sein, ist
   gefaehrlicher als eine stumme. */
.api-dot.getrennt { background: var(--danger); animation: dot-puls 1.4s ease-in-out infinite; }
@keyframes dot-puls { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* ============ Schrägstrich-Befehle (11.9) ============
   Der Befehlsmodus ist SICHTBAR und beschriftet. „Woran erkenne ich, dass Enter
   jetzt nichts sendet" darf keine Ratefrage sein — im Kundenfenster am
   allerwenigsten. */
/* `.ch-befehl` ist am 13.08.2026 entfallen — im Chat erklärt kein Band mehr,
   dass Enter nichts sendet; Enter öffnet den Dialog, der Rahmen wechselt die
   Farbe und der Senden-Knopf geht weg. `.wa-befehl` BLEIBT: dort sagt das Band
   etwas anderes, nämlich dass der Text nicht an den Kunden geht. */
.wa-befehl {
  font-size: 12px; line-height: 1.4;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 5px 9px;
  margin-bottom: 6px;
}
/* Der Rahmen des Eingabefelds wechselt mit — die Beschriftung allein übersieht
   man, wenn man auf die Tastatur schaut. */
.ch-composer.befehl .ch-input,
.compose.befehl .wa-text {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* „Neue Nachricht ↓" im Chat (13.08.2026).
   Sitzt zwischen Verlauf und Eingabefeld, nicht darüber schwebend: ein Element,
   das den Text verdeckt, den man gerade liest, ist selbst der Sprung, den es
   verhindern soll. */
.ch-neu {
  align-self: center;
  margin: 0 0 6px;
  padding: 4px 12px;
  font-size: 12px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  border: none;
  cursor: pointer;
}
.ch-neu:hover { filter: brightness(1.08); }

/* Trennlinie „ab hier neu" im Chat (13.08.2026).
   Der Verlauf oeffnet am Ende — die Linie ist der Weg zurueck zur Stelle, an
   der man aufgehoert hat. Sie steht IM Verlauf und scrollt mit; ein Etikett,
   das oben klebt, waere eine zweite Behauptung ueber denselben Ort. */
.ch-neu-linie {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 6px;
  color: var(--accent);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.ch-neu-linie::before, .ch-neu-linie::after {
  content: ""; flex: 1; height: 1px; background: var(--accent); opacity: .45;
}

/* Warnung über einem Beleg, dessen Positionen nicht auf die Summe hinauslaufen
   (13.08.2026). Rot und nicht zu übersehen: sie steht zwischen jemandem und
   einer Rechnung, die er gleich verschickt. Sie ist NICHT Teil des Dokuments —
   das PDF enthält sie nie, dort bricht der Weg stattdessen ab. */
.inv-warnung {
  margin: 0 0 14px;
  padding: 10px 14px;
  border-radius: 8px;
  background: #7f1d1d;
  color: #fff;
  font-size: 13px;
  line-height: 1.5;
  font-weight: 600;
}

/* ---------- Push: Stummtaste, Geraeteliste (17.08.2026) ---------- */
.push-stumm { display: flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 13px; cursor: pointer; }
.push-geraete { list-style: none; padding: 0; margin: 8px 0 10px; font-size: 12px; color: var(--muted); }
.push-geraete li { padding: 3px 0 3px 10px; border-left: 2px solid var(--line); }
.push-geraete li.fehler { border-left-color: var(--danger, #E0757F); color: var(--text); }

/* ---------- Schnell-Zettel (18.08.2026) ----------
   Formsprache wie die Glocke: derselbe Rahmen, dieselbe Breite, damit die zwei
   Klappen in der Leiste nicht nach zwei Bauteilen aus zwei Jahren aussehen. */
.notiz-area { position: relative; }
.nav-punkt { position: absolute; top: 6px; right: 6px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px var(--surface); }
.notiz-tray { position: absolute; right: 0; top: calc(100% + 8px); width: min(380px, calc(100vw - 24px));
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow, 0 12px 32px rgba(0,0,0,.35)); padding: 10px; z-index: 60;
  display: flex; flex-direction: column; gap: 8px; }
.notiz-h { display: flex; align-items: baseline; gap: 8px; font-family: var(--font-display); font-size: 13px; }
.notiz-stand { margin-left: auto; color: var(--faint); font-size: 11px; }
/* Hoch genug für ein paar Zeilen, nicht so hoch, dass die Klappe den halben
   Bildschirm nimmt — auf dem Telefon ist die Leiste schmal. */
#notiz-feld { width: 100%; min-height: 160px; resize: vertical; font: inherit; font-size: 13px;
  line-height: 1.5; padding: 8px 9px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--raised); color: var(--text); }
.notiz-foot { display: flex; align-items: center; gap: 6px; }
.notiz-foot .foot-spacer { flex: 1; }
.notiz-foot select { font-size: 12px; padding: 3px 6px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--raised); color: var(--text); max-width: 110px; }
@media (max-width: 560px) {
  .notiz-tray { position: fixed; left: 12px; right: 12px; width: auto; }
  #notiz-feld { min-height: 200px; }
}

/* ---------- Die Handloeschung auf Antrag (24.09.2026) ---------- */
.hl-gruende { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 6px 0 12px; }
.hl-grund { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.hl-teil { margin: 8px 0 4px; font-size: 13px; }
.hl-liste { margin: 0 0 8px 18px; font-size: 12.5px; color: var(--muted); }
.hl-fehler { margin-top: 10px; padding: 8px 10px; border-radius: var(--radius-sm);
  background: rgba(240,96,107,.12); color: var(--danger); font-size: 12.5px; line-height: 1.5; }

/* ---------- Das Wochenziel eines Mitarbeiters (123, 24.09.2026) ---------- */
.wz-stand { font-size: 13px; margin: 4px 0 12px; }
.wz-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 12px; }
.wz-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.wz-form .wz-grund { grid-column: 1 / -1; }
.wz-liste { width: 100%; border-collapse: collapse; font-size: 12px; }
.wz-liste td { padding: 4px 6px; border-top: 1px solid var(--line); vertical-align: top; }
.wz-liste td.mono { white-space: nowrap; }
.wz-liste-grund, .wz-liste-von, .wz-leer { color: var(--muted); }
.wz-leer { font-size: 12px; }

/* ---------- Module eines Mitarbeiters (25.08.2026) ---------- */
.um-hinweis { font-size: 12px; color: var(--muted); margin: 6px 0 10px; line-height: 1.5; }
.um-warn { font-size: 12px; color: var(--warn, #d08a2a); margin: 6px 0 10px; line-height: 1.5; }
.um-liste { display: flex; flex-direction: column; gap: 2px; }
.um-zeile { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border-radius: 5px; font-size: 13px; }

/* Der rohe Schluessel neben dem Namen: die Maske zeigt seit dem 27.08.2026
   „Verträge" statt „vertraege". Der Schluessel bleibt sichtbar, weil er in
   Protokollen und in der Spur („gesetzt von") auftaucht — wer dort „vertraege"
   liest, soll es hier wiederfinden. */
.um-zeile .um-key { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; }.um-zeile:hover { background: var(--raised); }
.um-zeile input[disabled] + .um-name { opacity: .6; }
.um-name { flex: 1; }
/* Die Spur steht MIT in der Zeile — wer wann was freigegeben hat, ist bei
   Berechtigungen die erste Frage, und eine Spur, die niemand sieht, beantwortet
   sie nicht. */
.um-spur { font-size: 11px; color: var(--faint); font-family: var(--font-mono); }

/* ---------- Warnung, die kein Fehler ist (26.08.2026) ----------
   Die Ueberlappungswarnung erscheint, WEIL das Speichern geklappt hat. Rot waere
   hier die falsche Auskunft: der Leser will zuerst wissen, ob seine Eingabe
   steht. */
.toast.warn { border-left-color: var(--warn, #d08a2a); }

/* ---------- Dokumentenindex: Baum und Liste (26.08.2026) ---------- */
.dx-wrap { display: grid; grid-template-columns: 260px 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) { .dx-wrap { grid-template-columns: 1fr; } }
.dx-baum { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 10px; }
.dx-knoten > summary { cursor: pointer; display: flex; align-items: center; gap: 8px;
  padding: 5px 4px; font-weight: 600; font-size: 13px; }
.dx-knoten > summary::-webkit-details-marker { display: none; }
.dx-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dx-zahl { color: var(--muted); font-size: 12px; font-family: var(--font-mono); }
.dx-ohne { color: var(--warn, #d08a2a); margin-left: 2px; }
.dx-kind { display: flex; width: 100%; align-items: center; gap: 8px; padding: 4px 6px 4px 18px;
  background: none; border: 0; color: var(--text); font-size: 13px; text-align: left;
  border-radius: 5px; cursor: pointer; }
.dx-kind:hover { background: var(--raised); }
.dx-kind.aktiv { background: var(--raised); font-weight: 600; }
.dx-kind > span:first-child { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dx-liste { min-width: 0; overflow-x: auto; }
/* „ohne Datum" ist ein ZUSTAND und kein leeres Feld — es steht da, statt zu fehlen. */
.dx-ohne-datum { color: var(--faint); font-style: italic; }
.dx-warn { color: var(--warn, #d08a2a); }
.dx-verwaist { opacity: .65; }

/* ---------- Anhang in die Ablage uebernehmen (27.08.2026) ---------- */
.att-wrap, .ch-img-wrap { position: relative; display: inline-block; }
.att-ue {
  display: inline-flex; align-items: center; gap: 3px;
  margin-left: 6px; padding: 2px 7px;
  font-size: 11px; font-family: inherit; line-height: 1.6;
  background: var(--raised); color: var(--muted);
  border: 1px solid var(--line); border-radius: 5px; cursor: pointer;
}
.att-ue:hover { color: var(--text); border-color: var(--accent); }
/* Am Bild sitzt er IN der Ecke — sonst schiebt er das Bild aus der Zeile. */
.att-wrap .att-ue, .ch-img-wrap .att-ue {
  position: absolute; right: 6px; bottom: 6px; margin: 0;
  background: rgba(20,22,28,.82); backdrop-filter: blur(2px);
}
.ue-hinweis { font-size: 12px; color: var(--muted); line-height: 1.5; margin-bottom: 10px; }
.ue-hinweis.warn { color: var(--warn, #d08a2a); }
.ue-kunde { font-size: 13px; line-height: 1.5; }
.ue-kunde .hint { color: var(--muted); font-size: 12px; }

/* ---------- Der Auswahlbogen im Rechnungsmodul (28.08.2026) ----------
   Gruppen nach Ticket, Haken je Zeile, Gegenrechnung darunter. Die Farben
   folgen dem Zustand, nicht der Laune: was nicht mitgeht, tritt zurueck. */
.bg-tbl .bg-mark { width: 34px; text-align: center; }
.bg-tbl .bg-tag { width: 62px; color: var(--muted); white-space: nowrap; }
.bg-tbl .bg-txt { min-width: 0; }
.bg-tbl tbody.bg-gruppe { border-top: 1px solid var(--line); }
.bg-kopf { background: var(--bg); cursor: pointer; }
.bg-kopf td { padding-top: 9px; padding-bottom: 9px; }
.bg-kopf .bg-titel { color: var(--muted); margin-left: 8px; font-weight: 400; }
.bg-zeile.abgewaehlt { opacity: .5; }
/* Abgerechnet und nicht abrechenbar sind KEINE Entscheidung des Nutzers —
   sie bekommen deshalb keinen leeren Haken, sondern ein Zeichen. */
.bg-zeile.abgerechnet, .bg-zeile.nicht_abrechenbar { color: var(--muted); }
.bg-fest { color: var(--faint); cursor: default; }
.bg-beleg { font-size: 11px; color: var(--muted); font-family: ui-monospace, monospace; margin-left: 6px; }
.bg-zeile.unbekannt { background: rgba(224,90,90,.10); }

.bg-gegen { margin-top: 14px; border-top: 2px solid var(--line); padding-top: 10px; }
.bg-gegen-tbl { width: 100%; font-size: 13px; border-collapse: collapse; }
.bg-gegen-tbl td { padding: 3px 6px; }
.bg-gegen-tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
.bg-gz.gewaehlt td { font-weight: 600; color: var(--text); }
.bg-gz.abgewaehlt td, .bg-gz.abgerechnet td, .bg-gz.nicht_abrechenbar td { color: var(--muted); }
.bg-gz.unbekannt td { color: var(--p-kritisch); font-weight: 600; }
.bg-gz.summe td { border-top: 1px solid var(--line); padding-top: 7px; font-weight: 600; }
.bg-gegen.kaputt { border-top-color: var(--p-kritisch); }
.bg-gegen.kaputt .bg-gz.summe td { color: var(--p-kritisch); }
.bg-warn { margin-top: 8px; font-size: 12.5px; color: var(--p-kritisch);
  background: rgba(224,90,90,.08); border: 1px solid rgba(224,90,90,.3);
  border-radius: var(--radius-sm); padding: 8px 10px; }
/* Die Anzahl gehoert zum Titel, nicht in die Stundenspalte: sie stand dort und
   schob Stunden und Satz je eine Spalte nach rechts. In den Zahlen war das
   nicht zu sehen, im Bild sofort. */
.bg-kopf .bg-anz { color: var(--muted); margin-left: 10px; font-weight: 400; font-size: 12px; }
/* Der Zeitraum in der Kopfzeile des Bogens: eine Vollstaendigkeitsaussage ueber
   einen Zeitraum, den sie nicht benennt, ist keine Auskunft. */
.panel-head .bg-zeitraum { color: var(--muted); font-weight: 400; font-size: 12.5px; margin-left: 12px; }

/* ---------- Anfahrtspauschale im Auswahlbogen (28.08.2026) ---------- */
.bg-anfahrt-zeile { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding: 10px 12px; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-sm); }
.bg-anfahrt-zeile label { font-size: 13px; font-weight: 500; margin: 0; }
.bg-anfahrt-zeile input { width: 64px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text); padding: 5px 8px; font: inherit; }
.bg-anf-info { font-size: 12.5px; color: var(--muted); }
/* Die Messart steht DANEBEN, nicht nur im Kommentar: sonst rechnet in einem
   Jahr jemand Hin- und Rueckweg und landet eine Zone zu hoch. */
.bg-anf-mess { flex: 1 1 100%; font-size: 11.5px; color: var(--faint); }
.bg-anf-grund { color: var(--p-hoch); font-size: 12px; }
/* ---- Die Anfahrt ist eine Position, keine Fussnote (01.09.2026, 20.4.2) ----
   Sie stand in `--muted` — derselben Farbe wie „abgewaehlt" und „nicht
   abrechenbar", also die Farbe fuer „zaehlt hier nicht mit". Sie zaehlt aber:
   sie ist die einzige Zeile ausser den Leistungen, die Geld auf die Rechnung
   bringt, und der Unterschied zwischen 787,50 und 812,50.

   Die Abgrenzung folgt `.bg-gz.rechnung` — Trennlinie plus Abstand, das Muster
   fuer „hier beginnt etwas anderes". Kein neues erfunden. Die Schrift bleibt
   normal: fett ist der Abschluss darunter, und zwei fette Zeilen
   hintereinander heben sich gegenseitig auf.

   Der Betrag bleibt rechtsbuendig in derselben Spalte wie alle anderen — das
   war schon so und wird nicht angefasst. */
.bg-gz.anfahrt td {
  color: var(--text);
  border-top: 1px solid var(--line-soft); padding-top: 7px;
}
.bg-gz.rechnung td { border-top: 1px solid var(--line); padding-top: 7px;
  font-weight: 600; color: var(--text); }
/* Die Frage gegen die Doppelung — sie entscheidet nicht, sie fragt. */
.bg-doppel { margin-top: 10px; font-size: 12.5px; color: var(--p-hoch);
  background: rgba(230,162,86,.08); border: 1px solid rgba(230,162,86,.3);
  border-radius: var(--radius-sm); padding: 9px 11px; }

/* ---------- Die Entfernung im Kundenblatt (28.08.2026) ---------- */
.kb-km .kb-km-feld { display: inline-flex; align-items: center; gap: 6px; }
.kb-km input { width: 84px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text); padding: 4px 8px; font: inherit;
  font-size: 13px; text-align: right; }
.kb-km-zone { font-size: 12.5px; color: var(--muted); margin-left: 8px; }
.kb-km-warn { color: var(--p-hoch); }
/* Die Messart steht daneben, nicht nur im Kommentar — sonst traegt jemand
   Hin- und Rueckweg ein und landet eine Zone zu hoch. */
.kb-km-mess { grid-column: 1 / -1; font-size: 11.5px; color: var(--faint); margin-top: -4px; }

/* ---------- Der Fahrzeit-Haken (28.08.2026) ---------- */
.l-fahrt-hint { font-size: 12px; color: var(--muted); margin: -4px 0 8px 26px; }
/* Die Anfahrtszahl im Bogen ist eine ANZEIGE, kein Feld: sie folgt aus den
   Vor-Ort-Leistungen ohne Haken. */
.bg-anf-zahl { font-weight: 600; font-size: 13px; }
.bg-fahrt-info { margin-top: 10px; font-size: 12.5px; color: var(--muted);
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 8px 11px; }

/* ---------- Die Glocke: lange Woerter brechen um (30.08.2026) ----------
   Gemessen: ein Titel mit einem langen ununterbrochenen Wort war 425 px breit
   in einem 272 px schmalen Kasten und ragte 106 px hinaus — im Bild ueber das
   Loeschkreuz. Der Kasten rollte dann waagerecht.

   Die Ursache war NICHT die Breite. `.bell-tray` hat 340 px (am Handy 374),
   aber die sind fuer die Glocke allein und keine gemeinsame Dialogbreite wie
   `.rk-modal` — an dieser Zahl haengt nichts sonst. Ein breiterer Kasten haette
   die Grenze nur verschoben: beim naechsten laengeren Wort rollt es wieder.

   Und der Rollbalken selbst steht nirgends im Stylesheet. `.bell-tray` setzt
   `overflow-y: auto`, und der Browser leitet daraus `overflow-x: auto` ab —
   steht eines auf `visible` und das andere nicht, wird `visible` zu `auto`.
   Das ist die Sorte Ursache, die man beim Lesen des CSS nicht findet.

   `anywhere` statt `break-word`: nur `anywhere` wirkt auch auf die
   min-content-Breite, und genau die entscheidet hier, weil die Zeile in einem
   Flex-Kasten steht. `min-width: 0` dazu, weil ein Flex-Kind sonst nicht unter
   seine Inhaltsbreite schrumpft — dieselbe Falle wie bei `.ur-name`. */
.bell-tray, .bell-item, .bell-row { min-width: 0; }
.bell-item, .bell-item b, .bell-item span, .bell-vorschau { overflow-wrap: anywhere; }

/* ============================================================
   Leistungsarten ohne hinterlegte Farbe
   ============================================================
   Die Regel ist dieselbe wie an `.kb-card.hat-farbe` weiter oben, und der
   Kommentar dort sagt sie schon: „keine Farbe" ist kein grauer Strich,
   sondern kein Strich.

   Vorher stand an drei Anzeigestellen `#8A94A6` als Rueckfall — die ECHTE
   Farbe von `interne-arbeit`. Eine Art ohne Farbe gab sich damit als eine
   andere aus, und das ist schlimmer als gar keine Farbe.

   `--c` wird jetzt gar nicht gesetzt (`Leistungsart.farbAttr`), damit greift
   der CSS-Rueckfall `var(--c, var(--line))`. Der allein reichte nicht: ein
   durchgezogener grauer Umriss sieht aus wie eine Art, die grau IST. Deshalb
   gestrichelt — ein fehlender Wert soll wie ein fehlender aussehen. */
.tl-art.ohne-farbe, .ab-art.ohne-farbe {
  border-style: dashed; color: var(--faint); opacity: .75;
}
/* Der Balken an der Zeitleistenzeile: bei fehlender Farbe kein Balken.
   `transparent` statt `var(--line)`, sonst bliebe ein Strich stehen, den
   niemand gesetzt hat — dieselbe Ueberlegung wie bei `.kb-card`. */
.tl-row.ohne-farbe { border-left-color: transparent; }

/* ---- Der Farbwaehler: der SCHREIBPFAD, andere Schwereklasse ----
   Ein `<input type="color">` kann nicht leer sein. Steht dort ein Ersatzwert,
   ist er keine Behauptung mehr, sondern eine Vorbelegung: er sieht aus wie ein
   hinterlegter Wert und wird beim ersten Speichern zu einem. Danach kann
   niemand mehr sagen, ob die Farbe gewaehlt oder geerbt wurde.

   Der Wert bleibt (technisch noetig), aber das Feld sagt sichtbar, dass es
   keiner ist: durchgestrichen und blass. Wer waehlt, hat gewaehlt — dann faellt
   die Klasse im `onchange`. */
.art-color.ohne-farbe {
  opacity: .45; border-style: dashed;
  background-image: linear-gradient(to top left, transparent 46%,
    var(--muted) 46%, var(--muted) 54%, transparent 54%);
}
/* ---- Das Wort neben dem farblosen Waehler (31.08.2026) ----
   Zwei echte Grautoene (`interne-arbeit`, `nicht-klassifiziert`) stehen in
   derselben Liste wie ein gestricheltes Feld. Bei 26 px ist der Unterschied
   zwischen „grau gewaehlt" und „keine Farbe" da, aber leise — und er ist auch
   keiner, den ein Kasten tragen kann.

   Dieselbe Regel wie an der Leistungszeile: die Farbe ist nie der einzige
   Traeger. Wo sie fehlt, steht das Wort. */
/* ---- Warum die Farbspalte eine FESTE Breite hat ----
   Der erste Versuch setzte das Wort einfach neben den Waehler. Im Bild war
   dann die ganze Zeile aus der Flucht: `.art-row` ist ein Flex, das Wort nahm
   Platz, und Namensfeld wie Artikelspalte rutschten nach rechts — nur bei der
   einen Art, der die Farbe fehlt. Eine Liste, in der eine Zeile ausschert,
   liest sich als Fehler, und zwar an der falschen Stelle.

   Also traegt die Spalte immer dieselbe Breite; nur ihr INHALT wechselt.
   Gefunden hat das kein Prueffall, sondern ein Blick auf das Bild. */
.art-farbe {
  flex: none; width: 96px; display: flex; align-items: center; gap: 6px;
}
.art-ohne-wort {
  flex: none; font-size: 11px; color: var(--faint); font-style: italic;
  white-space: nowrap;
}

/* ============================================================
   Art und Notiz sind zwei Angaben — an allen sechs Orten
   ============================================================
   Vorher stand an drei Stellen `notiz || leistungsart` in EINER Zelle: wer
   eine Notiz schrieb, sah die Art nicht mehr. Bei einem Kunden mit 20
   beschrifteten Leistungen war die Art damit nirgends zu sehen.

   Ein Rueckfall zwischen den beiden gibt es jetzt in KEINER Richtung — auch
   nicht rueckwaerts. Sonst waere es dieselbe Bauart mit getauschten Rollen,
   und in einem halben Jahr sucht jemand wieder, warum ein Feld manchmal etwas
   anderes zeigt.

   Eine leere Notiz zeigt deshalb NICHTS. Kein Gedankenstrich, keine Art: die
   Spalte ist da, sie ist leer, und das ist die Auskunft. */
.bg-art, .ra-art { white-space: nowrap; }
.bg-art-n, .ra-art-n {
  display: inline-block; font-size: 10.5px; font-weight: 600;
  color: var(--c, var(--muted)); border: 1px solid var(--c, var(--line));
  border-radius: 999px; padding: 0 7px; line-height: 17px;
}
.bg-art-n.ohne-farbe, .ra-art-n.ohne-farbe {
  border-style: dashed; color: var(--faint); opacity: .75;
}
.kb-art {
  flex: none; font-size: 10.5px; font-weight: 600;
  color: var(--c, var(--muted)); border: 1px solid var(--c, var(--line));
  border-radius: 999px; padding: 0 7px; line-height: 17px; white-space: nowrap;
}
.kb-art.ohne-farbe { border-style: dashed; color: var(--faint); opacity: .75; }

/* ---- Der Haken bei „vor-ort": NEUTRAL, und ohne Aussage ueber Geld ----
   Der erste Entwurf war an drei Stellen falsch, und die dritte ist die
   schwerste.

   1. Er trug ein festverdrahtetes Orange. Die Artfarbe steht in
      `leistungsarten.color` und ist in den Einstellungen aenderbar — ein
      eigenes Orange daneben ist eine zweite Wahrheit, die an dem Tag bricht,
      an dem „vor-ort" umgefaerbt wird. Im Bild standen `Vor-Ort` und die Marke
      beide orange nebeneinander und lasen sich als EINE Marke: das Auge kann
      nicht unterscheiden, ob das Orange „Vor-Ort" heisst oder „Anfahrt".

   2. Die zwei Aufschriften redeten ueber verschiedene Dinge. „Fahrzeit inkl."
      ist eine Aussage ueber die erfassten Stunden, „Anfahrt zaehlt" eine ueber
      die Rechnung. Festgelegt war: der Haken ist eine Aussage ueber die ZEIT.
      Ein Paar, dessen Haelften verschiedene Gegenstaende haben, ist keins.

   3. Die Zeitleiste kann die Folge gar nicht kennen. Ob eine Anfahrt zaehlt,
      haengt an der Auswahl im Bogen und an `entfernung_km` am Kunden — beides
      liegt dort nicht vor. Bei einem Kunden ohne hinterlegte Entfernung gibt es
      keine Pauschale, und „Anfahrt zaehlt" waere schlicht falsch. Eine Marke,
      die eine Folge behauptet, ohne deren Voraussetzung zu sehen, ist ein Wert
      ohne Bezug.

   Jetzt sagt sie nur, was im Feld steht: „Fahrzeit enthalten" gegen „ohne
   Fahrzeit". Die Geldfolge steht im Bogen, und dort steht sie schon.

   BEIDE Zustaende tragen eine Marke, mit gleichem Gewicht — kein Zustand darf
   durch Abwesenheit gemeint sein. Der Beleg dafuer ist aus diesem Auftrag
   selbst: `fahrzeit_enthalten` fehlte in `Reports.entries()`. Hiesse „keine
   Marke" = „ohne Fahrzeit", saehe ein fehlendes Feld wie der Normalfall aus,
   und niemand haette es bemerkt. */
.bg-fz, .ra-fz, .tl-fz, .ab-fz {
  display: inline-block; margin-left: 6px; font-size: 10px;
  color: var(--faint); border: 1px solid var(--line);
  border-radius: 4px; padding: 0 5px; line-height: 15px; white-space: nowrap;
}
/* Der Unterschied steckt im WORT, nicht in der Lautstaerke. Die gesetzte
   Angabe steht etwas fester, damit sie sich vom Grundzustand abhebt — aber
   ohne eine eigene Farbe zu erfinden. */
.bg-fz.inkl, .ra-fz.inkl, .tl-fz.inkl, .ab-fz.inkl {
  color: var(--muted); border-color: var(--line-soft, var(--line));
}

/* ---- Die innere Tabelle bestimmt ihre eigene Ausrichtung (31.08.2026) ----
   Im Bild stand die Notizspalte des Aufrisses RECHTSBUENDIG, und die Artmarken
   standen nicht in Flucht: „Vor-Ort" bei x=166, „Fernwartung" bei x=217.

   Die Ursache steht 1300 Zeilen weiter oben und gehoert einer anderen Tabelle:

     .report-table td:nth-child(n+2) { text-align: right; }

   Ein NACHFAHREN-Selektor, kein `>`. Der Aufriss (`.ra-tab`) wird in eine
   Zelle von `.report-table` geklappt, also zaehlt er als deren Nachfahre und
   die Regel greift bis in seine Zellen durch. `.ra-datum` ist die erste und
   blieb links; `.ra-art` und `.ra-notiz` sind die zweite und dritte.

   Das war schon vor der Trennung so und fiel nicht auf, solange die Art ein
   Wort in Grau war. Als sie eine farbige Marke wurde, sah man es.

   Die Spezifitaet muss mithalten: `.report-table td:nth-child(n+2)` ist 0-2-1,
   ein blosses `.ra-tab td` waere 0-1-1 und verloere — dieselbe Falle wie bei
   `.msg.mail_out` gegen `.msg-art-eigen` am 22.08. Deshalb die Klasse an der
   Zelle, und die Zahlenspalten holen sich ihre Ausrichtung danach zurueck. */
.ra-tab td.ra-datum, .ra-tab td.ra-art, .ra-tab td.ra-notiz { text-align: left; }
.ra-tab td.ra-dauer, .ra-tab td.ra-sync, .ra-tab td.ra-bill,
.ra-tab td.ra-ticket { text-align: right; }

/* ============================================================
   Der Auswahlbogen: Gruppen sind zu, bis jemand hinsieht
   ============================================================
   Ein Monat mit zwanzig Tickets war eine Wand aus Zeilen, und die Summen, um
   die es beim Stellen geht, standen unter allem. Die Gruppen sind deshalb
   zugeklappt; ihre Kopfzeile traegt Anzahl, Stunden, Auswahl und Betrag.

   Der Zustand wird NICHT gemerkt (kein localStorage). Ein gemerkter waere die
   Erinnerung an einen anderen Monat bei einem anderen Kunden — beim Stellen
   zaehlt, was heute offen ist. */
.bg-gruppe.zu tr.bg-zeile { display: none; }
.bg-klapp {
  background: none; border: none; cursor: pointer; padding: 0 6px 0 0;
  color: var(--muted); font-size: 11px; line-height: 1;
}
.bg-klapp:hover { color: var(--text); }
tr.bg-kopf { cursor: pointer; }
tr.bg-kopf:hover { background: var(--hover); }

/* Die Leiste ueber der Tabelle: Suche und der Schalter.
   Ohne Suche waeren zugeklappte Gruppen ein Rueckschritt — wer eine bestimmte
   Leistung sucht, muesste zwanzig Gruppen einzeln oeffnen. */
.bg-leiste {
  display: flex; align-items: center; gap: 10px; padding: 8px 14px 0;
}
.bg-leiste #bg-suche {
  flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text);
  font-family: var(--font-body); font-size: 12.5px; padding: 6px 10px;
}
.bg-leiste #bg-suche:focus { outline: none; border-color: var(--accent-dim); }
.bg-such-erg { flex: none; font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* ---- Ids, die nicht im Zeitraum liegen (31.08.2026) ----
   `auswahlBogen(13, '2026-08', [19,46,48])` lieferte `gewaehlte_ids: [48,46]`
   und meldete `vollstaendig: true` — Leistung 19 liegt im Juli und fiel
   lautlos aus der Auswahl. Am Ende dieses Weges entsteht eine
   Rechnungsnummer, und die ist unumkehrbar.

   Der lesende Weg meldet (korrigierbar), der schreibende weist ab. Die
   Meldung steht deshalb sichtbar ueber der Tabelle und nicht als Fussnote:
   wer sie uebersieht, sieht auch die fehlende Zeile nicht. */
.bg-ausserhalb {
  margin: 8px 14px 0; padding: 8px 12px;
  background: var(--warn-bg, rgba(230, 162, 86, .08));
  border: 1px solid var(--warn, #E6A256); border-radius: var(--radius-sm);
  color: var(--text); font-size: 12.5px; line-height: 1.5;
}

/* ---- Die Spur einer zurueckgenommenen Satzfestlegung (31.08.2026) ----
   `satz_zurueckgenommen_am` wurde geschrieben und nirgends angezeigt. Sie steht
   jetzt im Bogen — dort stellt sich die Frage „warum steht die wieder offen?".
   Nicht an der Leistungszeile: die traegt schon Art, Fahrzeit-Marke und Notiz,
   und eine Ruecknahme ist eine Vorgeschichte, keine Eigenschaft der Leistung.

   NEUTRAL, kein Warnaussehen: es ist nichts kaputt. Der Beleg wurde geloescht,
   die Leistung steht wieder zur Verfuegung — das ist der vorgesehene Weg und
   keine Stoerung. */
.bg-rueckn {
  display: inline-block; margin-left: 8px; font-size: 10.5px;
  color: var(--faint); border: 1px solid var(--line);
  border-radius: 4px; padding: 0 6px; line-height: 16px; white-space: nowrap;
}

/* ---- Der Summenblock zeigt sein Ergebnis, nicht seine Herleitung (31.08.2026) ----
   Die fuenf Zustandszeilen BLEIBEN — sie sind Information, keine Rechnung.
   „kommt rein 15" und „im Zeitraum 15" sahen aus wie zwei Wege zur selben
   Zahl; sie stimmen aber nur ueberein, weil alles gewaehlt ist. Waehlt jemand
   ab, steht 14 neben 15, und das ist richtig.

   Was fehlte, war die Auskunft, DASS es eine Aufteilung ist. Das traegt jetzt
   die Struktur: eine Ueberschrift darueber, die Kontrollzeile darunter. */
.bg-gz.kopf td {
  padding-top: 2px; padding-bottom: 4px;
  font-size: 11px; color: var(--faint); font-weight: 600;
  letter-spacing: .02em; border-bottom: 1px solid var(--line-soft);
}
/* Der Riegel steht LINKS, in der Beschriftungsspalte. „Summe stimmt" stand in
   der Betragsspalte — eine Textzelle in einer Spalte, in der darueber
   Euro-Betraege stehen. Unauffaellig: es ist der Normalfall, und eine
   Bestaetigung, die laut ist, macht den Fehlerfall leiser. */
.bg-riegel {
  margin-left: 8px; font-size: 10.5px; font-weight: 500;
  letter-spacing: .01em;
  /* ---- Nicht mehr --faint (01.09.2026, 20.4.1) ----
     Das war die Farbe von Fussnoten, und dahinter steckt eine Pruefung: die
     Aufteilung ergibt zusammen die Menge des Zeitraums. `--ok` ist die Farbe,
     die im Haus „in Ordnung" heisst (`.kb-col-fertig`, `--ok: #4EC58C`) —
     wiederverwendet, nicht neu erfunden.

     Deutlich genug, um als Symbol erkennbar zu sein; nicht so kraeftig, dass
     es Aufmerksamkeit bindet. Der Fall, der Aufmerksamkeit braucht, ist der
     andere, und der hat seinen eigenen Kasten (`.bg-warn`). */
  color: var(--ok);
}
/* Die Beanstandung steht AN der Zeile, die sie betrifft — vorher unter der
   ganzen Tabelle, drei Zeilen vom Befund entfernt. */
.bg-gz.warnzeile td { padding: 4px 0 2px; }
.bg-gz.warnzeile .bg-warn { margin-top: 0; }

/* ---------- Der Kundentext im Bogen (31.08.2026, Punkt 12.3) ----------
   Die Spalte zeigt den ZUSTAND, das Aufklappfeld den Text. „fehlt" ist
   absichtlich sichtbar und nicht grau: es ist der Zustand, der die Rechnung
   aufhaelt, und eine dezente Andeutung uebersieht man genau so lange, bis der
   Beleg abgewiesen wird. */
.bg-tbl .bg-kt { max-width: 180px; }
.bg-kt-knopf {
  font: inherit; font-size: 12px; text-align: left; width: 100%;
  padding: 3px 7px; border-radius: 5px; cursor: pointer;
  border: 1px solid var(--line); background: var(--raised); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bg-kt-knopf:hover { border-color: var(--accent); }
.bg-kt-knopf.fehlt {
  border-color: #c9762e; color: #c9762e; background: transparent;
}
/* ---- Die Flaeche ist --raised, nicht --faint (01.09.2026, 22.1) ----
   Hier stand `background: var(--faint)`. `--faint` ist im Haus eine TEXTfarbe
   fuer Nebensaechliches (#808DA1) — als Flaeche erzeugte sie einen hellen
   Kasten in einer dunklen Anwendung und war die gemeinsame Wurzel von drei
   Befunden dieser Sitzung. Gerechnet, was sie den Inhalten gab:

     Text --text auf --faint    2,89:1     unter jeder Schwelle
     Aufschrift --muted darauf  1,14:1     praktisch unsichtbar
     Knopf --bg darauf          5,78:1     nur DER stimmte

   `--raised` ist das Muster fuer aufgeklappte Kaesten im Haus — `.bell-tray`,
   `.kb-card`, `.card` tragen es alle. Nicht neu erfunden. Dieselben Inhalte
   darauf:

     Text --text auf --raised   13,95:1
     Aufschrift --muted darauf   5,49:1
     Knopf: siehe .bg-kt-ueb — die Zahl von 21.2 gilt nicht mehr, denn eine
            Kontrastzahl gilt gegen einen bestimmten Hintergrund, nicht an sich. */
.bg-kt-auf > td { background: var(--raised); padding: 10px 12px; }
.bg-kt-box { display: flex; gap: 16px; align-items: flex-start; }
/* Das Feld nimmt den Platz, die Notiz steht daneben und bleibt schmal —
   sie ist die Vorlage, nicht der Gegenstand. */
.bg-kt-feld { flex: 1 1 auto; min-width: 0; }
.bg-kt-box label {
  display: block; font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); margin-bottom: 4px;
}
.bg-kt-feld textarea {
  width: 100%; min-height: 90px; resize: vertical; font: inherit;
  line-height: 1.45; padding: 8px 10px; border-radius: 6px;
  border: 1px solid var(--line); background: var(--raised); color: var(--text);
  /* Kein overflow-y: die Hoehe wird beim Tippen nachgefuehrt (`wachsen`),
     damit vierzig Zeilen sichtbar sind statt scrollbar. */
  overflow-y: hidden;
}
.bg-kt-akt { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.bg-kt-stand { font-size: 12px; color: var(--muted); }
@media (max-width: 900px) {
  .bg-kt-box { flex-direction: column; }
  }

/* ============================================================
   19.2 — Der Kundentext-Bearbeiter wird benutzbar (01.09.2026)
   ============================================================
   Gemessen in 19.1: nichts ist gesperrt, nichts überlagert, nichts ragt
   hinaus. Der Bearbeiter war funktionsfähig und las sich als kaputt. Das
   Folgende ist deshalb ausschliesslich Gestaltung — an der Übernahme-Logik,
   am Abweis-Riegel und am PDF ändert sich nichts.
   ============================================================ */

/* ---- 19.2.1: der Übernehmen-Knopf trägt Rand und Fläche ----
   Er ist der EINSTIEG in den Vorgang und war der unauffälligste Knopf im
   Kasten: `.btn.ghost`, `background: transparent`, `border-color:
   rgba(0,0,0,0)`, 782 px vom Feld entfernt (alles gemessen).

   Nicht orange: das gehört dem Speichern. Zwei orange Knöpfe nebeneinander
   sagen nichts mehr — dann ist wieder keiner hervorgehoben. */
/* ---- Der Rand faellt weg (01.09.2026, 21.2) ----
   19.2.1 gab dem Knopf `border-color: var(--accent-dim)` und prueft, dass er
   gesetzt IST. Gemessen am 01.09.: dieser Rand hat **1,07:1** gegen seinen
   Hintergrund — praktisch unsichtbar. Ein Farbwert sagt, dass etwas gesetzt
   ist; ob man es SIEHT, sagt nur das Verhaeltnis zum Untergrund.

   Der Knopf liegt auf `.bg-kt-auf > td`, und das traegt `background:
   var(--faint)` = rgb(128,141,161) — eine helle Graufarbe. Die Kette dahinter,
   gemessen: td rgb(128,141,161) / tr rgb(25,33,43) / panel rgb(18,23,31) /
   body rgb(10,13,19). Massgeblich ist der erste.

   Was den Knopf traegt, ist seine FLAECHE. `--bg` statt `--raised`: dunkler
   als der Untergrund, und der Kontrast steigt von 4,83 auf ueber 7. Der Rand
   nimmt dieselbe Farbe wie die Flaeche — er verschwindet nicht, er hoert auf,
   etwas anderes zu behaupten.

   BEFUND, nicht behoben: `--faint` ist im Haus eine TEXTfarbe fuer
   Nebensaechliches und steht hier als Hintergrund. Das ist die Wurzel — auch
   der Hinweis unter den Knoepfen war deshalb unlesbar (19.3). Eigener Punkt. */
/* ---- Der Rand kehrt zurueck (01.09.2026, 22.1) ----
   19.2.1 gab dem Knopf `border-color: var(--accent-dim)`. 21.2 nahm ihn weg,
   weil er 1,07:1 hatte — gemessen gegen die damalige Flaeche `--faint`.

   Der Rand war nicht falsch, er stand auf dem falschen Untergrund. Gegen
   `--raised` hat dieselbe Farbe **5,15:1**. Was sich geaendert hat, ist der
   Grund darunter, nicht der Rand.

   Das ist die Lehre aus 21.2 in ihrer schaerfsten Form: eine Kontrastzahl
   gehoert zu einem Paar. Wer den Hintergrund tauscht, muss jede Zahl neu
   rechnen, die gegen ihn gemessen wurde — auch wenn das gemessene Element
   unveraendert bleibt. */
.bg-kt-ueb {
  background: var(--bg);
  border-color: var(--accent-dim);
  color: var(--text);
}
.bg-kt-ueb:hover { background: var(--surface); border-color: var(--accent); }


/* ---- 19.2.3: die Notiz in der Zeile bricht um ----
   Sie geht jetzt durch `gliederungHtml()` (app.js), dieselbe Funktion wie im
   Arbeitsbericht. Deren Klassen `.ab-topic`/`.ab-subs` sind global gestylt und
   greifen hier mit — hier steht nur, was der BOGEN anders braucht: enger, weil
   eine Tabellenzeile kein Modal ist.

   Der Deckel ist eine Pixelgrenze, die Zahl im Knopf kommt aus dem TEXT
   (`SICHTBARE_NOTIZZEILEN` in app.js). Beide zusammen: gekürzt wird sichtbar,
   und die Auskunft darüber ist gezählt, nicht geschätzt. */
.bg-txt-gl { max-width: 520px; }
.bg-txt-gl .ab-topic { font-size: 12.5px; margin: 3px 0 1px; }
.bg-txt-gl .ab-subs { margin: 0 0 3px 16px; }
.bg-txt-gl .ab-subs li { font-size: 12px; }
.bg-txt-gl .ab-note { font-size: 12.5px; margin: 0; }
.bg-txt-gl.gekuerzt {
  max-height: 132px; overflow: hidden;
  /* Ausblenden statt hart abschneiden: eine Kante mitten im Wort liest sich
     wie ein Fehler, ein Verlauf wie eine Fortsetzung. */
  mask-image: linear-gradient(to bottom, #000 60%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent);
}
.bg-txt-mehr {
  display: inline-block; margin-top: 2px; padding: 2px 8px;
  font: inherit; font-size: 11.5px; cursor: pointer;
  background: transparent; color: var(--accent);
  border: 1px solid var(--accent-dim); border-radius: 5px;
}
.bg-txt-mehr:hover { background: var(--hover); }

.bg-kt-box label { color: var(--text); opacity: .75; }

/* ---- 19.3: der Bearbeiter nimmt die volle Breite (01.09.2026) ----
   Der schmale Notizkasten rechts ist weg (Weg B). Seine Regeln stehen nicht
   mehr im Stylesheet — eine Regel ohne Markup ist eine Anweisung an niemanden,
   und die nächste Sitzung sucht das Element.

   `.bg-kt-box` bleibt Flex: das Feld ist jetzt das einzige Kind und nimmt
   alles. Kein `display: block` daraus zu machen spart eine Änderung an einer
   Stelle, die sonst nichts angeht. */

/* ---- 20.3: die Spaltenbreiten stehen fest (01.09.2026) ----
   Vorher bestimmte sie, wer gerade am breitesten war: `table-layout` stand auf
   `auto`, und beim Zuklappen legte die Gruppenkopfzeile mit ihrem `colspan="4"`
   die Spalten 2-5 allein fest. Gemessen: die Breiten sprangen beim Auf- und
   Zuklappen, vorher um bis zu 14 px, nach 20.2.3 um bis zu 59.

   Die Werte in der `colgroup` (app.js) sind GEMESSEN, nicht gesetzt — am
   offenen Bogen mit dem Bestand aus Auftrag 17, bei 1066 px Tabellenbreite:

     Marke 37 px (3,5 %)   Tag 62 (5,8 %)    Art 294 (27,5 %)
     Notiz 209 (19,6 %)    Kundentext 177 (16,6 %)
     Std. 80 (7,5 %)       Satz 94 (8,8 %)   Betrag 113 (10,6 %)

   Prozent fuer die Textspalten, damit die Tabelle mit dem Fenster mitgeht;
   feste Pixel nur fuer die Markenspalte — dort steht ein Kontrollkaestchen, und
   das wird nicht breiter, wenn der Schirm es wird.

   `fixed` NUR am Bogen, nicht an `.rk-tbl` allgemein: die anderen Tabellen
   dieser Klasse haben keine `colgroup`, und bei `fixed` ohne Breitenangabe
   verteilt der Browser gleichmaessig — das waere fuer sie eine Verschlechterung
   ohne Anlass.

   Was sich damit aendert (gemessen, siehe 20.3 Punkt 3): Text, der nicht in
   seine Spalte passt, bricht um und macht die ZEILE hoeher, statt die Spalte zu
   verbreitern. Genau das ist der Zweck. Ein Wort ohne Trennstelle braucht dafuer
   `overflow-wrap` — sonst laeuft es ueber die Spaltengrenze hinaus. */
.bg-tbl { table-layout: fixed; }
.bg-tbl td { overflow-wrap: anywhere; }
/* Die Zahlenspalten NICHT: „787,50" mitten im Betrag umzubrechen waere
   schlimmer als eine schmale Spalte. Sie sind ueber `.num` ohnehin `nowrap`. */
.bg-tbl td.num { overflow-wrap: normal; }

/* ---------- Der Stornobericht (104.4, 15.09.2026) ----------
   Die Meldung ging vorher als Toast hinaus — 180 Zeichen mit fuenf
   Leistungsnummern, weg nach Sekunden. Was eine HANDLUNG verlangt, bekommt
   einen Ort und bleibt stehen, bis jemand ihn schliesst. */
.st-bericht { padding: 4px 2px 10px; display: flex; flex-direction: column; gap: 10px; }
.st-zeile { font-size: 13.5px; line-height: 1.5; color: var(--text); }
.st-zeile.st-ok   { color: var(--s-geloest); }
.st-zeile.st-warn { color: var(--s-arbeit); font-weight: 600; }

/* ---------- Die gebundenen Leistungen am erledigten Beleg (110.2) ----------
   Sie stehen als eigener Block ueber den Knoepfen und nicht als Zeile in
   der Kennwerttabelle: das ist keine Eigenschaft des Belegs, sondern eine
   Handlung, die noch aussteht. Abgesetzt, damit man sie nicht ueberliest —
   der Vorgaenger war ein Toast, den Christian nie gesehen hat. */
.rk-gebunden { margin: 12px 17px 0; padding: 11px 13px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft); background: var(--surface); }
.rk-geb-kopf { font-weight: 600; font-size: 13px; }
.rk-geb-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.rk-geb-text { font-size: 12px; color: var(--faint); line-height: 1.5; margin: 7px 0 9px; }
