/* components.css — geteilte Bausteine (Spec §4): Karte, Feed-Zeile, Chips,
   Inbox, Sektions-Label. Wiederverwendet von Puls (S5-A), Projekt (S5-B). */
/* .card background/border come from app.css (theme-aware var(--panel)/var(--line));
   only override the radius/padding here so dark theme is not clobbered. */
.card { border-radius: 10px; padding: var(--sp-3) var(--sp-4); }
.sec-label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted, #888); margin: var(--sp-4) 0 var(--sp-2); }
.chips { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.chip { display: inline-block; padding: var(--sp-1) var(--sp-3); min-height: 28px;
  border: 1px solid var(--border, #ccc); border-radius: 999px; font-size: 13px;
  text-decoration: none; color: inherit; max-width: 32ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.chip.is-active { background: var(--accent, #6b46ff); color: #fff;
  border-color: var(--accent, #6b46ff); }
.feed-day { font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted, #888); margin: var(--sp-4) 0 var(--sp-2);
  /* Anker-Ziel des Aktivitätsbands (#tag-<iso>) darf nicht unter der sticky
     Topnav landen (Puls v2.2, Idee 1). */
  scroll-margin-top: calc(var(--topnav-h, 56px) + var(--sp-3)); }
/* Projekt-Farbleiter (Puls v2.2, Idee 2): pjb-N setzt EINE lokale Variable
   --pj-cur (erbt an Kinder), die Farbkante + Chip-Punkt gemeinsam lesen. So
   trägt dieselbe Klasse den 3px-Feed-Rand UND den 2px-Selector-Balken. */
.pjb-0 { --pj-cur: var(--pj-0); } .pjb-1 { --pj-cur: var(--pj-1); }
.pjb-2 { --pj-cur: var(--pj-2); } .pjb-3 { --pj-cur: var(--pj-3); }
.pjb-4 { --pj-cur: var(--pj-4); }
.feed-row { display: flex; gap: var(--sp-3); align-items: flex-start;
  border-left: 3px solid var(--pj-cur, transparent); padding-left: var(--sp-2); }
.feed-row + .feed-row { margin-top: var(--sp-2); }
/* Projekt-Chip bleibt neutral (kein weißer Text auf Farbe) + farbiger Punkt
   davor; Farbe erbt via --pj-cur von der Feed-Zeile. */
.pj-dot { color: var(--pj-cur, var(--muted, #888)); font-size: 9px; line-height: 1;
  margin-right: 3px; }
.feed-ico { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 8px;
  display: grid; place-items: center; font-size: 14px; }
.feed-ico.created { background: #dff3e9; } .feed-ico.linked { background: #e3ecfb; }
.feed-ico.updated { background: #fdf3d8; } .feed-ico.merged,
.feed-ico.reverted, .feed-ico.archived { background: #f3e3fb; }
/* GitHub App (Spec 2026-09-09 §6.5): Push-Icon-Kachel + Branch-Chip. Der Chip
   ist ein Text-Marker im Feed-Item, kein Filter-Chip — er bleibt klein und
   monospace, auch auf Touch-Viewports (die 44px-Regel unten gilt Filter-Chips;
   .chip.chip-branch gewinnt per Spezifität). */
.feed-ico.pushed { background: #e3f1f7; }
.chip.chip-branch { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; min-height: 0; line-height: 1.6; padding: 0 var(--sp-2);
  vertical-align: middle; max-width: 24ch; color: var(--muted, #666); }
.feed-proj { font-size: 11px; border: 1px solid var(--border, #ddd);
  border-radius: 4px; padding: 0 var(--sp-1); color: var(--muted, #666); }
.feed-source { font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  border: 1px solid var(--border, #ddd); border-radius: 4px; padding: 0 var(--sp-1);
  color: var(--muted, #888); }
.feed-summary { font-size: 12px; color: var(--muted, #888);
  margin: calc(-1 * var(--sp-1)) 0 var(--sp-2); }
.feed-items { margin: var(--sp-1) 0 0; padding-left: var(--sp-4); }
.visit-marker { text-align: center; font-size: 11px; color: var(--accent, #6b46ff);
  border-top: 2px dashed var(--accent-soft, #c9b8ff); padding-top: var(--sp-1);
  margin: var(--sp-3) 0; }
.inbox { background: var(--accent-soft, #efe7ff);
  border: 1px solid var(--accent-border, #c9b8ff); border-radius: 12px;
  padding: var(--sp-3) var(--sp-4); }
.inbox-head { margin: 0; }
.inbox-types { color: var(--muted, #888); font-size: 13px; }
.inbox-card { background: var(--surface, #fff); border-radius: 8px;
  padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-2); font-size: 13px; }
.inbox-cta { margin: var(--sp-3) 0 0; font-size: 13px; }
.inbox-row { display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: var(--sp-3); background: var(--surface, #fff);
  border-radius: 8px; padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-2); }
.inbox-row form { display: inline; }
.inbox-actions { display: flex; gap: var(--sp-2); flex-wrap: nowrap; }
.btn-ok, .btn-no { min-width: 44px; min-height: 44px; border-radius: 8px;
  border: 0; font-weight: 700; cursor: pointer; }
.btn-ok { background: #dff3e9; color: #067a52; }
.btn-no { background: #fbe5e5; color: #b03030; }
.statsfoot { display: flex; flex-wrap: wrap; gap: var(--sp-4); justify-content: center;
  font-size: 12px; color: var(--muted, #888); padding: var(--sp-4) 0;
  border-top: 1px solid var(--border, #e2e0dc); margin-top: var(--sp-5); }
/* Relativzeit am Ende der Feed-Zeile (Spec §5.1). */
.feed-rel { color: var(--muted, #888); font-size: 12px; }
.inbox-empty { color: var(--muted, #888); font-size: 13px; margin-top: var(--sp-2); }
.filterbar { margin: var(--sp-4) 0; }
.filterbar .sec-label { margin: var(--sp-3) 0 var(--sp-1); }

/* Spec §5.5: Touch-Targets ≥ 44 px auf Touch-Viewports. Chips sind desktop
   kompakt (28 px), auf Mobil aber Fingerziele. */
@media (max-width: 767px) {
  /* 44px Fingerziel OHNE inline-flex: ein Flex-Container hebt text-overflow:
     ellipsis auf (die Ellipsis braucht einen Inline-/Block-Kontext, kein
     Flex-Item). Also inline-block (aus der Basis) behalten und die Höhe über
     line-height füllen; die vertikale Padding wird genullt, damit die Zeile
     mittig sitzt und die Ellipsis (Basis: overflow/white-space/text-overflow)
     weiter greift. */
  .chip { min-height: 44px; line-height: 44px; padding-top: 0; padding-bottom: 0; }
  /* Token bleibt ein Fingerziel (§5.5), auch wenn er desktop kompakter ist. */
  .fa-token { min-height: 44px; }
}

/* ---- Puls v2.1: Projekt-Selector + Aktive-Filter-Tokens -------------------
   Selector nach dem .acct-menu-Vorbild (nav.js-Muster): Desktop absolutes
   Dropdown, Mobil fixed Bottom-Sheet (Override in layout.css, neben .acct-menu).
   Die Filterbar liegt NICHT in der sticky Topnav → das Panel trägt seinen eigenen
   z-index (> Bottom-Nav 40), sonst verschwände es mobil hinter der Leiste. */
.filterbar-sel { position: relative; display: inline-block; }
.sel-btn { display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border, #ccc); border-radius: 999px;
  background: var(--surface, #fff); color: inherit; font: inherit; font-size: 13px;
  font-weight: 600; cursor: pointer; }
.sel-chev { color: var(--muted, #888); flex: none; }
.sel-panel { position: absolute; left: 0; top: calc(100% + 6px); z-index: 45;
  width: 360px; max-width: calc(100vw - 2 * var(--sp-4)); max-height: 70vh;
  overflow-y: auto; padding: var(--sp-2); background: var(--panel, #fff);
  border: 1px solid var(--line, #e2e0dc); border-radius: 12px;
  box-shadow: var(--shadow, 0 4px 14px rgba(0,0,0,.12)); }
/* Zeile als festes Grid: Punkt | Titel (ellipsis) | Zähler | Zeit — Spalten
   fluchten über alle Zeilen (Grid-Regel; Zeit-Spalte fix, damit Zähler eine
   Spalte bilden). .sel-name darf KEIN Flex sein: text-overflow greift nur
   auf Block-Containern (gleiche Falle wie der Chip-Ellipsis-Fix). */
.sel-item, .sel-item:visited { display: grid;
  grid-template-columns: 14px minmax(0, 1fr) auto 72px;
  column-gap: var(--sp-2); align-items: center; min-height: 44px;
  padding: var(--sp-2) var(--sp-3); border-radius: 8px;
  text-decoration: none; color: inherit;
  /* Projekt-Farbleiter (Idee 2): schmaler Farbbalken links, Farbe via pjb-N →
     --pj-cur; der Frische-Punkt (sel-dot) bleibt separat erhalten. */
  border-left: 2px solid var(--pj-cur, transparent); }
.sel-dot { text-align: center; font-size: 11px; line-height: 1; }
.sel-item:hover { background: var(--raised, #f3f2f0); }
.sel-item.is-active { background: var(--accent-soft, #efe7ff); font-weight: 600; }
.sel-name { display: block; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sel-cnt { justify-self: end; background: var(--raised, #eee); border-radius: 9px;
  padding: 0 var(--sp-2); font-size: 11px; color: var(--muted, #555); }
.sel-rel { justify-self: end; font-size: 11px; color: var(--muted, #888);
  white-space: nowrap; }

/* Aktive-Filter-Zeile: entfernbare Tokens im Grid-Token-Stil (--sp-* Abstände). */
.filter-active { display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2); margin: 0 0 var(--sp-4); }
.fa-label { font-size: 12px; color: var(--muted, #888); }
.fa-token { display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 32px; padding: var(--sp-1) var(--sp-3); border-radius: 8px;
  background: var(--accent-soft, #efe7ff);
  border: 1px solid var(--accent-border, #c9b8ff);
  color: var(--accent, #4a2fbf); text-decoration: none; font-size: 13px; }
.fa-token:hover { filter: brightness(.97); }

/* ---- S5-B: Projektliste (Karten + Frische) + Projekt-Detailseite ---- */
.pj-card { display: block; background: var(--surface, #fff); border: 1px solid var(--border, #e2e0dc);
  border-radius: 10px; padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-3);
  text-decoration: none; color: inherit; min-height: 44px; }
.pj-head { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.pj-last { display: block; margin-top: var(--sp-2); padding-top: var(--sp-2);
  border-top: 1px dashed var(--border, #e6e4e0); font-size: 13px; color: var(--muted, #666); }
.dot-fresh { color: #067a52; } .dot-days { color: #b07500; } .dot-stale { color: var(--muted, #999); }
/* Badge scoped to the project surfaces: the global `.badge` (app.css) is reused
   by the entities browser via v1_badge — an unscoped override would regress it. */
.pj-head .badge { font-size: 11px; font-weight: 500; border: 1px solid var(--border, #ddd);
  border-radius: 4px; padding: 0 var(--sp-1); color: var(--muted, #666); letter-spacing: 0; }
.cols-8-4 { display: grid; grid-template-columns: 1fr; gap: var(--sp-5); }
@media (min-width: 768px) { .cols-8-4 { grid-template-columns: 2fr 1fr; } }
.pj-desc summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; }
.cta { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: 44px;
  padding: var(--sp-2) var(--sp-4); margin: var(--sp-3) 0; border-radius: 8px;
  background: var(--accent, #4a5f6b); color: #fff; text-decoration: none; font-weight: 600; }
.kind-h { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted, #888); margin: var(--sp-3) 0 var(--sp-1); }
.kind-list { list-style: none; margin: 0 0 var(--sp-2); padding: 0; }
/* §5.5 Touch-Targets: die Einträge unter „Inhalt nach Art" sind Fingerziele.
   Die Zeile ist Flex, damit der optionale .kindtag inline neben dem Link bleibt;
   der Link selbst (.kind-link) trägt die 44px Höhe. */
.kind-list li { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); padding: 0; }
.kind-link { min-height: 44px; display: flex; align-items: center; }
.kindtag { font-size: 11px; color: var(--muted, #888); margin-left: var(--sp-2); }

/* Rhythmus (Spec §5.1-Feedback): Seitentitel sitzen auf jeder Seite konsistent
   unter der sticky Topnav. Die Detailseite trägt ihren h1 in .pj-head (Flex,
   baseline) statt als direktes .shell>h1 — derselbe Rhythmus, ohne die Badges
   aus der Baseline zu kippen (Margin am h1, nicht am Flex-Container). */
.shell > h1, .shell > .pj-head > h1 { margin: var(--sp-5) 0 var(--sp-4); }

/* ---- S5-P2: /proposals Triage — Gruppen, Confidence-Balken, Bulk ---- */
.tri-group { margin: var(--sp-4) 0; }
.tri-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: var(--sp-3); background: var(--accent-soft, #efe7ff);
  border: 1px solid var(--accent-border, #c9b8ff); border-radius: 12px;
  padding: var(--sp-3) var(--sp-4); }
.tri-title { display: block; }
.tri-explain { margin: var(--sp-1) 0 0; font-size: 13px; color: var(--muted, #666); }
.tri-bulk { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end; }
.btn-bulk { min-width: auto; padding: 0 var(--sp-3); font-size: 13px; white-space: nowrap; }
.tri-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: var(--sp-3); background: var(--surface, #fff); border: 1px solid var(--border, #e2e0dc);
  border-radius: 8px; padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-2); }
.tri-card form { display: inline; }
.tri-card-explain { grid-column: 1 / -1; margin: 0; }
.tri-card-conf { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.tri-conf { font-size: 12px; color: var(--muted, #666); }
.tri-by { font-size: 11px; color: var(--muted, #888); }
.tri-card-actions { display: flex; gap: var(--sp-2); flex-wrap: nowrap; justify-self: end; }
.conf-bar { width: 120px; max-width: 40vw; height: 6px; border-radius: 999px;
  background: var(--border, #e2e0dc); overflow: hidden; }
.conf-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent, #6b46ff); }
.conf-0 { width: 0; } .conf-5 { width: 5%; } .conf-10 { width: 10%; } .conf-15 { width: 15%; }
.conf-20 { width: 20%; } .conf-25 { width: 25%; } .conf-30 { width: 30%; } .conf-35 { width: 35%; }
.conf-40 { width: 40%; } .conf-45 { width: 45%; } .conf-50 { width: 50%; } .conf-55 { width: 55%; }
.conf-60 { width: 60%; } .conf-65 { width: 65%; } .conf-70 { width: 70%; } .conf-75 { width: 75%; }
.conf-80 { width: 80%; } .conf-85 { width: 85%; } .conf-90 { width: 90%; } .conf-95 { width: 95%; }
.conf-100 { width: 100%; }

/* Puls v2.1: Projekt-Selector mobil als Bottom-Sheet. Lebt HIER (nicht in
   layout.css), weil components.css als letztes lädt — sonst gewinnt die
   absolute-Dropdown-Basisregel oben die Kaskade und top+bottom quetschen das
   Panel auf einen Streifen (gefunden per Playwright-DoD, 2026-08-01). */
@media (max-width: 767px) {
  .sel-panel { position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-width: none; max-height: 70vh; overflow: auto; z-index: 45;
    border-radius: 16px 16px 0 0; box-shadow: 0 -6px 24px rgba(0,0,0,.25); }
}

/* ============================================================================
   Puls v2.2 — Grafisches System (Ideen 1+2+3): Aktivitätsband, Summary-Segment-
   balken, Prozent-Schritt-Utilities. EIN System — dieselben Typ-Farben
   (die --ev-* Tokens) in Band und Balken; die Legende gilt für beide.
   CSP: alle Höhen/Breiten/Positionen über 5%-Schritt-Klassen, kein Inline-Style.
   ============================================================================ */

/* Typ-Kanal-Flächen (Band-Segmente + Summary-Balken + Legende teilen sich diese) */
.bd-created { background: var(--ev-created); }
.bd-linked  { background: var(--ev-linked); }
.bd-updated { background: var(--ev-updated); }
.bd-other   { background: var(--ev-other); }

/* --- Idee 1: Aktivitätsband (Signature) --- */
.pls-band-wrap { margin: var(--sp-4) 0 var(--sp-5); }
.pls-band-wrap { overflow-x: clip; }  /* Marker-Pill darf nie H-Scroll erzeugen */
.pls-band { position: relative; display: flex; align-items: flex-end;
  gap: 4px; height: 56px;
  border-bottom: 1px solid var(--line, #dbe1e9); }  /* Grundlinie: Balken stehen AUF etwas */
/* Slots füllen die Bandbreite (Marker-Position rechnet in Tages-Fünfteln);
   der eigentliche Balken bleibt schlank und mittig im Slot. */
.pls-day { flex: 1 1 0; height: 100%; display: flex;
  flex-direction: column-reverse; align-items: center; gap: 2px;
  text-decoration: none; overflow: clip; }  /* Rundungs-Overshoot am Max-Tag kappen */
.pls-day.pls-empty { pointer-events: none;
  /* Leere Tage: 3px-Stummel auf der Grundlinie — Tagesrhythmus ohne Fake-Daten */
  box-shadow: inset 0 -3px 0 var(--raised, #e8e6e2); border-radius: 2px 2px 0 0; }
.pls-seg { display: block; width: min(100%, 18px); min-height: 2px; }
.pls-seg:last-child { border-radius: 2px 2px 0 0; }   /* oberstes Segment gerundet */
/* „Zuletzt hier"-Marker als gestrichelte Linie IM Band (Mock: „wird zur Linie
   im Band"); left kommt aus der pos-N-Klasse (Tages-Mitte). */
.pls-marker { position: absolute; top: -2px; bottom: -2px; width: 0;
  border-left: 2px dashed var(--accent); pointer-events: none; }
.pls-marker.at-right .pls-marker-pill { left: auto; right: 5px; }  /* Pill bleibt im Band */
.pls-marker-pill { position: absolute; top: -6px; left: 5px; white-space: nowrap;
  font-size: 9px; color: var(--accent); background: var(--accent-soft);
  border: 1px solid var(--accent-border); border-radius: 4px; padding: 0 4px; }
.pls-band-lbl { display: flex; justify-content: space-between; font-size: 10px;
  color: var(--muted, #888); margin-top: var(--sp-1); }
.pls-legend { display: flex; flex-wrap: wrap; gap: var(--sp-3); list-style: none;
  margin: var(--sp-2) 0 0; padding: 0; font-size: 11px; color: var(--muted, #888); }
.pls-legend li { display: inline-flex; align-items: center; gap: var(--sp-1); }
.lg-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; }

/* --- Idee 3: Summary-Segmentbalken (vor dem Summary-Text) --- */
.segbar { display: inline-flex; height: 8px; width: 120px; max-width: 40vw;
  gap: 2px; vertical-align: middle; margin-right: var(--sp-2); border-radius: 4px;
  overflow: hidden; }
.segbar-seg { height: 100%; }

/* --- Prozent-Schritt-Utilities (5er) — Höhen (Band), Breiten (Balken),
   Marker-Position. Muster wie conf-N; getrennte Eigenschaften pro Set. --- */
.h-0 { height: 0; } .h-5 { height: 5%; } .h-10 { height: 10%; } .h-15 { height: 15%; } .h-20 { height: 20%; }
.h-25 { height: 25%; } .h-30 { height: 30%; } .h-35 { height: 35%; } .h-40 { height: 40%; } .h-45 { height: 45%; }
.h-50 { height: 50%; } .h-55 { height: 55%; } .h-60 { height: 60%; } .h-65 { height: 65%; } .h-70 { height: 70%; }
.h-75 { height: 75%; } .h-80 { height: 80%; } .h-85 { height: 85%; } .h-90 { height: 90%; } .h-95 { height: 95%; }
.h-100 { height: 100%; }
.w-0 { width: 0; } .w-5 { width: 5%; } .w-10 { width: 10%; } .w-15 { width: 15%; } .w-20 { width: 20%; }
.w-25 { width: 25%; } .w-30 { width: 30%; } .w-35 { width: 35%; } .w-40 { width: 40%; } .w-45 { width: 45%; }
.w-50 { width: 50%; } .w-55 { width: 55%; } .w-60 { width: 60%; } .w-65 { width: 65%; } .w-70 { width: 70%; }
.w-75 { width: 75%; } .w-80 { width: 80%; } .w-85 { width: 85%; } .w-90 { width: 90%; } .w-95 { width: 95%; }
.w-100 { width: 100%; }
.pos-0 { left: 0; } .pos-5 { left: 5%; } .pos-10 { left: 10%; } .pos-15 { left: 15%; } .pos-20 { left: 20%; }
.pos-25 { left: 25%; } .pos-30 { left: 30%; } .pos-35 { left: 35%; } .pos-40 { left: 40%; } .pos-45 { left: 45%; }
.pos-50 { left: 50%; } .pos-55 { left: 55%; } .pos-60 { left: 60%; } .pos-65 { left: 65%; } .pos-70 { left: 70%; }
.pos-75 { left: 75%; } .pos-80 { left: 80%; } .pos-85 { left: 85%; } .pos-90 { left: 90%; } .pos-95 { left: 95%; }
.pos-100 { left: 100%; }

/* ============================================================================
   Wissen-Seite (S5-C Task 3): Suchfeld-Kopf, serverseitige Kind-Chips +
   Kontext-Selector (letzterer erbt .sel-*/.chip/.filter-active von oben),
   Treffer-Karten, Pagination. Alle mobilen Overrides leben HIER (components.css
   lädt zuletzt) — keine konkurrierenden Regeln über Dateigrenzen.
   ============================================================================ */

/* Suchfeld-Kopf: input ≥16px (verhindert iOS-Auto-Zoom beim Fokus). */
.wsn-search { display: flex; align-items: center; gap: var(--sp-2);
  margin: var(--sp-4) 0 var(--sp-3); }
.wsn-search-field { flex: 1 1 auto; display: flex; align-items: center;
  gap: var(--sp-2); min-width: 0; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border, #ccc); border-radius: 999px;
  background: var(--surface, #fff); }
.wsn-search-ico { color: var(--muted, #888); flex: none; }
.wsn-input { flex: 1 1 auto; min-width: 0; border: 0; outline: none;
  background: transparent; color: inherit; font: inherit; font-size: 16px; }
.wsn-input:focus { box-shadow: none; }

/* Filter-Reihe: Kind-Chips (Links) + Kontext-Selector. */
.wsn-filters { display: flex; align-items: flex-start; flex-wrap: wrap;
  gap: var(--sp-3); margin-bottom: var(--sp-3); }
.wsn-filters .chips { flex: 1 1 auto; }
/* Zähler im Chip; Kind-Punkt fluchtet in der Chip-Zeile (die .chip bleibt
   inline-block mit Ellipsis — kdot als inline-block mit vertical-align). */
.chip-n { color: var(--muted, #888); font-variant-numeric: tabular-nums; }
.chip.is-active .chip-n { color: inherit; }
.chip .kdot { vertical-align: middle; margin-right: 4px; }

/* Treffer-Zeile über der Liste. */
.wsn-resultline { font-size: 13px; color: var(--muted, #888); margin: 0 0 var(--sp-2); }
.wsn-resultline strong { color: var(--fg); }

/* Treffer-Karten-Liste: Zeile = ein <a> (44px), Grid Badge | Titel/Kontext |
   Zeit. .wsn-main ist Grid-Item (blockifiziert, min-width:0) → der Titel darf
   als Block ellipsen; text-overflow greift NIE auf einem Flex-Container. */
.wsn-hits { margin: 0 0 var(--sp-4); }
.wsn-hit, .wsn-hit:visited { display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--sp-3);
  align-items: center; min-height: 44px; padding: var(--sp-2) var(--sp-2);
  border-radius: 8px; text-decoration: none; color: inherit; }
.wsn-hit + .wsn-hit { border-top: 1px solid var(--line, #ececec); }
.wsn-hit:hover { background: var(--raised, #f3f2f0); }
.wsn-main { min-width: 0; }
.wsn-title { display: block; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* Projekt-Farbpunkt (erbt --pj-cur via .pjb-N auf der Zeile). */
.wsn-title .pj-dot { margin-right: 4px; }
.wsn-title mark { background: var(--accent-soft, #efe7ff);
  color: inherit; border-radius: 2px; padding: 0 1px; }
.wsn-sub { display: block; font-size: 11px; color: var(--muted, #888);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wsn-when { justify-self: end; font-size: 11px; color: var(--muted, #888);
  white-space: nowrap; }

/* Pagination: „Seite X von Y" + vor/zurück. */
.wsn-pager { display: flex; align-items: center; justify-content: center;
  gap: var(--sp-3); margin: var(--sp-4) 0; }
.wsn-pageno { font-size: 13px; color: var(--muted, #888); }
.wsn-pg-off { opacity: .45; pointer-events: none; }

/* „Zuletzt geändert" / „Zuletzt angesehen". */
.wsn-recent { margin-top: var(--sp-5); }

/* ── S5-C Task 5: strukturierte Attribute + Verbindungs-Bottom-Sheet ──────── */

/* Attribut-Zeilen (dt/dd) ersetzen den JSON-<pre>-Dump. Label-Spalte fest,
   Wert bricht um. Roh-Keys (unbekannt) stehen monospace. */
.attr-list { display: grid; grid-template-columns: minmax(120px, max-content) 1fr;
  gap: var(--sp-1) var(--sp-4); margin: 0; align-items: baseline; }
.attr-key { font-size: 12px; font-weight: 600; color: var(--muted, #888); }
.attr-key-raw { font-family: var(--font-mono, monospace); font-weight: 500;
  text-transform: none; }
.attr-val { font-size: 13.5px; color: var(--fg); overflow-wrap: anywhere; margin: 0; }
@media (max-width: 767px) {
  .attr-list { grid-template-columns: 1fr; gap: 0; }
  .attr-key { margin-top: var(--sp-2); }
}

/* ⓘ-Button je Verbindungs-Zeile — 44px Fingerziel, öffnet das Bottom-Sheet. */
.sheet-trigger { flex: none; margin-left: auto; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--muted, #888); border: 1px solid transparent;
  border-radius: var(--r-sm, 7px); font-size: 15px; cursor: pointer; }
.sheet-trigger:hover { color: var(--accent); border-color: var(--line);
  background: var(--accent-soft, #efe7ff); }

/* <dialog class="sheet">: Desktop zentriert, Mobil unten angedockt. Kein
   Padding auf dem Dialog selbst → ein Klick, der ihn trifft, ist der Backdrop
   (das .sheet-panel fängt seine eigenen Klicks). */
dialog.sheet { padding: 0; border: none; border-radius: var(--r, 12px);
  max-width: 460px; width: calc(100vw - 2 * var(--sp-4));
  background: var(--panel); color: var(--fg);
  box-shadow: 0 18px 48px rgba(0,0,0,.28); }
dialog.sheet::backdrop { background: rgba(8,19,32,.46); }
.sheet-panel { position: relative; padding: var(--sp-5) var(--sp-5) var(--sp-4); }
.sheet-close { position: absolute; top: var(--sp-2); right: var(--sp-2);
  width: 44px; height: 44px; display: inline-flex; align-items: center;
  justify-content: center; background: transparent; color: var(--muted, #888);
  border: none; border-radius: var(--r-sm, 7px); font-size: 22px; cursor: pointer; }
.sheet-close:hover { color: var(--fg); }
.sheet-title { font-size: 1.05rem; font-weight: 700; margin: 0 44px 0 0;
  line-height: 1.3; overflow-wrap: anywhere; }
.sheet-meta { font-size: 0.78rem; color: var(--faint, var(--muted));
  margin: var(--sp-2) 0 0; }
.sheet-excerpt { font-size: 0.9rem; color: var(--muted, #888); line-height: 1.5;
  margin: var(--sp-3) 0 0; overflow-wrap: anywhere; }
.sheet-loading, .sheet-error { font-size: 0.9rem; color: var(--muted, #888);
  margin: 0; }
.sheet-open { display: inline-block; margin-top: var(--sp-4); font-weight: 600; }

@media (max-width: 767px) {
  dialog.sheet { width: 100%; max-width: 100%; margin: 0 0 0 0;
    top: auto; bottom: 0; border-radius: var(--r, 12px) var(--r, 12px) 0 0; }
  .sheet-panel { padding-bottom: var(--sp-5); }
}

/* ── Ego-Graph (S5-C): radiales, touch-first Modul; ego.js baut das SVG. ──────
   Kind-Farben aus der geteilten kind_palette (--kc je .k-<kind>), Projekt-Ring
   aus dem Farbrad (pjb-N → --pj-cur). Keine Inline-styles (CSP). */
.ego { margin: var(--sp-2) 0 0; }
.ego > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center;
  font-family: var(--font-display); font-size: 13px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted, #888); }
.ego-body { padding-top: var(--sp-2); }
.ego-svg { display: block; width: 100%; max-width: 420px; height: auto;
  margin: 0 auto; touch-action: manipulation; }
/* Projekt-Desktop: die rechte Spalte hält die Nachbarschaft immer offen. */
@media (min-width: 768px) {
  .ego-pin > summary { pointer-events: none; list-style: none; cursor: default; }
  .ego-pin > summary::-webkit-details-marker { display: none; }
}

/* Knoten: unsichtbares 44px-Hit-Ziel, sichtbarer Disc kleiner (dataviz-Regel). */
.ego-node { cursor: pointer; }
.ego-node:focus { outline: none; }
.ego-hit { fill: transparent; }
.ego-disc { fill: color-mix(in oklab, var(--kc, var(--muted)) 20%, transparent);
  stroke: var(--kc, var(--muted)); stroke-width: 2; }
.ego-initial { fill: var(--kc, var(--muted)); font-family: var(--font-display);
  font-weight: 700; font-size: 11px; }
.ego-node.is-focus .ego-disc { stroke-width: 3; }
.ego-node[data-selected="1"] .ego-disc,
.ego-node:focus-visible .ego-disc { stroke: var(--accent); stroke-width: 3; }
.ego-edge { stroke: var(--faint, #ccc); stroke-width: 1.2; stroke-opacity: .5; }
.ego-pj { fill: none; stroke: var(--pj-cur, var(--line)); stroke-width: 2; }
/* kind_palette (Hex-Spiegel von kind_palette.py / graph.js TYPE_COLORS). */
.ego-node.k-concept{--kc:#4a7a9b}.ego-node.k-fact{--kc:#7a4a9b}
.ego-node.k-decision{--kc:#9b7a4a}.ego-node.k-template{--kc:#4a9b7a}
.ego-node.k-secret{--kc:#9b4a4a}.ego-node.k-config{--kc:#6b7a9b}
.ego-node.k-project{--kc:#4a7a4a}.ego-node.k-task{--kc:#9b9b4a}
.ego-node.k-document{--kc:#5a6a8a}.ego-node.k-note{--kc:#8a8a5a}
.ego-node.k-inbox_item{--kc:#7a8a9a}.ego-node.k-user{--kc:#9a7a7a}
.ego-node.k-tag{--kc:#7a9a8a}.ego-node.k-skill{--kc:#c97a3a}.ego-node.k-commit{--kc:#6a8a6a}

/* Info-Zeile unter dem SVG (Tap = Auswahl) + Überlauf-/Voll-Graph-Links. */
.ego-info { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  min-height: 44px; margin: var(--sp-2) 0 0; font-size: 13px; color: var(--muted, #888); }
.ego-info-title { font-weight: 600; color: var(--fg); overflow-wrap: anywhere; }
.ego-info-kind { font-size: 11px; color: var(--faint, var(--muted)); }
.ego-info-open, .ego-more-link, .ego-full { display: inline-flex; align-items: center;
  min-height: 44px; color: var(--accent); text-decoration: none; font-weight: 600; }
.ego-info-open { margin-left: auto; }
.ego-more { margin: var(--sp-1) 0 0; font-size: 12px; }
.ego-full { font-size: 12px; margin-top: var(--sp-1); }

/* ============================================================================
   S5-F Board — Read-only Task-Board (Projekt-Detailseite) + Listen-Segbar.
   Statusfarben als --st-*-Tokens (app.css, Light+Dark); Kante links im
   pjb-Muster (--st-cur erbt an Kinder). Farbe nie alleiniger Träger — die
   Gruppen-Header tragen den Klartext.
   ============================================================================ */
.st-blocked { --st-cur: var(--st-blocked); }
.st-in_progress { --st-cur: var(--st-progress); }
.st-open { --st-cur: var(--st-open); }
.st-done { --st-cur: var(--st-done); }
.st-closed { --st-cur: var(--st-closed); }
.st-none { --st-cur: transparent; }
/* Segmentbalken der Projektliste (Task 3) — Anatomie wie .segbar (Puls v2.2) */
.bs-blocked { background: var(--st-blocked); } .bs-in_progress { background: var(--st-progress); }
.bs-open { background: var(--st-open); } .bs-done { background: var(--st-done); }
.pj-tasks { display: block; margin-top: var(--sp-2); font-size: 12px; color: var(--muted, #666); }

/* ============================================================================
   S5-F-Politur (Live-Abnahme 2026-08-14): „grid-lose wall of text".
   (1) Board-Zeilen werden Surface-Karten (Muster .inbox-card) — die Fläche
   gibt der Rail das Raster zurück, die Status-Kante bleibt der Farbträger.
   (2) Feed-Inline-Titel bekommen eine Einzeilen-Klammer: lange pm-agent-Titel
   brachen sonst in 4-6 Zeilen Linktext um. min-width:0 am Flex-Kind ist
   Voraussetzung, sonst greift text-overflow in Flex-Layouts nie.
   ============================================================================ */
.feed-row > div { min-width: 0; }
.feed-inline { display: inline-block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* ============================================================================
   S5-G Board-Seite (Nicos Abnahme-Befund 2026-08-17: „mehr als Board
   visualisieren"): echte Status-Spalten auf /projekte/{id}/board. Der
   Scroll-Container hält den Body H-scroll-frei (Haus-Regel breite Inhalte);
   auf schmalen Viewports wird die Fläche zum swipebaren Board (scroll-snap).
   Die Spalte trägt den Zustand (Top-Kante über --st-cur), die Karten bleiben
   ruhige Surfaces; Blockiert-Karten behalten die Soft-Fläche als Eskalations-
   Nachdruck. Die Detailseite verweist nur noch per Summary-Karte hierher.
   ============================================================================ */
.boardp-scroll { overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: var(--sp-3); }
.boardp { display: grid; grid-auto-flow: column;
  grid-auto-columns: minmax(250px, 1fr); gap: var(--sp-4); align-items: start; }
.boardp-col { border-top: 3px solid var(--st-cur, var(--border, #ddd));
  padding-top: var(--sp-2); scroll-snap-align: start; min-width: 0; }
.boardp-h { font-size: 12px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted, #888); margin: 0 0 var(--sp-2); }
.boardp-card { background: var(--surface, #fff); border-radius: 8px;
  padding: var(--sp-2) var(--sp-3); }
.boardp-card + .boardp-card { margin-top: var(--sp-2); }
.boardp-title { display: flex; align-items: center; min-height: 44px; }
.boardp-col.st-blocked .boardp-card { background: var(--st-blocked-soft); }
.boardp-empty { font-size: 12px; color: var(--muted, #888); margin: var(--sp-2) 0; }
/* Detailseiten-Summary: eine Karte, ein Ziel — ersetzt die Gruppen-Textblöcke. */
.board-cta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3);
  background: var(--surface, #fff); border-radius: 10px; min-height: 44px;
  padding: var(--sp-3) var(--sp-4); text-decoration: none; color: inherit; }
.board-cta b { margin-left: auto; white-space: nowrap; color: var(--accent, #6b46ff); }
.board-cta .segbar { margin-right: 0; }

/* ============================================================================
   S5-D: /einstellungen — eine Seite, vier Sektionen (Passkeys · Sessions ·
   Tokens · OAuth-Clients) mit Anker-Navigation. Kein JS (reine #-Anker).
   Desktop: Anker-Zeile sticky unter der Topnav; mobil: gestapelte Sektionen,
   Anker-Zeile horizontal scrollbar. CSP: nur Klassen, keine Inline-Styles.
   ============================================================================ */
.set-anchors { position: sticky; top: var(--topnav-h, 56px); z-index: 30;
  display: flex; gap: var(--sp-2); margin: var(--sp-4) 0; padding: var(--sp-2) 0;
  overflow-x: auto; background: var(--surface, #fff);
  border-bottom: 1px solid var(--border, #e2e0dc); }
.set-anchor, .set-anchor:visited { display: inline-flex; align-items: center;
  min-height: 44px; padding: var(--sp-2) var(--sp-3); border-radius: 999px;
  white-space: nowrap; text-decoration: none; color: inherit;
  border: 1px solid var(--border, #ccc); font-size: 13px; font-weight: 600; }
.set-anchor:hover { background: var(--raised, #f3f2f0); }
/* Sprungziel darf nicht unter Topnav + sticky Anker-Zeile landen. */
.set-section { scroll-margin-top: calc(var(--topnav-h, 56px) + var(--sp-6) + var(--sp-6)); }
.set-section + .set-section { margin-top: var(--sp-5); }
.set-section > h2:first-child { margin-top: 0; }
/* §5.5: keine Tabelle ohne Scroll-Container — die Credential-Tabellen sind
   breit und dürfen mobil nicht die Seite horizontal aufreißen. */
.set-tbl { overflow-x: auto; }
/* Tabellen im Scroll-Wrapper dürfen nicht unter ihre Lesbarkeit schrumpfen:
   unterhalb von 48rem scrollt der Wrapper, statt dass Spalten zerbrechen
   (Befund Screenshot 390px: Sessions-Tabelle unlesbar zerquetscht). */
.set-tbl table, .tbl-scroll table { min-width: 48rem; }
.set-tbl th, .tbl-scroll th { white-space: nowrap; }

/* ============================================================================
   S5-D Task 3: Admin-Tabellen responsive (Spec §5.4/§5.5, Audit-Finding
   „keine Tabelle ohne Scroll-Container"). Lebt am Dateiende — components.css
   lädt zuletzt; keine konkurrierenden Regeln vor diesen. CSP: nur Klassen.
   ============================================================================ */

/* Scroll-Container um JEDE verbleibende Admin-Tabelle: die Tabelle scrollt in
   ihrem Wrapper, der Body reißt nie horizontal auf. Auch von shares.html (T2)
   genutzt (Tabelle in .card) — dort trägt die Karte Rahmen/Radius, der Wrapper
   nur den Scroll. Radius passend zu .card (10px). */
.tbl-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch;
  margin: var(--sp-3) 0; border-radius: 10px; }

/* Mobile Card-Liste als Alternative zu den breitesten Tabellen (Users,
   Webhooks): Desktop zeigt die Tabelle, mobil die Karten. BEIDE speisen sich
   aus derselben Jinja-Row-Liste (zwei Schleifen über users/sources) — kein
   Handler-seitiges Verdoppeln. Nur die mit .tbl-cards markierten Tabellen
   verschwinden mobil; audit/auth_audit behalten ihre scrollbare Tabelle. */
.acard-list { display: none; list-style: none; margin: var(--sp-3) 0; padding: 0; }
.acard { border: 1px solid var(--border, #e2e0dc); border-radius: 10px;
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-3);
  background: var(--surface, #fff); }
.acard.muted { opacity: .6; }
.acard-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  font-weight: 600; margin-bottom: var(--sp-2); }
/* Label-Spalte fest, Wert bricht um (dt/dd — dasselbe Muster wie .attr-list). */
.acard-dl { display: grid; grid-template-columns: minmax(72px, max-content) 1fr;
  gap: var(--sp-1) var(--sp-3); margin: 0; align-items: baseline; }
.acard-dl dt { font-size: 12px; font-weight: 600; color: var(--muted, #888); }
.acard-dl dd { margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
.acard-actions { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2); margin-top: var(--sp-3); }
/* §5.5: Aktionen in der Karte bleiben Fingerziele (≥44px). */
.acard-actions button, .acard-actions .btn { min-height: 44px; }
/* Karten-Aktionen können reine Links sein (/webhooks: „GitHub-App →“). */
.acard-actions a { min-height: 44px; display: inline-flex; align-items: center; }

@media (max-width: 767px) {
  .tbl-scroll.tbl-cards { display: none; }   /* breite Tabelle mobil ausblenden */
  .acard-list { display: block; }            /* Karten übernehmen */
}

/* ============================================================================
   GitHub-App-Admin-Seite (/integrations/github, Plan Task 9): App-Kopf,
   Owner-Checkliste, Zuordnungs-Formulare in Tabellenzellen, Fremd-Markierung.
   CSP: nur Klassen. Touch-Ziele ≥ 44px (Muster .acard-actions).
   ============================================================================ */
.gh-head { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2) var(--sp-4); }
.gh-head h2 { margin: 0; }
.gh-ok { color: var(--ok, #168a7f); font-weight: 600; }
.gh-warn { color: #9b7a4a; font-weight: 600; }
.gh-check { list-style: none; margin: var(--sp-3) 0; padding: 0; }
.gh-check li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding: var(--sp-1) 0; border-bottom: 1px solid var(--line, #e2e0dc); }
.gh-check .btn { min-height: 44px; display: inline-flex; align-items: center; }
.gh-assign { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  margin-right: var(--sp-2); }
.gh-assign select { min-height: 44px; max-width: 18rem; }
.gh-assign button { min-height: 44px; white-space: nowrap; }
.gh-manifest { margin: var(--sp-4) 0; }
.gh-manifest button { min-height: 44px; }
.gh-remove { margin-top: var(--sp-5); }
.gh-remove summary { cursor: pointer; min-height: 44px; display: flex; align-items: center;
  color: var(--muted, #888); }
/* §5.5: Secret-Form und „App entfernen“ sind ebenfalls Fingerziele (≥44px). */
.gh-secret button, .gh-remove form button { min-height: 44px; }
tr.is-foreign td { background: rgba(155, 122, 74, 0.08); }
