/*
 * Report base stylesheet — shared design system for every rendered /reports/*.html companion.
 *
 * Inlined verbatim into the generated, self-contained HTML by script-report-html-render.js, so it
 * must not reference external fonts, images or stylesheets. Layout is card-first (not table-first)
 * so the same markup reads well from a 360px phone up to a wide desktop without a separate mobile
 * variant. Colors live as tokens on :root and are redefined for dark mode plus an explicit
 * [data-theme] override, so the in-page theme toggle wins over the OS setting in both directions.
 */

/* ---------- tokens ---------- */
:root {
  /* Widest the report content ever gets, centred within whatever space is available. */
  --layout-max: 1440px;
  --bg: #f4f6f8;
  --panel: #ffffff;
  --panel-soft: #fafbfc;
  --text: #171a1f;
  --muted: #5d6472;
  --border: #e2e6eb;
  --border-strong: #cbd2db;
  /* brand primary — app-nebenzeit angular/projects/app-nebenzeit/src/styles/variables.scss --ion-color-primary */
  --accent: #05427f;
  --accent-soft: #e1e9f1;
  --accent-contrast: #ffffff;
  --danger: #cf3b23;
  --danger-soft: #fdecea;
  /* F-29c/2026-09-21: was #b57608 — 3.4:1 on --warn-soft (the amber badge) and 3.8:1 on white, below the 4.5:1 that
     small text needs. #8a5a06 measures 5.4:1 on --warn-soft, 5.9:1 on white; it is also the fill behind the pressed
     "refine" button, whose #fff text gains from the darker amber too. */
  --warn: #8a5a06;
  --warn-soft: #fdf3e0;
  --ok: #23824f;
  --ok-soft: #e6f6ed;
  --info: #05427f;
  --info-soft: #e1e9f1;
  /* High severity (new 4th level, plan REPORT_SCHEMA_HARDENING_V2 [D13]) and the uniform E/Idea marker
     ([D15]) — kept as their own tokens rather than reusing --danger/--warn, since both now stand for a
     single, unambiguous meaning (Kritisch=red, Medium=yellow) that a shared hue would blur. */
  --high: #c1580a;
  --high-soft: #fdead9;
  --idea: #7c3aed;
  --idea-soft: #f1e9fd;
  /* queued/falsified: decided-but-not-yet-actioned states (queued = human decided via PWA, agent hasn't
     implemented; falsified = flagged as a possible false positive, pending evidence review) — neither is
     "open" (undecided) nor "done" (finished), so they get their own hue instead of falling through to the
     unstyled base .tag/.status-badge rule. */
  --pending: #0f766e;
  --pending-soft: #e0f2f1;
  --shadow: 0 1px 2px rgba(16, 20, 28, .06), 0 4px 12px rgba(16, 20, 28, .04);
  --radius: 12px;
  --radius-sm: 8px;
  --gap: 16px;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #121419;
    --panel: #1a1d24;
    --panel-soft: #21252e;
    --text: #e7eaee;
    --muted: #98a0ad;
    --border: #2b303a;
    --border-strong: #3a414e;
    --accent: #4f8dd4;
    --accent-soft: #1b2938;
    /* F-29c/2026-09-21: text on a solid fill. White on the dark theme's light blue accent measured 3.45:1 (the active
       chip); near-black is 5.4:1 — and the light fills of the other pressed buttons need it just as much. */
    --accent-contrast: #0b1220;
    --danger: #ff7d67;
    --danger-soft: #35201d;
    --warn: #e0a94a;
    --warn-soft: #33291a;
    --ok: #5fc78e;
    --ok-soft: #172b21;
    --info: #4f8dd4;
    --info-soft: #1b2938;
    --high: #f0954f;
    --high-soft: #3a2717;
    --idea: #b794f6;
    --idea-soft: #2c2140;
    --pending: #2dd4bf;
    --pending-soft: #123a37;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  }
}

:root[data-theme="dark"] {
  --bg: #121419;
  --panel: #1a1d24;
  --panel-soft: #21252e;
  --text: #e7eaee;
  --muted: #98a0ad;
  --border: #2b303a;
  --border-strong: #3a414e;
  --accent: #4f8dd4;
  --accent-soft: #1b2938;
  --accent-contrast: #0b1220;
  --danger: #ff7d67;
  --danger-soft: #35201d;
  --warn: #e0a94a;
  --warn-soft: #33291a;
  --ok: #5fc78e;
  --ok-soft: #172b21;
  --info: #4f8dd4;
  --info-soft: #1b2938;
  --high: #f0954f;
  --high-soft: #3a2717;
  --idea: #b794f6;
  --idea-soft: #2c2140;
  --pending: #2dd4bf;
  --pending-soft: #123a37;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
}

/* ---------- base ---------- */
* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* <dialog>.showModal() traps focus but does NOT stop the page behind it from scrolling in every browser
   — report-base.js toggles this class (refreshModalLock) whenever any <dialog> is open/closed. */
html.modal-open { overflow: hidden; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.6 var(--font);
  -webkit-text-size-adjust: 100%;
}

a { color: var(--accent); }

code, kbd {
  font-family: var(--mono);
  font-size: .88em;
}

code {
  background: var(--panel-soft);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: .08em .35em;
  overflow-wrap: anywhere;
}

kbd {
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: .05em .4em;
  background: var(--panel-soft);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- app frame ---------- */
/* In-flow top toolbar (fixed 2026-09-06: was `position: fixed`, permanently floating over the
 * content on every scroll position — user feedback "nicht schwebend über dem Rest"). Renders as a
 * normal full-width row above `.layout`, same as any other page-level toolbar. */
.floating-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px clamp(16px, 3vw, 28px);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}

.head-meta {
  color: var(--muted);
  font-size: 12.5px;
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
}

.head-meta b { color: var(--text); font-weight: 600; }

/* Completion badge — ✅ all decidable codes closed, ⏳ + count while any is still in the works. Uses the
   existing ok/warn token pairs so it inherits the dark theme without its own colour set. The
   ".is-marker" variant (icon + number) is what the remote-app list row puts in front of a report title. */
.completion-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.6;
}

.completion-badge.is-done { background: var(--ok-soft); color: var(--ok); }
.completion-badge.is-open { background: var(--warn-soft); color: var(--warn); }

.completion-badge.is-marker {
  padding: 0;
  background: none;
  font-size: inherit;
}

/* The in-work number next to the marker variant's hourglass (2026-09-17) — small and in the warn tint so it
   reads as an annotation on the icon, not a competing badge of its own. */
.completion-badge.is-marker .cb-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--warn);
  margin-left: 1px;
}

/* Progress + "waiting on you" badges (renderProgressBadges() in report-render-core.js, user request
   2026-09-19) — one pill shape shared by the report header, the list row and the list title summary.
   Visible text rather than a bare icon (a tooltip is invisible on touch). Hue carries urgency:
   progress = --pending (decided, agent's turn); the flag counters take their hue from the owner rules below. */
.progress-badge,
.attention-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.progress-badge { background: var(--pending-soft); color: var(--pending); }

/* Status-model counters and tags (2026-09-19): hue follows who is on turn — you = --warn, agent =
   --pending, nobody = muted. The two states that block somebody right now (an open question, a blocked
   implementation) stay --danger. Shared by the header counters (.flag-badge) and the card-headline tags
   (.state-tag), so a symbol has the same colour wherever it appears. */
.flag-badge.owner-you,
.state-tag.owner-you { background: var(--warn-soft); color: var(--warn); }
.flag-badge.owner-agent,
.state-tag.owner-agent { background: var(--pending-soft); color: var(--pending); }
.flag-badge.owner-none,
.state-tag.owner-none { background: var(--panel-soft); color: var(--muted); }
/* PROGRESS-AXIS/2026-10-14: ❓ and 🧱 are progress values now (state-progress-*), no flags. */
.state-tag.state-progress-question,
.state-tag.state-progress-blocked { background: var(--danger-soft); color: var(--danger); }
.progress-badge.state-progress-in-progress,
.state-tag.state-progress-in-progress { background: var(--info-soft); color: var(--info); }

/* report-loop-watch live status (I7/I8, 2026-09-07) — only rendered while a loop is active, see
   renderLoopStatusBadge() in report-render-core.js. Reuses the info token pair, distinct from the
   ok/warn completion badge next to it since "a loop is watching" is orthogonal to "is this done". */
.loop-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.6;
  background: var(--info-soft, rgba(96, 165, 250, 0.15));
  color: var(--info, #60a5fa);
}

/* Auto-started loop (report-loop-watch § „Ein Loop — Umfang = Marker", creator auto-start: the session that
   created the report holds it, trigger `auto-creator`; F-206/2026-10-19: the earlier critical-finding auto-start is
   retired, its `auto-severity` marker is only still read) — same shape as the plain info badge above, just on the
   accent tint so "nobody asked for this, it started itself" is visible on the card without needing to hover the
   tooltip. */
.loop-status-badge.is-auto-trigger {
  background: var(--accent-soft);
  color: var(--accent);
}

/* Scope/Methodik are long — collapsed by default so the sticky header stays compact */
.head-details { margin-top: 6px; font-size: 12.5px; color: var(--muted); max-width: 900px; }

.head-details summary {
  cursor: pointer;
  color: var(--accent);
  width: fit-content;
}

.head-details > div { margin-top: 6px; }

.head-details b { color: var(--text); font-weight: 600; }

.head-actions { display: flex; gap: 8px; flex-shrink: 0; align-items: center; }

.layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: clamp(16px, 3vw, 32px);
  /* Shared with the remote app's detail back-bar so the bar and the content below it centre on the
     same axis — the bar lives in remote-app.css and would otherwise hard-code the same number. */
  max-width: var(--layout-max);
  margin: 0 auto;
  padding: clamp(16px, 3vw, 28px);
  align-items: start;
}

/* ---------- side navigation ---------- */
.side-nav {
  position: sticky;
  top: 16px;
  max-height: calc(100vh - 32px);
  overflow: auto;
  font-size: 13px;
}

.side-nav h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  margin: 0 0 8px;
}

.side-nav ul { list-style: none; margin: 0 0 14px; padding: 0; }

.side-nav li { margin: 0; }

.side-nav a {
  display: block;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  border-left: 2px solid transparent;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.side-nav a:hover { background: var(--panel-soft); color: var(--text); }

.side-nav a.active {
  background: var(--accent-soft);
  color: var(--accent);
  border-left-color: var(--accent);
  font-weight: 600;
}

.nav-toggle { display: none; }

/* ---------- summary panel (report name + description + counts + filter tiles, one card) ---------- */
.summary-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px clamp(14px, 2.2vw, 20px) 14px;
  margin-bottom: var(--gap);
}

.summary-panel h2 { margin: 0; font-size: 18px; }

/* Shared by the report header (h2, inline) and the remote-app list row (.remote-list-title, inline) — one
   small icon-only button, so it doesn't compete with the title itself. */
.copy-title-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 6px;
  padding: 0;
  border: 0;
  background: none;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
  opacity: .7;
}

/* Icon only (user request 2026-11-03): no background, no border, no padding box — hover and focus only lift the opacity. */
.copy-title-btn:hover,
.copy-title-btn:focus-visible { opacity: 1; }

/* F-29/2026-09-15: same insufficient-contrast gap F-18 closed for .summary-toggle — a border/background
   color swap alone has ~1.2:1 contrast against the panel in both themes, far below WCAG 1.4.11's 3:1
   minimum for a focus indicator, and must never be the only signal. */
.copy-title-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.summary-desc { margin: 8px 0 0; }












/* ---------- toolbar ---------- */
/* Plain in-flow block, deliberately not sticky/fixed (F-XX/2026-09-07, third report of the same
 * "floats around" complaint — the sticky+auto-hide/reveal logic never read as intentional to the user
 * no matter how it was tuned). User's own fallback, taken as-is: stay at the top of the document, no
 * scroll behavior at all — scrolling past it is the same as scrolling past any other section. */
/* The filter section is a framed panel, not a loose stack of rows (user request 2026-09-08, together
 * with variant D2). The frame is what makes "everything inside here narrows the list" a single visual
 * statement — before, the chips floated directly on the page background and the boundary between
 * filters and content was carried by whitespace alone. */
.toolbar {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.toolbar input[type="search"] {
  flex: 1 1 100%;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

/* ---------- filter levels (plan REPORT_APP_FILTERS_AND_WIZARD, Phase 2) ----------
 *
 * Mobile-first, and deliberately quiet: a level is a labelled block, not a boxed panel. The toolbar
 * already stacks four to five of these vertically on a phone, so every border, shadow or background the
 * level itself adds is paid four times over in vertical space and visual noise. The label carries the
 * hierarchy; the chips carry the interaction.
 *
 * Colour is untouched on purpose — brand `--accent` stays the only "selected" colour, exactly as before.
 * The three new states (disabled, empty, locked) are expressed through opacity and a dashed border, so
 * this can be restyled per variant without ever renegotiating the palette. */
/* Variant D3 (2026-09-17, replaces the D2/L2 row grid — user request): label above, chips below, one
 * block per level. The row grid's fixed 84px label column gave every level a shared vertical edge, but
 * it permanently took that width away from the chips — on a narrow phone, exactly where the chip row
 * needed the space most. Stacking costs vertical space instead, which is the tradeoff the user asked
 * for explicitly. */
.filter-level,
.sort-level {
  flex: 1 1 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 3px 0;
}

.filter-level[hidden] { display: none; }

.filter-level-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  min-height: 18px;
}

.filter-level-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* [I7] Per-level reset. Hidden until that level actually carries a selection, so an untouched toolbar
 * shows no reset affordances at all and the one that appears is unambiguous about what it clears. */
.filter-level-reset {
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  line-height: 1;
  padding: 2px 5px;
  border-radius: var(--radius-sm);
}

.filter-level-reset:hover { color: var(--accent); background: var(--accent-soft); }
.filter-level-reset[hidden] { display: none; }

.filter-level-lock {
  margin: 2px 0 0;
  font-size: 11px;
  color: var(--muted);
}

/* Chips wrap freely in the remaining column. Deliberately not a fixed column count: the space left
   after the label varies with viewport width, so any fixed grid would either clip labels on a narrow
   phone or leave a gap on a wide one. */
.filter-level-values {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-content: start;
}

/* The "Nur unentschieden" preset takes its own line inside the chip column — it is the level's
   shortcut, not a ninth status, and reading it inline between 🔓 Offen and ✅ Behoben would say the
   opposite. */
.filter-level-values .chip-span2 { flex: 1 1 100%; }

.filter-level-lock[hidden] { display: none; }

/* Locked = the level still exists and still says what it would do, it just cannot act right now
 * ([D13] "merely empty → lock"). Half opacity plus the 🔒 note above; no display:none, because a filter
 * row that vanishes and reappears is precisely the shape-shifting this redesign removed. */
.filter-level.is-locked .filter-level-values { opacity: .45; }

/* A chip whose Klick-Versprechen is 0. Kept in place and kept readable — the count is the message. */
.chip:disabled {
  cursor: not-allowed;
  opacity: .4;
}

.chip:disabled:hover { border-color: var(--border); }

/* The "Nur unentschieden" preset spans both columns of the status grid: it is not a seventh status but
 * the level's shortcut, and half-width next to 🔓 Offen would read as one. */
.toolbar-row-grid2 .chip-span2 { grid-column: 1 / -1; }

/* [I6] The way out of an empty result, appended to the empty state's own sentence. */
.empty-state-hints {
  display: block;
  margin-top: 6px;
  font-size: 13px;
  color: var(--muted);
}

/* One full-width block per filter group (type / severity / status / undecided / reset) — see
 * renderToolbar(): keeps each group's chips wrapping only against their own kind, never against a
 * different group.
 *
 * F-XX/2026-09-07: this used to hold only up to ~768px — a `min-width: 768px` override below then let
 * `.toolbar-row` shrink to `flex: 0 1 auto; width: auto`, which packed multiple groups onto the same
 * flex line whenever they fit ("cuts the toolbar's vertical footprint"). At 1280px, that meant the
 * type/severity/status rows all rendered on one shared line, `top` identical for all three, wrapping
 * against *each other* — exactly what the comment above says this rule exists to prevent. Which groups
 * shared a line, and where the line broke, depended on viewport width and each group's current chip
 * count, so the toolbar visibly reshuffled between adjacent widths and between reports with a different
 * number of severities (user report: "die filter schwirren umher"). Full-width stacking now applies at
 * every viewport — deterministic, and the vertical space it costs on desktop is the tradeoff for a
 * toolbar that no longer reflows depending on width. */
.toolbar-row {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  gap: 6px;
  width: 100%;
}

/* Severity row (🔴🟡🔵🟢) and status row (🔓 Offen/🚫 Falsifiziert/✅ Behoben/🤝 Akzeptiert/
 * ❌ Ignoriert): a hard 2-column grid, not a flex row — user-requested 2026-09-07, "max 2 Stück pro
 * Reihe, nicht alle 4 in einer Zeile" for severity, then explicitly extended to the status row the same
 * way ("offen/behoben noch 2er paar full breite wie severity"). Grid over flex is deliberate here, not
 * just style: with chips of uneven label length (Kritisch/Achtung/Info/"OK / Stärke"; "Falsifiziert
 * (Vorschlag)" is the longest status label) squeezed into one `flex: 1 1 0` row, `min-width: 0` shrank
 * each chip below its content's natural width — and `.chip` has `white-space: nowrap` with no
 * `overflow`/`text-overflow` handling, so the label rendered wider than its pill and visually spilled
 * into the neighboring chip (measured: "🟢 OK / Stärke3" was 111px of content in an 83px box). A
 * 2-column grid gives each chip roughly half the row — always enough room for these labels — and,
 * unlike flex-wrap, the column count never changes with viewport width, so it can't reflow into
 * 1-per-row or 4-per-row at some in-between breakpoint. Applies at every width, not just mobile: the
 * previous behavior (equal-flex only under 768px, auto-width and free to share a line with other groups
 * above it) was itself part of the inconsistency being fixed. Status can have up to 6 chips, so this
 * wraps into up to 3 rows of 2 — still deterministic, never 3-and-a-widow or all 6 crammed into one
 * line. */
.toolbar-row-grid2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
}

.toolbar-row-grid2 .chip {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
}

/* "Nur unentschieden" is the only chip alone in its own row (unlike every other filter, which shares
 * its row with siblings from renderChips()/renderSeverityChips()/renderStatusChips()) — content-sized by
 * default it sat left-aligned with empty row to its right, and unlike "Filter zurücksetzen" right below
 * it (a .btn-block, already full-width) looked like an inconsistent afterthought on mobile, where every
 * other control in the toolbar spans the full row. User-requested 2026-09-07, mobile only: above the
 * 768px breakpoint the row has room to spare regardless, and stretching a single pill-shaped toggle to
 * the full container width there would look like a form field, not a filter chip. */
@media (max-width: 767px) {
  .chip-block {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
  }
}

/* Type (F/E/P) row: equal-width columns instead of content-sized chips, so a short "P 3" chip doesn't
   look tiny next to "F 8" — mobile only, user-requested 2026-09-06; the row itself now always spans full
   width (see .toolbar-row above), so this only governs the chips' own share of that width. */
@media (max-width: 767px) {
  .toolbar-row-equal .chip {
    flex: 1 1 0;
    min-width: 0;
    text-align: center;
  }
}

.btn-block {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
}

.toolbar input[type="search"]:focus-visible,
.chip:focus-visible,
.btn:focus-visible,
.card-head:focus-visible,
.dbtn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.chip {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 999px;
  padding: 7px 13px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  /* Belt-and-braces alongside the layout fixes above: a compressed chip clips its own label instead of
     spilling text past its pill into whatever sits next to it (report-render-escaping/-consistency
     tests can't catch a visual overflow, so this stays defensive rather than relying on every chip
     row always having enough room). */
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.chip:hover { border-color: var(--border-strong); }

.chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

.chip .count { opacity: .7; margin-left: 5px; font-variant-numeric: tabular-nums; }

.btn {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 8px 13px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
}

.btn:hover { border-color: var(--border-strong); }

.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
  font-weight: 600;
}

.btn-icon { padding: 8px 10px; line-height: 1; }

/* ---------- code cards ---------- */
.group-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 22px 0 10px;
  scroll-margin-top: 160px;
}

.group-head h2 { margin: 0; font-size: 16px; }

.group-head .group-count { color: var(--muted); font-size: 13px; }

.cards { display: flex; flex-direction: column; gap: 10px; }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  scroll-margin-top: 160px;
  /* Base transition lives here, not on .is-updated (same reasoning as .remote-list-row's identical
     pair in remote-app.css, E-10/2026-09-08): the fade-out on class removal needs the transition
     declared on the element's resting style, not only on the temporary class that is gone by the time
     the transition is supposed to be running. box-shadow added 2026-09-17 alongside .is-updated's own
     box-shadow ring, same reasoning. */
  transition: background 3s ease-out, box-shadow 3s ease-out;
}

.card.sev-krit { border-left-color: var(--danger); }
.card.sev-high { border-left-color: var(--high); }
.card.sev-warn { border-left-color: var(--warn); }
.card.sev-ok { border-left-color: var(--ok); }
.card.sev-info { border-left-color: var(--info); }
.card.sev-idea { border-left-color: var(--idea); }

/* Brief highlight for a card whose code just changed via reloadReportInPlace() (remote-app.js,
   2026-09-14 real-time-sync) — remote-app.js removes this class again after 4s, and the base .card
   transition above turns that removal into a fade, exactly like .remote-list-row.is-updated. The fade-IN
   transitions too (short + snappy, distinct from the 3s fade-out above). */
/* FLASH-INVERT/2026-09-24 (user report: some texts stayed black on the blue flash, "the font must be inverted on
   the whole card"): the flash is now the list row's solid --accent with EVERY text inverted — descendants included,
   since the code, tags, summary preview and buttons each carry a colour of their own that no inherited colour
   reaches. Tinted pills and unpressed buttons get a translucent contrast wash so their shape survives; pressed
   decision buttons keep their solid fill (already contrast text). The <dialog> is excluded — a modal open while
   its card flashes must stay readable. remote-app.js flashUpdated() swaps .is-updated for .is-updated-fading, which
   lets the descendants fade back over the same 3s as the card's background. */
.card.is-updated {
  background: var(--accent);
  color: var(--accent-contrast);
  box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow);
  transition: background 0.35s ease, box-shadow 0.35s ease;
}

.card.is-updated > :not(dialog),
.card.is-updated > :not(dialog) * {
  color: var(--accent-contrast) !important;
  border-color: color-mix(in srgb, var(--accent-contrast) 45%, transparent) !important;
  transition: color 0.35s ease, background-color 0.35s ease, border-color 0.35s ease;
}

/* FLASH-BADGE/2026-11-03: .progress-group (Entscheidung | Progress pill in the card head) keeps its own opaque panel fill,
   so the inverted white text sat on a light pill and was unreadable. It needs the same translucent wash as the other pills. */
.card.is-updated > :not(dialog) :is(.tag, .state-tag, .progress-group, .completion-badge, .card-solution, .rf-btn:not([aria-pressed="true"])) {
  background-color: color-mix(in srgb, var(--accent-contrast) 16%, transparent) !important;
}

.card.is-updated .card-head:hover { background: transparent; }

.card.is-updated-fading > :not(dialog),
.card.is-updated-fading > :not(dialog) * {
  transition: color 3s ease-out, background-color 3s ease-out, border-color 3s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .card,
  .card.is-updated,
  .card.is-updated > :not(dialog) *,
  .card.is-updated-fading > :not(dialog) * {
    transition: none;
  }
}

.card.is-hidden { display: none; }

.card.is-target {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Flex column (2026-09-06: severity+ID/Status top row added) — V5/"Minimal-Fix" (2026-09-06 user choice
   among 5 rendered variants) already needed every child full-width; a plain column flex container gives
   every direct child that for free via the default `align-items: stretch`, without the grid-column
   span-hack the previous 2-column grid needed. */
.card-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
  padding: 12px 14px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.card-head:hover { background: var(--panel-soft); }

/* Severity (left, next to the code — mirrors the modal's own "{severity} CODE" header) and Status
   (right) — both moved out of the .card-tags cloud onto their own top row (2026-09-06 user request). */
.card-head-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
}

/* State-tag headline (status model, 2026-09-19; first the "waiting on you" headline of the same day) — sits
   ABOVE .card-head-top, never inside it: code + severity on the left and Zustand + Entscheidung on the right
   must stay one line, and an extra tag in that row is what overflowed a 390px card (an attempt to fix that
   by wrapping the row put the status on its own line, which is the wrong way round). One symbol-only pill
   (.state-tag) per progress value and per flag — the name is in title/aria-label, the header legend lists
   them — so a card with three flags still costs one short line. Colour per owner, see .state-tag.owner-*. */
.card-attention {
  /* CARD-BADGES/2026-10-04: second line under code + Zustand, right-aligned. */
  align-self: flex-end;
  justify-content: flex-end;
  max-width: 100%;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

.state-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 12.5px;
  line-height: 1.7;
  cursor: help;
}

/* SOLO-TAG-LABEL/2026-09-24, WIZARD-REDESIGN/2026-10-04: every tag shows its name next to its symbol; the headline wraps, a tag does not. */
.state-tag.has-label { gap: 5px; padding: 0 9px 0 7px; white-space: nowrap; }
.state-tag-label { font-weight: 600; }

/* Zustand tag + Entscheidung tag, right-aligned in .card-head-top — nowrap keeps the row one line. */
.card-stand {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

/* ATTACH/2026-09-25: count of screenshots attached to a code, next to the status tags. */
.card-attach { font-size: 12px; color: var(--muted); font-weight: 600; }

.tag.tag-decision.decision-fix { background: var(--pending-soft); border-color: transparent; color: var(--pending); }
.tag.tag-decision.decision-refine { background: var(--info-soft); border-color: transparent; color: var(--info); }

.card-key {
  font-weight: 700;
  font-family: var(--mono);
  font-size: 13.5px;
  white-space: nowrap;
}

/* Optional short one-line title (2026-09-06) — bold, above .card-summary; wraps normally like
   .card-summary rather than being cut off, so the full headline is always readable on the card
   (2026-09-08 user request — a clipped headline forced opening the modal just to read the title). */
/* CARD-HEADLINE-LINE/2026-10-04: .card-key-group holds the code only; the headline is a sibling line below row 1. */
.card-key-group {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.card-headline {
  /* CARD-HEADLINE-LINE/2026-10-04: its own full-width line between the code row and the state-tag row. */
  display: block;
  width: 100%;
  font-weight: 700;
  margin: 0;
  text-align: left;
  overflow-wrap: anywhere;
}

/* Clamped to 8 lines (plan REPORT_SCHEMA_HARDENING_V2 [D3]/[D4]) so a "complex" fixClass summary (up to
   20 sentences) can't blow up the compact list view — report-base.js's initSummaryToggles() measures the
   clamped overflow and appends a .summary-toggle link only where one is actually needed. */
.card-summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 8;
  line-clamp: 8;
  overflow: hidden;
  min-width: 0;
  margin: 2px 0 0;
}

.card-summary.is-expanded {
  display: block;
  -webkit-line-clamp: unset;
  line-clamp: unset;
  overflow: visible;
}

/* `role="button"` span, not a real <button> — .card-summary lives inside .card-head, which already IS a
   button, and HTML forbids nesting one button inside another. Styled as a real small outline button
   (border + background, mirrors .btn/.chip below) instead of the earlier underlined-link look — user
   feedback 2026-09-15 ("mehr anzeigen ist beschissen... richtigen button outline draus"), the plain text
   link didn't read as an interactive control next to the card's other real buttons. Deliberately smaller
   than the standard .btn (padding/font both reduced) since this is a secondary, low-emphasis toggle, not
   a primary action. */
.summary-toggle {
  display: inline-block;
  margin-top: 6px;
  padding: 4px 10px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

.summary-toggle:hover,
.summary-toggle:focus-visible {
  background: var(--accent-soft);
}

/* F-18/2026-09-15: `.summary-toggle` is a `role="button"` span (see the comment above), not a real
   <button>, so it needs the exact same 2px accent outline treatment as its siblings (.chip/.btn/
   .card-head/.dbtn above) — still needed even now that the toggle has its own visible border, since a
   border alone doesn't satisfy WCAG 1.4.11's 3:1 focus-indicator requirement (the border colour doesn't
   change on focus, so without this outline a keyboard user gets no extra signal beyond the same border
   they always see). */
.summary-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Mobile only, full block width (user request 2026-09-15: "mobile block width", same full-width pattern
   as .btn-block/.chip-block elsewhere in this file) — a small button floating at content width read as
   an odd, easy-to-miss tap target on a phone; spanning the row makes it unmistakably tappable. Padding/
   font stay at the same small size as desktop ("kleiner Button aber") — only the width changes. */
@media (max-width: 767px) {
  .summary-toggle {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
  }
}

/* Status-appropriate field preview (Lösung/Umsetzung/Begründung, see previewField in deriveCode()) shown
   directly on the card so a reader can judge the fix without opening the modal (2026-09-08 user request).
   Clamped to a few lines — a multi-step a)/b)/c) solution would otherwise blow up list scanning; the full,
   unclamped field still appears in the modal's detail table. Plain text, no box (2026-09-15 user request:
   "kein blockquote element" — the previous background/padding/rounded-corner treatment read as a callout
   box; this now matches .card-summary right above it exactly, same clamp-and-"Mehr anzeigen" mechanism,
   just unstyled body text). */
.card-solution {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  overflow: hidden;
  margin: 6px 0 0;
}

/* Same expand mechanism as .card-summary.is-expanded (report-base.js initSummaryToggles()) — a 5-line
   clamp (was 4 before the label moved onto its own line below) cuts off multi-step solutions especially
   on narrow/mobile widths, where fewer characters fit per line (F-XX/2026-09-14: "auf mobilem Layout
   trotzdem manchmal der untere Bereich der Lösung abgeschnitten"). */
.card-solution.is-expanded {
  display: block;
  -webkit-line-clamp: unset;
  line-clamp: unset;
  overflow: visible;
}

/* User request 2026-09-15: the "LÖSUNG"/"UMSETZUNG" kicker used to sit inline right before the value
   text on the same line — cramped, and easy to misread as part of the sentence. `display: block` alone
   pushes `.card-solution`'s value text (plain sibling text, no wrapping element) onto its own line below,
   same headline-above-value pattern the modal's own `.detail-table` already uses on mobile (`tbody th`
   stacked above `tbody td`, see the `@media (max-width: 720px)` block further down) — this just applies
   it to the card preview too. Coloured accent-blue instead of the previous muted grey, so the kicker reads
   as a clear "this is the fix" headline, not a faint caption (the border strip this comment used to
   reference next to it was removed 2026-09-15 — see .card-solution's own comment above). */
.card-solution-label {
  display: block;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 13px;
  letter-spacing: .03em;
  color: var(--accent);
  margin: 0 0 3px;
}

.card-tags {
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 9px;
  background: var(--panel-soft);
  white-space: nowrap;
}

/* I-live/2026-09-14: deliberately the quietest tag on the card — see renderCard()'s commentCountTag
   comment for why. No border/background of its own (transparent, inherits nothing that would draw the
   eye), small and muted, purely a "there's a comment here" presence hint rather than a status. */
.tag.tag-comment-count {
  border-color: transparent;
  background: none;
  padding: 1px 4px;
  color: var(--muted);
  font-size: 11px;
}

.tag.status-open { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.tag.status-fixed { background: var(--ok-soft); border-color: transparent; color: var(--ok); }
.tag.status-refine,
.tag.status-queued { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
/* 2026-09-19 status model: refine/queued are the Zustand "Offen" like `open` — the decision they carry has
   its own tag (.tag-decision) next to it, so their Zustand tag no longer needs a hue of its own. falsified
   keeps its own hue: decided, not yet actioned, see the --pending token comment above. */
.tag.status-falsified { background: var(--idea-soft); border-color: transparent; color: var(--idea); }
/* E-03/2026-10-03: Backlog is the Zustand "Backlog" (done, not now), no longer "Offen" — quiet like the other end states. */
.tag.status-backlog,
.tag.status-ignored,
.tag.status-accepted { background: var(--panel-soft); color: var(--muted); }
/* P codes carry no decision state — the dash is a placeholder, not a status, so it stays visually quiet. */
.tag.status-none { background: transparent; border-color: var(--border); color: var(--muted); }

/* fixClass (F-only, plan REPORT_SCHEMA_HARDENING_V2 [D5]) is metadata, not an alert — bolder/stronger
   border than a plain category tag so it's noticeable, but deliberately not severity-colored, since its
   trivial/standard/complex icon already carries a color cue. */
.tag.tag-fixclass { border-color: var(--border-strong); color: var(--text); font-weight: 700; }

/* F-33/2026-09-08, generalised 2026-09-19: a code waiting on the user (unanswered question, open decision)
   must be visible at a glance on the card — the headline is .card-attention above, the banner in the detail
   dialog is below. */

/* Banner at the top of a code's detail dialog: one .state-banner-line per progress value and flag, each
   saying who is on turn and what is missing. Hue by who is on turn (renderStateBanner(), E-01/2026-09-24): something
   on the user (.attention-you) gets the warn tint, only the agent (.attention-agent) the pending tint, anything else
   the neutral one. */
.attention-banner {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  line-height: 1.45;
}

.attention-banner.attention-you { background: var(--warn-soft); color: var(--warn); }
.attention-banner.attention-agent { background: var(--pending-soft); color: var(--pending); }
.attention-banner.attention-info { background: var(--panel-soft); color: var(--text); }

.state-banner-line { margin: 0; }
.state-banner-line + .state-banner-line { margin-top: 6px; }

/* Done section of a group (status model, 2026-09-19): the finished codes folded into one native <details>
   below the not-done ones. A closed <details> prints closed, so report-base.js opens every fold on `beforeprint`. */
.cards-subhead { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--muted); }

.done-fold { margin-top: 14px; }

.done-fold > summary {
  cursor: pointer;
  width: fit-content;
  margin-bottom: 10px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}

/* Symbol legend (D61): symbol-only tags need their names somewhere a touch screen can read them. */
.symbol-legend { margin-top: 8px; font-size: 12.5px; color: var(--muted); }

.symbol-legend > summary { cursor: pointer; color: var(--accent); width: fit-content; }

.legend-groups { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-top: 6px; }

.legend-group h4 { margin: 0 0 2px; font-size: 12px; color: var(--text); }

.legend-group ul { list-style: none; margin: 0; padding: 0; }

.legend-icon { display: inline-block; min-width: 1.6em; }

.legend-note { color: var(--muted); }

/* Block variant of .tag.status-* — the modal's own status readout (see renderCard()'s statusBadgeHtml),
   sitting first in .dialog-body instead of as trailing text in the <h2>. Same color-per-status mapping,
   just full-width and bigger so it reads as its own element, not a small pill among other tags. */
.status-badge {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 13px;
  text-align: center;
}

.status-badge.status-open { background: var(--warn-soft); color: var(--warn); }
.status-badge.status-fixed { background: var(--ok-soft); color: var(--ok); }
.status-badge.status-refine,
.status-badge.status-queued { background: var(--warn-soft); color: var(--warn); }
.status-badge.status-falsified { background: var(--idea-soft); color: var(--idea); }
.status-badge.status-backlog,
.status-badge.status-ignored,
.status-badge.status-accepted { background: var(--panel-soft); color: var(--muted); }
.status-badge.status-none { background: var(--panel-soft); color: var(--muted); }

/* ---------- row-level quick-decision toolbar (sibling of .card-head, never nested inside it) ---------- */
.card-quick {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 6px 14px 10px;
  border-top: 1px dashed var(--border);
}

.row-badge {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--panel-soft);
  color: var(--muted);
  border: 1px solid var(--border);
  /* left-aligned, allowed to wrap to a second line (a combined "✏️ Änderung · 🔍 Erklärung" text can get
     long) rather than being squeezed/truncated against the action buttons on the right */
  white-space: normal;
  flex: 1 1 auto;
  min-width: 0;
}

.row-badge[data-value="fix"] { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.row-badge[data-value="refine"] { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.row-badge[data-value="accept"] { background: var(--info-soft); color: var(--info); border-color: transparent; }
.row-badge[data-value="ignore"] { background: var(--panel-soft); color: var(--muted); border-color: var(--border-strong); }

/* F-XX/2026-09-06: was a passive <span>, hidden unless a comment already existed — now an always-visible
   button so a comment can be added from here too, not only read; .is-active still marks "has a comment",
   same visual language as before. Opens the same detail dialog in a filtered "comment-only" view (see
   .detail-modal.comment-only below) instead of a second, separate dialog — avoids two independent comment
   textareas per code drifting out of sync.
   F-7/2026-09-27: now carries the shared `rf-btn` class in markup (report-render-core.js) instead of hand-
   duplicating its box model (min-width/padding/border-radius/background/font/cursor) — one source of that box
   for every sibling action button (.rf-btn/.rf-toggle), never three copies that can drift apart. Only the
   info accent color and the comment-count "(N)" face (same "(N)" convention as the modal's own comment label,
   `Kommentar${code.commentCount > 0 ? \` (${code.commentCount})\` : ''}`) are specific to this button. */
.row-comment-flag {
  border-color: var(--info);
  color: var(--info);
  opacity: .6;
}

.row-comment-flag:hover { opacity: .85; border-color: var(--info); }

.row-comment-flag.is-active { opacity: 1; background: var(--info-soft); }

/* Right-aligned group: the decision/reopen control plus the comment button sit together on the right;
   .row-badge (left, see above) takes the remaining space and wraps instead of being squeezed. */
.card-quick-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  flex-wrap: wrap;
}

.row-decision {
  display: flex;
  gap: 4px;
}

.rf-btn {
  min-width: 30px;
  padding: 4px 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}

.rf-btn:hover { border-color: var(--accent); }
/* F-29c/2026-09-21: the text on these solid fills is --accent-contrast (white in the light theme, near-black in the dark one) —
 * a literal #fff on the dark theme's light red/amber/grey fills read at 2-3:1. */
.rf-btn[aria-pressed="true"][data-action="fix"] { background: var(--danger); color: var(--accent-contrast); border-color: var(--danger); }
.rf-btn[aria-pressed="true"][data-action="refine"] { background: var(--warn); color: var(--accent-contrast); border-color: var(--warn); }
.rf-btn[aria-pressed="true"][data-action="accept"] { background: var(--info); color: var(--accent-contrast); border-color: var(--info); }
.rf-btn[aria-pressed="true"][data-action="ignore"] { background: var(--muted); color: var(--accent-contrast); border-color: var(--muted); }
.rf-btn[aria-pressed="true"][data-action="backlog"] { background: var(--idea); color: var(--accent-contrast); border-color: var(--idea); }
.rf-btn[aria-pressed="true"][data-action="done"] { background: var(--ok); color: var(--accent-contrast); border-color: var(--ok); }

.row-reopen-actions {
  display: flex;
  gap: 4px;
}

/* Real toggle buttons (aria-pressed), not checkboxes — same box as .rf-btn, one accent color per flag
   once active so "change/explain/reverify erwünscht" is a color+icon state, never a bare checked box. */
.rf-toggle[aria-pressed="true"][data-reopen-action="change"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.rf-toggle[aria-pressed="true"][data-reopen-action="explain"] { background: var(--info-soft); color: var(--info); border-color: var(--info); }
.rf-toggle[aria-pressed="true"][data-reopen-action="reverify"] { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }

/* two-column detail table (V8 report format) */
.detail-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin: 10px 0 0;
  font-size: 14px;
}

/* F-XX/2026-09-06: was a <caption> inside .detail-table — on the mobile @media rule further down that
   converts the table to display:block, the caption sat directly above the first stacked row with only
   this rule's 6px bottom padding between them, so on narrow screens it visually ran into the first
   row's <th> label with no separating border. A real heading element outside the table (see
   renderDetailTable()) can't collapse into the table's own layout regardless of viewport width. */
/* F-XX/2026-09-07: "auffälliger" + hr-Trennung — every sectional heading (Zusammenfassung/Details/
   Aktionen/Kommentar) gets its own top divider + a bolder, higher-contrast label, so the line sits
   ABOVE the heading (separating it from the previous block) instead of between the heading and its
   content — the previous placement had the divider on .decision-row/.reopen-actions, i.e. below
   "Aktionen"'s own heading, not above it. The very first heading in the modal (Zusammenfassung, right
   after the status badge) suppresses its own divider — see .dialog-body h3:first-of-type below. */
.detail-table-heading {
  margin: 20px 0 8px;
  padding-top: 14px;
  border-top: 2px solid var(--border-strong);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text);
  font-weight: 800;
}

/* SIMILAR-DIVIDER/2026-10-14: `> h3` on purpose — the "Ähnliche Findings" heading sits inside its own <section> and is the first
   h3 of that section, so the descendant form suppressed its divider line. */
.dialog-body > h3.detail-table-heading:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.detail-table th,
.detail-table td {
  text-align: left;
  vertical-align: top;
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
}

.detail-table tbody th {
  width: 27%;
  min-width: 128px;
  font-weight: 600;
  color: var(--muted);
  background: var(--panel-soft);
}

.detail-table tbody tr:last-child th,
.detail-table tbody tr:last-child td { border-bottom: 0; }

/* F-XX/2026-09-08: the Gesamtresultat heatmap reuses .detail-table but has the opposite column
   shape from the field-label/field-value table this class was designed for — th holds a prose-like
   area name (can be long) and td holds a short "🔴 3 · 🟡 5" badge string. The generic 27%/73% split
   squeezed area names into a narrow left column while the count column sat mostly empty on the
   right. Swap it: th grows to fit the label, td shrinks to its content. */
.heatmap-table tbody th {
  width: auto;
  min-width: 0;
}

.heatmap-table tbody td {
  width: 1%;
  white-space: nowrap;
}

.detail-table td > code { overflow-wrap: anywhere; }

/* Structured a)/b)/c) checklist for a "fixed" code (renderSubItems(), plan REPORT_SCHEMA_HARDENING_V2
   [D4]/[D7]) — sits directly under the "Teilpunkte" .detail-table-heading, so it reuses that table's
   bordered-box language instead of a bare bullet list. */
.subitem-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  font-size: 14px;
}

.subitem {
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
}

.subitem:last-child { border-bottom: 0; }

.subitem-open { background: var(--warn-soft); }
.subitem-deferred { background: var(--panel-soft); color: var(--muted); }
.subitem-deferred em { color: var(--muted); }

/* Triggered by the row-level 💬 button (renderQuickRow) as a lightweight "read/edit just the comment"
   shortcut — reuses the same dialog/textarea as the full details view (report-base.js openModal()) so
   there is only ever one comment-box per code, just hides everything else in .dialog-body for this view. */
.detail-modal.comment-only .body-summary,
.detail-modal.comment-only .detail-table-heading,
.detail-modal.comment-only .detail-table,
.detail-modal.comment-only .subitem-list,
.detail-modal.comment-only .related-codes,
.detail-modal.comment-only .decision-row,
.detail-modal.comment-only .reopen-actions,
.detail-modal.comment-only .refine-questions { display: none; }

/* ---------- decision controls (inside the detail modal) ---------- */
/* F-XX/2026-09-07: divider moved to .detail-table-heading (the "Aktionen" heading directly above this
   row) — no border here anymore, or the line would sit BETWEEN the heading and the buttons instead of
   above the heading itself. */
.decision-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* full-width comment field, always below the decision row — allowed on every code, incl. already-closed ones */
.comment-box {
  display: block;
  width: 100%;
  min-height: 64px;
  margin-top: 10px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  resize: vertical;
}

/* I-live/2026-09-14: mandatory-comment modal for the "Änderung erwünscht" reopen-request
 * (remote-app.js openChangeRequestModal()) — a plain <dialog>, so it inherits the generic
 * dialog/dialog[open] positioning+backdrop rules above; this only adds the internal padding those
 * generic rules deliberately leave at 0 for dialogs that build their own head/body structure. */
.change-request-modal { padding: 18px; }

.change-request-modal h2 { margin: 0 0 4px; font-size: 16px; }

.change-request-modal .hint { margin: 0 0 4px; }

.change-request-modal .change-request-textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 84px;
  margin-top: 8px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  resize: vertical;
}

.change-request-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}

/* Same visual language as .detail-table-heading (2026-09-07 user request: every modal block —
   Zusammenfassung/Details/Aktionen/Kommentar — gets an identical, more prominent bold section label
   with its own divider above it, not below). */
.comment-box-label {
  display: block;
  margin: 20px 0 8px;
  padding-top: 14px;
  border-top: 2px solid var(--border-strong);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text);
  font-weight: 800;
}

/* ---------- interactive refine Q&A (embedded in the detail modal, plan [D12]) ---------- */
.refine-questions {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed var(--border);
}

.refine-questions h3 { margin: 0 0 10px; font-size: 14px; }

/* F-33/2026-09-08: <div>/<div> instead of <fieldset>/<legend> — see renderRefineQuestion()'s comment for
   why (cross-browser fieldset/legend sizing quirks a reported "background box too short" bug traced to). */
.refine-question {
  margin: 0 0 14px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
}

.refine-question-legend {
  margin: 0 0 8px;
  font-size: 13px;
}

.refine-explanation { margin: 4px 0 8px; color: var(--muted); font-size: 12.5px; }

.refine-answered-hint { color: var(--ok); font-size: 12px; font-weight: 600; }

.refine-skipped-hint { color: var(--muted); font-size: 12px; font-weight: 600; }

/* F-08/2026-10-13: a question the agent withdrew — struck through (the <s> in the legend), dimmed, not answerable. */
.refine-withdrawn-hint { color: var(--muted); font-size: 12px; font-weight: 600; }

.refine-question.is-withdrawn .refine-option,
.refine-question.is-withdrawn .refine-explanation { opacity: 0.55; text-decoration: line-through; }

/* F-09/2026-10-13: shown instead of the hidden "done" button while a sub-item is still open (R4d). */
.subitems-open-hint { margin: 4px 0 0; }

.refine-option {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 4px 0;
  font-size: 13px;
  cursor: pointer;
}

.refine-option input { margin-top: 3px; flex-shrink: 0; }

/* Free text is its own selectable option (radio/checkbox, same group as the others) — a bigger,
   resizable textarea replaces the old single-line input that truncated a longer answer. */
.refine-freetext {
  flex: 1 1 auto;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  min-height: 64px;
  resize: vertical;
}

.refine-skip-option { color: var(--muted); font-style: italic; }

/* closed codes (behoben/ignoriert/akzeptiert) get three independent reopen-style toggles instead of the fix/refine/accept/ignore select — a row of full-width-clickable pill toggles, one per requestable action.
   F-XX/2026-09-07: no border here anymore — the divider lives on the "Aktionen" .detail-table-heading above this row (see there).
   F-33/2026-09-08: margin-top added — .decision-row above carries no bottom margin of its own, so
   without this the Fix/Refine/Accept/Ignore row and this chip row touched with zero gap between them. */
.reopen-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

/* Real toggle button (aria-pressed), not a checkbox — the whole pill is the click target. */
.chip-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-strong);
  background: var(--panel);
  color: var(--text);
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
  font: inherit;
  font-size: 13.5px;
}

.chip-toggle:hover { border-color: var(--accent); }

.chip-toggle[aria-pressed="true"][data-reopen-action="change"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.chip-toggle[aria-pressed="true"][data-reopen-action="explain"] { background: var(--info-soft); color: var(--info); border-color: var(--info); }
.chip-toggle[aria-pressed="true"][data-reopen-action="reverify"] { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }

/* E-05/F-06/2026-09-24 (workflow review): the `.card.is-changed`/`.is-explain-requested`/`.is-reverify-requested`/
   `.is-false-positive-flagged` glyphs are gone — they marked the local chat-copy wish flags, which no longer exist. A
   server-side wish shows as its state tag in the card headline. */

.decision-row .label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

.decision-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

.dbtn {
  padding: 10px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  text-transform: capitalize;
}

.dbtn:hover { border-color: var(--accent); }
.dbtn[aria-pressed="true"].val-fix { background-color: var(--danger); color: var(--accent-contrast); border-color: var(--danger); }
.dbtn[aria-pressed="true"].val-refine { background-color: var(--warn); color: var(--accent-contrast); border-color: var(--warn); }
.dbtn[aria-pressed="true"].val-accept { background-color: var(--info); color: var(--accent-contrast); border-color: var(--info); }
.dbtn[aria-pressed="true"].val-ignore { background-color: var(--muted); color: var(--accent-contrast); border-color: var(--muted); }
.dbtn[aria-pressed="true"].val-backlog { background-color: var(--idea); color: var(--accent-contrast); border-color: var(--idea); }
.dbtn[aria-pressed="true"].val-done { background-color: var(--ok); color: var(--accent-contrast); border-color: var(--ok); }

/* F-71/2026-09-21: the mark for "a decision is recorded on this card" is a plain check, not a dot — the filled circles belong
 * to the severity vocabulary alone ("circles = severity only"). The unused `.decided-dot` element rule went with it: nothing
 * ever rendered that element. */
.card.is-decided .card-key::after {
  content: "✓";
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  margin-left: 6px;
  vertical-align: middle;
}

/* ---------- prose sections rendered from markdown ---------- */
.prose-section {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 4px clamp(14px, 2.4vw, 22px) 18px;
  margin-top: var(--gap);
  scroll-margin-top: 160px;
  overflow-wrap: break-word;
}

.prose-section h2 { font-size: 17px; margin: 18px 0 10px; }

.prose-section h3 { font-size: 15px; margin: 16px 0 8px; }

.prose-section p { margin: 10px 0; }

.prose-section ul,
.prose-section ol { margin: 10px 0; padding-left: 22px; }

.prose-section li { margin: 4px 0; }

.prose-section blockquote {
  margin: 12px 0;
  padding: 8px 14px;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.prose-section blockquote p { margin: 4px 0; }

.prose-section pre {
  background: var(--panel-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  overflow-x: auto;
  font-size: 13px;
}

.prose-section pre code { background: none; border: 0; padding: 0; }

.prose-section hr { border: 0; border-top: 1px solid var(--border); margin: 18px 0; }

/* condensed sections (Audit-History by default) — collapsed on load, full content still in the DOM for search/print */
.section-collapse { margin: 8px 0 6px; }

.section-collapse > summary {
  cursor: pointer;
  color: var(--accent);
  font-weight: 600;
  list-style: none;
}

.section-collapse > summary::-webkit-details-marker { display: none; }

.section-collapse > summary::before { content: "▶ "; display: inline-block; transition: transform .15s ease; }

.section-collapse[open] > summary::before { transform: rotate(90deg); }

.section-collapse > summary .muted-count { color: var(--muted); font-weight: 400; margin-left: 6px; }

/* every wide block scrolls inside its own container — the page body never scrolls sideways */
.table-scroll { overflow-x: auto; margin: 12px 0; }

.table-scroll table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  min-width: 420px;
}

.table-scroll th,
.table-scroll td {
  text-align: left;
  vertical-align: top;
  padding: 8px 11px;
  border-bottom: 1px solid var(--border);
}

.table-scroll thead th {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

.table-scroll tbody tr:last-child td { border-bottom: 0; }

/* Generic click-to-sort + heatmap cell coloring for every markdown table (E-02/2026-09-16) — not
   specific to any one report; report-base.js attaches the sort behavior to every `.sortable-table`
   at load time, and these classes are applied server-side (report-render-core.js renderTable()) from
   a small closed dictionary of exact-match status tokens (PASS/FAIL/SKIP/NOT_RUN) and percentages. */
.sortable-table thead th {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 20px;
}

.sortable-table thead th:hover { color: var(--text); }

.sortable-table thead th[aria-sort="ascending"],
.sortable-table thead th[aria-sort="descending"] { color: var(--text); }

.sortable-table thead th[aria-sort="ascending"]::after,
.sortable-table thead th[aria-sort="descending"]::after {
  position: absolute;
  right: 6px;
  font-size: 10px;
}

.sortable-table thead th[aria-sort="ascending"]::after { content: "▲"; }
.sortable-table thead th[aria-sort="descending"]::after { content: "▼"; }

.cell-heat-pass { background: var(--ok-soft); color: var(--ok); font-weight: 600; }
.cell-heat-pass-partial { background: var(--warn-soft); color: var(--warn); font-weight: 600; }
.cell-heat-fail { background: var(--danger-soft); color: var(--danger); font-weight: 600; }
.cell-heat-skip { background: var(--warn-soft); color: var(--warn); }
/* F-18/2026-11-03: TEST-DEVIATION (SKILLS F-108) - orange like the E2E dashboard's heat-deviation; classifyCell() returns this class. */
.cell-heat-deviation { background: var(--high-soft); color: var(--high); font-weight: 600; }
.cell-heat-neutral { color: var(--muted); }

/* E-05/2026-09-24 (workflow review): the "Antwort für den Chat" panel (.response-panel, presets, dropdown, live decisions,
   response textarea) is gone, and its rules with it. */
/* Read-only comment block of the standalone view (report-render-core.js renderCommentView()). */
.comment-view {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.comment-view li {
  font-size: 13px;
  padding: 6px 10px;
  background: var(--panel-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* COMMENTS/2026-10-03: the per-code comment list (report-render-core.js commentItemHtml()) — author badge + date above the text, an agent
   comment tinted apart from the user's own; the edit/delete buttons exist only in the remote app. */
.comment-item { display: flex; flex-direction: column; gap: 2px; }
.comment-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; font-size: 11.5px; color: var(--muted); }
.comment-author { font-weight: 600; color: var(--text); }
.comment-by-agent { background: var(--info-soft, var(--panel-soft)); }
.comment-edited { font-style: italic; }
.comment-actions { margin-left: auto; display: inline-flex; gap: 2px; }
.comment-act { background: none; border: 0; cursor: pointer; padding: 2px 4px; font-size: 12px; opacity: 0.7; }
.comment-act:hover, .comment-act:focus-visible { opacity: 1; }
.comment-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.comment-editor { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.comment-edit-box { width: 100%; min-height: 4.5em; }
.comment-add-btn { margin-top: 6px; }

/* COMMENT-COMPOSER/2026-10-14: an empty new-comment box keeps its button greyed out (remote-app.js enables it on input). */
.comment-add-btn:disabled { opacity: .4; cursor: not-allowed; }


.hint { color: var(--muted); font-size: 12.5px; margin: 6px 0 0; }

.empty-state {
  background: var(--panel);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 24px;
  text-align: center;
  color: var(--muted);
  display: none;
}

.empty-state.is-visible { display: block; }

/* ---------- toast + help dialog ---------- */
/* Redesign 2026-09-14: was an always-dark, fully-rounded (999px) pill with no context colour, no icon
 * and no visible auto-dismiss cue. Now: a context-coloured card (success/error/offline — see the
 * .toast-{type} variants below; the default/untyped shape is unchanged neutral panel styling, for the
 * many call sites that don't pass a type and already carry their own inline emoji) with a much smaller
 * radius, a dedicated icon slot, and a shrinking bottom bar that visibly counts down to the moment the
 * toast auto-hides — set via toast()/showToast() writing the exact dismiss duration into
 * `--toast-duration` so the bar's animation always matches the real timer, never an approximation. */
/* F-04/2026-09-20: the element is `popover="manual"` (report-template.html) so report-base.js can lift it into
 * the top layer above an open modal <dialog>. The UA stylesheet gives every [popover] `inset: 0`, `margin: auto`,
 * `width/height: fit-content` and hides it (`display: none`) while it is not showing; the reset lines below
 * (top/margin/width/height, and the author `display: flex` that already beats the UA `display: none`) keep the
 * toast looking and animating exactly as the plain fixed element did — with or without the popover being shown. */
.toast {
  position: fixed;
  top: auto;
  left: 50%;
  right: auto;
  bottom: 24px;
  width: auto;
  height: auto;
  margin: 0;
  transform: translate(-50%, 12px);
  display: flex;
  align-items: center;
  gap: 9px;
  max-width: calc(100vw - 32px);
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  padding: 11px 16px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s, transform .18s;
  z-index: 60;
  overflow: hidden;
  box-shadow: var(--shadow);
}

.toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

.toast-icon { flex: none; font-size: 1.05em; line-height: 1; }

.toast-text { min-width: 0; overflow-wrap: break-word; }

/* Context variants — success/error/offline only; the untyped default above already covers the
 * neutral/info case, so there's no separate .toast-info rule to keep in sync with it. */
.toast.toast-success { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
.toast.toast-error { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.toast.toast-offline { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }

/* Auto-dismiss progress bar — `currentColor` so it always matches whichever context colour (or the
 * neutral default's --text) is active, no separate colour table to maintain. Anchored to the toast's
 * own bottom edge, inside the rounded corners via the parent's `overflow: hidden`. */
.toast::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: currentColor;
  opacity: .4;
  transform: scaleX(1);
  transform-origin: left;
}

.toast.is-visible::after {
  transition: transform var(--toast-duration, 1.9s) linear;
  transform: scaleX(0);
}

@media (prefers-reduced-motion: reduce) {
  .toast.is-visible::after { transition: none; transform: scaleX(0); }
}

/* Full-width on mobile (2026-09-14 user request) — a centred auto-width pill left large touch margins
 * on either side doing nothing; anchoring both edges with a fixed side gutter reads as a proper mobile
 * notification instead of a floating capsule. */
@media (max-width: 640px) {
  .toast {
    left: 16px;
    right: 16px;
    max-width: none;
    transform: translateY(12px);
  }

  .toast.is-visible { transform: translateY(0); }
}

/* Anchored to the top (2026-09-07 user request) instead of the UA default vertical-center — centering
   fights the mobile on-screen keyboard (which shrinks the visual viewport and re-centers the dialog
   mid-typing, e.g. while focused in .comment-box) and forces the user to scroll to reach the top of a
   tall dialog they can already see the top edge of. `bottom: auto` (dropping the UA's other inset edge)
   is required — with both top and bottom set, `margin: 0 auto` re-centers vertically in the space
   between them instead of pinning to the top. */
dialog[open] {
  position: fixed;
  top: 16px;
  bottom: auto;
  left: 0;
  right: 0;
  margin: 0 auto;
  max-height: calc(100vh - 32px);
  /* F-33/2026-09-08: 100vh on mobile Safari/Chrome is the *layout* viewport (browser chrome collapsed),
     not what's actually visible when the address bar is showing — the dialog could size itself taller
     than the real visible area, leaving a bottom button (e.g. "Antworten senden") below the fold and
     effectively unreachable. 100dvh tracks the real, current visual viewport; the 100vh rule above stays
     as the fallback for browsers that don't support dvh yet — this line simply wins where it's supported. */
  max-height: calc(100dvh - 32px);
  overflow: auto;
}

dialog {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0;
  max-width: 460px;
  width: min(92vw, 460px);
  color: var(--text);
  background: var(--panel);
  box-shadow: var(--shadow);
}

dialog::backdrop { background: rgba(4, 6, 10, .72); }

/* FINDING-MODAL-BACKDROP/2026-10-14 (user request): the page behind a finding dialog must not show through. */
dialog.detail-modal::backdrop { background: rgba(4, 6, 10, .96); }

.dialog-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}

.dialog-head h2 { margin: 0; font-size: 15px; }

.dialog-head-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* FINDING-MODAL-HEADER/2026-10-14 (user request): the status/category/origin badges in the modal header are small. */
.dialog-head-title .tag { font-size: 11px; padding: 0 7px; line-height: 1.5; }


.dialog-body { padding: 14px 18px 18px; }

.dialog-body table { width: 100%; border-collapse: collapse; font-size: 13.5px; }

.dialog-body td { padding: 5px 0; vertical-align: top; }

.dialog-body td:first-child { width: 92px; }

/* ---------- per-code detail modal (replaces the old inline accordion) ---------- */
.detail-modal {
  max-width: 760px;
  width: min(94vw, 760px);
  /* max-height/overflow now come from the top-anchored dialog[open] rule above (higher specificity than
     this class alone would win the property anyway — dropped here to avoid two conflicting values) */
}

.detail-modal .dialog-head { position: sticky; top: 0; background: var(--panel); z-index: 1; }

.detail-modal .detail-table th,
.detail-modal .detail-table td { padding: 12px 14px; }

.detail-modal .dialog-body { padding: 6px clamp(14px, 2.4vw, 22px) 20px; }

.detail-modal .body-summary { margin: 4px 0 12px; }

/* Pinned Beispiel / Wenn nicht umgesetzt block (2026-10-03) — sits between Zusammenfassung and Details. */
.pinned-example {
  margin: 12px 0;
  padding: 10px 14px;
  border: 1px solid var(--border-strong);
  border-left: 4px solid var(--accent);
  border-radius: 8px;
  background: var(--accent-soft);
}

.pinned-example-item + .pinned-example-item { margin-top: 10px; }

.pinned-example-label {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .07em;
  font-weight: 800;
  color: var(--text);
  margin-bottom: 4px;
}

/* ---------- decision wizard (plan REPORT_APP_FILTERS_AND_WIZARD) ---------- */

.wizard-start-dialog { max-width: 420px; width: min(92vw, 420px); }

.wizard-start-dialog .hint { margin: 0 0 12px; }

/* F-XX/2026-09-08: .dialog-body has no gap/flex of its own, so the two .toolbar-row blocks (type
   chips, severity chips) sat flush against each other with zero space between — only the hint above
   and the button below had their own margin. */
.wizard-start-dialog .toolbar-row + .toolbar-row { margin-top: 12px; }

.wizard-start-dialog .btn-primary { margin-top: 12px; }

/* Appended as a direct child of the currently open <dialog class="detail-modal"> (report-base.js
   wizardOpenStep()), sticking to the BOTTOM of that dialog's own scroll container — the same mechanism
   .detail-modal .dialog-head already uses at the top (position: sticky within a scrolling ancestor, not
   the viewport), so the wizard's navigation/comment stay reachable without scrolling past the rest of
   the finding's detail content. */
.wizard-bar {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin-top: 8px;
  padding: 6px clamp(10px, 2.4vw, 22px) 8px;
  background: var(--panel);
  border-top: 2px solid var(--border-strong);
  box-shadow: 0 -4px 12px rgba(0, 0, 0, .25);
}

/* WIZARD-REDESIGN/2026-10-04 (user request "kompakter"): row 1 = back, progress, next, exit; row 2 = the lifted decision and
   wish buttons; row 3 = the comment. Everything is denser than the dialog body (smaller buttons, one-line comment field). */
.wizard-bar-top {
  display: flex;
  align-items: center;
  gap: 6px;
}

.wizard-bar-progress {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  font-size: 11.5px;
  line-height: 1.15;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.wizard-bar .wizard-nav,
.wizard-bar .wizard-bar-exit {
  flex-shrink: 0;
  width: auto;
  padding: 3px 10px;
  font-size: 12.5px;
  white-space: nowrap;
}

/* The lifted .action-block: no section heading or divider in the bar, buttons in a tight wrapping row. */
.wizard-bar-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  margin-top: 6px;
}

.wizard-bar-actions .action-block { display: contents; }

.wizard-bar-actions .detail-table-heading { display: none; }

/* DECIDE-ROW-ONE-LINE/2026-10-14 (user report): the six decision buttons never wrap — one fixed six-column row, full bar width.
   Labels shrink (ellipsis) instead of pushing "Done" onto a second line on a 360px phone. */
.wizard-bar-actions .decision-row {
  flex: 1 1 100%;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 4px;
}

.wizard-bar-actions .dbtn {
  min-width: 0;
  padding: 4px 1px;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wizard-bar-actions .reopen-actions {
  flex: 0 1 auto;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 0;
}

.wizard-bar-actions .reopen-actions .chip-toggle { width: auto; padding: 3px 9px; font-size: 12px; gap: 4px; }

/* Inline reason for "Änderung erwünscht" inside the bar (remote-app.js openChangeRequestInline()) — replaces the modal over the modal. */
.wizard-reason { margin-top: 6px; padding: 6px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--panel-soft); }

.wizard-reason .hint { margin: 0 0 4px; font-size: 12px; }

.wizard-reason textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 52px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  resize: vertical;
}

.wizard-reason-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 6px; }

.wizard-reason-actions .btn { padding: 3px 10px; font-size: 12.5px; }

/* While the wizard is open the bar holds the decision and wish buttons; the list rows do not show their quick copies. */
.wizard-open .card-quick .row-decision,
.wizard-open .card-quick .row-reopen-actions { display: none; }

/* DECIDE-HEADER-SCROLL/2026-10-14 (user report): while the wizard walks a finding, the dialog header scrolls away with the
   content and only the decision bar (.wizard-bar, sticky at the bottom) stays. The header loses its divider line and gets a
   tighter scale so more of the finding fits on a phone. Scoped to .wizard-open — a normal detail dialog keeps its look. */
.wizard-open .detail-modal .dialog-head {
  position: static;
  padding: 8px 12px 4px;
  border-bottom: 0;
}

.wizard-open .detail-modal .dialog-head-title { gap: 4px 6px; }

.wizard-open .detail-modal .dialog-head h2 { font-size: 13.5px; line-height: 1.25; }


.wizard-open .detail-modal .dialog-body { padding: 4px clamp(10px, 2.4vw, 22px) 8px; }

.wizard-open .detail-modal .dialog-body table { font-size: 12.5px; }

.wizard-open .detail-modal .dialog-body td { padding: 3px 0; }

.wizard-open .detail-modal .detail-table th,
.wizard-open .detail-modal .detail-table td { padding: 5px 6px; }

.wizard-open .detail-modal .body-summary { margin: 2px 0 6px; font-size: 13px; line-height: 1.4; }

.wizard-open .detail-modal .detail-table-heading { margin: 8px 0 4px; }

/* DECIDE-FULLSCREEN/2026-10-14 (user report): on the mobile layout only (<= 720px, same breakpoint as the rest of the mobile rules) the wizard's finding fills the whole viewport instead of a floating card with
   margins, so the body gets all the room. The dialog keeps its own scroll (overflow: auto from dialog[open]); the decision bar
   stays sticky at its bottom. */
@media (max-width: 720px) {
  .wizard-open dialog.detail-modal[open] {
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100vw;
    max-width: none;
    height: 100vh;
    height: 100dvh;
    max-height: none;
    margin: 0;
    border: 0;
    border-radius: 0;
  }
}

/* Refine autosave status line (remote-app.js setRefineSaveStatus()). */
.refine-save-status { margin: 6px 0 0; font-size: 12.5px; color: var(--muted); }
.refine-save-status[data-state="saving"] { color: var(--info); }
.refine-save-status[data-state="saved"] { color: var(--ok, var(--muted)); }
.refine-save-status[data-state="queued"] { color: var(--warn); }
.refine-save-status[data-state="error"] { color: var(--danger); font-weight: 600; }

/* ---------- responsive ---------- */
@media (max-width: 1080px) {
  .layout { grid-template-columns: minmax(0, 1fr); }

  .side-nav {
    position: static;
    max-height: none;
    order: -1;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
  }

  .side-nav[data-collapsed="true"] .nav-body { display: none; }

  .nav-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    padding: 0;
  }

  .side-nav h2 { margin-bottom: 0; }

  .nav-body { margin-top: 10px; }
}

@media (max-width: 720px) {
  body { font-size: 14.5px; }

  .toolbar { padding: 8px 10px; }

  .card-head { padding: 11px 12px; }


  .detail-table,
  .detail-table tbody,
  .detail-table tr,
  .detail-table th,
  .detail-table td { display: block; width: 100%; }

  .detail-table tr { border-bottom: 1px solid var(--border); }

  .detail-table tr:last-child { border-bottom: 0; }

  /* F-XX/2026-09-15: the plain `.detail-table tbody th/td` selectors below lose every padding property to
     `.detail-modal .detail-table th/td { padding: 12px 14px; }` (report-base.css, ~line 1853) — that rule
     has higher specificity (two classes) than these single-class-plus-element mobile selectors, so inside
     a modal (the by far most common place this table renders) the intended tight mobile spacing was
     silently overridden back to the full 12px desktop gap on every side. The `.detail-modal`-prefixed
     duplicates match that specificity (two classes) plus the extra `tbody` element, so they win instead —
     this is the actual "Headline zu viel Abstand zum Text" fix, not a duplicate of the padding-bottom/
     padding-top declarations above. The un-prefixed selectors stay for `.detail-table` usage outside a
     modal (e.g. the Gesamtresultat heatmap), where no competing rule exists and they already worked
     correctly. `padding-top` on th is intentionally NOT zeroed (unlike a first pass that set it to 0,
     corrected the same day after user feedback "jetzt fehlt padding... sieht unsauber aus") — a label
     sitting flush against the row divider above it looked cramped; 8px keeps that breathing room while
     `padding-bottom: 2px` still keeps the label close to its own value directly below. */
  .detail-table tbody th,
  .detail-modal .detail-table tbody th {
    border-bottom: 0;
    padding-top: 8px;
    padding-bottom: 2px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    background: transparent;
    min-width: 0;
    /* F-14/2026-09-16: same specificity trap as the padding fix in this rule's own comment above, just a
       different property — the always-active `.detail-table tbody th { width: 27%; }` (desktop 2-column
       layout, ~line 1180) has higher specificity than the generic mobile stacking rule's `width: 100%`
       (`.detail-table th` branch further up, one class + one element), so without this the label stayed
       27%-wide and left-aligned on mobile while its value below it correctly went full width. */
    width: 100%;
  }

  .detail-table tbody td,
  .detail-modal .detail-table tbody td { padding-top: 0; border-bottom: 0; }

  /* .heatmap-table tbody th/td (higher specificity than the generic rules above) would otherwise
     leak its desktop-only width:auto/1% through the cascade here and break the stacked mobile
     layout — reset explicitly to the same full-width stacking as every other .detail-table. */
  .heatmap-table tbody th,
  .heatmap-table tbody td {
    width: 100%;
    white-space: normal;
  }

  .decision-row { grid-template-columns: repeat(2, 1fr); gap: 6px; }

  /* Mobile button-width audit (2026-09-14 user request): this row holds buttons with long,
   * uneven-length German labels ("Erklärung erwünscht", "Verifikation erwünscht", …) that
   * were never going to fit two-per-row on a phone the way the short single-word decision buttons
   * above do — flex-wrap just left a ragged, unpredictable mix of 1-and-2-per-row groupings depending
   * on each label's length. Stacked full-width blocks read as one clean, predictable column instead,
   * same gap as their desktop row (flex-direction switch only, no new spacing scale introduced). The
   * short-single-word decision-row (.dbtn) and the exactly-2-item wizard-bar-actions row above are
   * deliberately excluded — both already have their own clean, previously-requested layouts (2-col
   * grid / 50-50 split) that already avoid uneven wrapping without needing to go full-width. */
  .reopen-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .reopen-actions .chip-toggle {
    width: 100%;
    box-sizing: border-box;
    justify-content: center;
  }
}

/* F-29/F-70/2026-09-20: tap targets. Measured at 390 px: .rf-btn/.rf-toggle/.row-comment-flag 29 px high,
 * .btn/.btn-icon 31, .chip 37, .copy-title-btn 19, .nav-toggle 21 — below the 40 px this workspace asks for
 * (platform guidance: 44), and the four buttons that WRITE a status sat 4 px apart. Touch-first pointers only
 * (`pointer: coarse`), so the dense desktop layout is untouched; the row buttons grow to a 40 px square with a
 * wider gap, the rest get a 40 px minimum height. The copy-title icon takes negative vertical margins so its
 * larger box does not inflate the heading's line. */
@media (pointer: coarse) {
  .chip, .btn, .btn-icon, .nav-toggle, .filter-level-reset, .refine-option { min-height: 40px; }
  .rf-btn, .rf-toggle, .row-comment-flag { min-width: 40px; min-height: 40px; }
  .row-decision { gap: 8px; }
  .copy-title-btn { min-width: 40px; min-height: 40px; margin-top: -10px; margin-bottom: -10px; }
  .summary-toggle { min-height: 40px; display: inline-flex; align-items: center; }
  .done-fold > summary { min-height: 40px; box-sizing: border-box; padding-top: 10px; padding-bottom: 10px; }
  .side-nav a { padding-top: 10px; padding-bottom: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

@media print {
  .toolbar, .side-nav, .floating-actions, .toast, .empty-state, .card-quick { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .layout { display: block; padding: 0; }
  .card { break-inside: avoid; box-shadow: none; border-color: #999; }
  .prose-section { box-shadow: none; border-color: #999; break-inside: avoid; }
  .prose-section[data-collapsible] > summary { display: none; }
  a { color: #000; text-decoration: underline; }
  /* detail modals are normally only reachable via showModal(); force them fully visible in print output */
  dialog.detail-modal {
    display: block !important;
    position: static !important;
    max-width: none;
    width: 100%;
    max-height: none;
    overflow: visible;
    border: 1px solid #999;
    box-shadow: none;
    break-inside: avoid;
    margin: 0 0 14px;
  }
  dialog.detail-modal::backdrop { display: none; }
}

/* F-21/2026-09-08: status badges carry their icon next to the label (card tag and modal header).
   Kept at the badge's own font size and given a hair of optical separation — an emoji glyph sits
   slightly differently on the baseline than the text beside it. aria-hidden on the span keeps it
   out of the accessible name, which already reads the label. */
.tag .status-icon,
.status-badge .status-icon {
  font-style: normal;
  line-height: 1;
  margin-right: 1px;
}

/* ------------------------------------------------------------------------------------------------
   REPORT-UX/2026-10-13 — layout polish from the user's review round. One block so the look is easy to
   find and to keep consistent: count pills, the Codes-gesamt tile, group separators, the grouped progress
   counters, the Gesamtresultat tiles and area bars, filter spacing, one collapse look, the wizard bar line.
   ------------------------------------------------------------------------------------------------ */

/* Collapses: one look for every folding <details> — the filter panel, the symbol legend, the "Fertig" fold and Scope & Methodik.
   A bordered bar with the chevron on the left, the same in the report and in the list. */
.filter-panel > summary,
.symbol-legend > summary,
.done-fold > summary,
.head-details > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  box-sizing: border-box;
  min-height: 40px;
  margin: 0 0 10px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.filter-panel > summary::-webkit-details-marker,
.symbol-legend > summary::-webkit-details-marker,
.done-fold > summary::-webkit-details-marker,
.head-details > summary::-webkit-details-marker { display: none; }

.filter-panel > summary::before,
.symbol-legend > summary::before,
.done-fold > summary::before,
.head-details > summary::before { content: "▸"; color: var(--muted); }

.filter-panel[open] > summary::before,
.symbol-legend[open] > summary::before,
.done-fold[open] > summary::before,
.head-details[open] > summary::before { content: "▾"; }

.filter-panel > summary:hover,
.symbol-legend > summary:hover,
.done-fold > summary:hover,
.head-details > summary:hover { border-color: var(--border-strong); }

.filter-panel > summary:focus-visible,
.symbol-legend > summary:focus-visible,
.done-fold > summary:focus-visible,
.head-details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Count pills next to Findings, Erweiterungen and Positive (and the per-status rows of the summary). */
.group-count {
  display: inline-block;
  min-width: 28px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
  text-align: center;
}


/* The line between the main sections (Findings, Erweiterungen, Positive) — a real break, not a faint rule.
   F-137/2026-10-20: drawn as the top border of a visible block that follows another visible block (the filter hides
   a block with `hidden`), so it never frames a single remaining block; it used to be a separate <hr> that the filter
   did not know. */
.group-block:not([hidden]) ~ .group-block:not([hidden]) {
  border-top: 2px solid var(--border);
  margin-top: 28px;
  padding-top: 24px;
}

/* Pending, In Arbeit and umgesetzt belong together: one group that reads as one progress. */
.progress-group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-soft);
}

/* Gesamtresultat: four totals as tiles (a fifth appears with the first finished F code, I3/2026-10-18), then one row per area with a bar and its counts.
   auto-fit keeps four tiles in one row of four and lets five share one row of five; below the breakpoint it is two columns (see the media query). */
.gesamt-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
  margin: 8px 0 14px;
}

.gesamt-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
}

.gesamt-tile .gt-n { font-size: 22px; font-weight: 700; line-height: 1.2; }
.gesamt-tile .gt-label { color: var(--muted); font-size: 12px; }
.gesamt-tile.gt-you { border-left-color: var(--warn); }
.gesamt-tile.gt-agent { border-left-color: var(--pending); }
.gesamt-tile.gt-done { border-left-color: var(--ok); }
.gesamt-tile.gt-pos { border-left-color: var(--accent); }
.gesamt-tile.gt-falsified { border-left-color: var(--idea); }

.gr-areas { display: grid; gap: 10px; }

.gr-area {
  display: grid;
  grid-template-columns: minmax(110px, 1.2fr) minmax(90px, 2fr) auto;
  align-items: center;
  gap: 4px 12px;
}

.gr-name { font-weight: 600; overflow-wrap: anywhere; }

.gr-bar {
  display: block;
  height: 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-soft);
  overflow: hidden;
}

.gr-fill { display: block; height: 100%; background: var(--accent); }
.gr-area.is-done .gr-fill { background: var(--ok); }
.gr-area.is-done .gr-name { color: var(--muted); }
.gr-meta { color: var(--muted); font-size: 12.5px; white-space: nowrap; }

.gr-w-0 { width: 0; }
.gr-w-10 { width: 10%; }
.gr-w-20 { width: 20%; }
.gr-w-30 { width: 30%; }
.gr-w-40 { width: 40%; }
.gr-w-50 { width: 50%; }
.gr-w-60 { width: 60%; }
.gr-w-70 { width: 70%; }
.gr-w-80 { width: 80%; }
.gr-w-90 { width: 90%; }
.gr-w-100 { width: 100%; }

@media (max-width: 600px) {
  .gesamt-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* I3/2026-10-18: with five tiles the last one would sit alone in a half-width cell - let it span the row (an even count is untouched). */
  .gesamt-tile:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .gr-area { grid-template-columns: 1fr; }
  .gr-meta { white-space: normal; }
}

/* Filter panel: every block gets room of its own. */
.filter-panel > .filter-level + .filter-level,
.toolbar .filter-level + .filter-level { margin-top: 18px; }

.filter-panel { padding: 2px 0 4px; }

/* "Zuletzt bearbeitet" and the other instants: the zone name is short, the date never wraps inside itself. */
.head-meta .instant { white-space: nowrap; }

/* Decision wizard bar: the code, then "x / y", then the pill of collected decisions. */
.wizard-code { font-weight: 700; margin-right: 6px; }

.wizard-count {
  display: inline-block;
  padding: 1px 9px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 13px;
  font-weight: 700;
}

.wizard-buffered {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--ok-soft);
  color: var(--ok);
  font-size: 12px;
  font-weight: 600;
}

/* Stand presets: two per row, with room between them — the full-width stack read as squeezed. */
.filter-level-values { gap: 8px; }
.filter-level-values .chip-span2 { flex: 1 1 calc(50% - 6px); }

/* REPORT-UX/2026-10-13 (user request): no "Bei dir" / "Beim Agent" any more. The progress label is one piece: ⏳ X | ⚙️ A | ✅ D. */
.gesamt-tile.gt-pending { border-left-color: var(--pending); }
.gesamt-tile.gt-agent { border-left-color: var(--accent); }
.progress-group .pg-part { font-size: 13px; font-weight: 600; white-space: nowrap; }
.progress-group .pg-sep { color: var(--border-strong); }

/* REPORT-UX/2026-10-13 (user request): the Refine counter has no background pill; its symbol and text stay. */
.progress-badge[data-progress="refine"] { background: none; padding-left: 0; padding-right: 0; }


/* FILTER-UNIFIED/2026-10-14, FILTER-SELECT/2026-10-19: the unified filter (Typ / Entscheidung / Status / Kategorie / Bereich, all always
   visible) — report page and start page share this markup (report-render-core.js renderUnifiedFilter()). Nothing is selected at the start
   and nothing is highlighted; a selected value is the ordinary pressed chip (accent). The chips wrap: no inner scrollbar, no fold, no
   dimmed state. */
.ufilter .filter-level + .filter-level { margin-top: 14px; }
.ufilter-chip-row { padding: 2px; }
.ufilter-level-reset[hidden] { display: none; }
/* FILTER-ZERO/2026-10-19: a chip with count 0 (not selected) and a level without any visible chip are hidden, not greyed. */
.ufilter-chip[hidden], .ufilter-level[hidden] { display: none; }
.ufilter-note { margin: 0 0 8px; }
.filter-result-count { margin: 4px 0 8px; color: var(--muted); font-size: 12.5px; font-weight: 600; }
.filter-result-count[hidden] { display: none; }

/* REPORT-UX/2026-10-14 (user request): severity, Positive and Ideas share the progress label's shape; each part keeps its colour. */
.progress-group .pg-part.sev-krit { color: var(--danger); }
.progress-group .pg-part.sev-high { color: var(--high); }
.progress-group .pg-part.sev-warn { color: var(--warn); }
.progress-group .pg-part.sev-info { color: var(--info); }
.progress-group .pg-part.sev-ok, .progress-group .pg-part.pos-count { color: var(--ok); }
.progress-group .pg-part.sev-idea { color: var(--idea); }
/* REPORT-UX/2026-10-14: a part without data stays visible but muted. */
.progress-group .pg-part.is-empty { opacity: .45; }

/* REPORT-UX/2026-10-14 (user request, header redesign): a smaller title, then the same three combination badges as the start page's list row
   (Schwere, Entscheidung, Fortschritt) — one block, each badge a pill that never wraps inside; on a phone every badge takes the full card width. */
.summary-panel h2.head-title { font-size: 15px; font-weight: 650; line-height: 1.35; letter-spacing: .005em; overflow-wrap: anywhere; }
.head-badges { display: flex; flex-wrap: wrap; gap: 6px 8px; margin-top: 8px; }
.head-badges .progress-group { flex-wrap: nowrap; padding: 2px 10px; gap: 4px 8px; }
.head-badges .progress-group .pg-part { font-size: 12.5px; }
.summary-panel .head-meta { margin-top: 8px; font-size: 12px; }
@media (max-width: 720px) {
  .head-badges .progress-group { display: flex; flex: 1 1 100%; width: 100%; justify-content: space-between; padding: 3px 8px; gap: 3px; }
  .head-badges .progress-group .pg-part { font-size: 12px; }
}
@media (max-width: 380px) {
  .head-badges .progress-group .pg-part { font-size: 11.5px; }
}

/* REPORT-UX/2026-10-14 (user request, card layout): severity + code on the left, ONE Entscheidung | Progress pill on the right, the title below it,
   and a slightly smaller type scale for the title and the two text blocks (Problem/Idee on top, Lösung/Umsetzung/Begründung/Widerlegung below). */
.card-stand-group { padding: 1px 8px; gap: 4px 6px; flex-wrap: nowrap; }
.card-stand-group .pg-part { font-size: 11.5px; }
.card-key { font-size: 12px; }
.card-headline { font-size: 14px; line-height: 1.35; }
.card-summary, .card-solution { font-size: 12.5px; line-height: 1.5; }
.card-block-label, .card-solution-label { display: inline-block; margin-right: 6px; font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }


/* ------------------------------------------------------------------------------------------------
   TOOLBAR-CARDS/2026-11-03 (user request): the toolbar is no single frame with a fold any more but separate cards — search, filter
   (one block per category, each with its own "Ausblenden" button next to the heading) and the decision wizard. Report page and start
   page share this look.
   ------------------------------------------------------------------------------------------------ */
.toolbar.toolbar-cards {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  flex-wrap: nowrap;
  gap: 12px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

.toolbar-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-sizing: border-box;
  width: 100%;
  padding: 12px clamp(12px, 2.2vw, 16px);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.toolbar-card-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
}

.toolbar-card-hint { margin: 0; font-size: 13px; color: var(--muted); }

/* One category = one block: a divider above it, the same room above and below the content, the reset and the chevron in the heading row.
   The divider belongs to every block but the first one under the card title, so the lines between all categories look alike. */
.filter-card { gap: 0; }
.filter-card > .toolbar-card-title { padding-bottom: 12px; }
.filter-card > .btn-block { margin-top: 12px; }

.toolbar-card .filter-level,
.toolbar-card .sort-level {
  gap: 10px;
  padding: 12px 0;
  border-top: 1px solid var(--border);
}

.toolbar-card .toolbar-card-title + .filter-level { border-top: 0; padding-top: 0; }

.toolbar-card .filter-level + .filter-level,
.toolbar-card .ufilter .filter-level + .filter-level { margin-top: 0; }

/* The unified block is a wrapper: its levels carry the dividers, so the wrapper adds no gap of its own. */
.toolbar-card .ufilter { display: flex; flex-direction: column; gap: 0; }

.toolbar-card .filter-result-count { margin: 0; padding: 0 0 12px; }

/* Chevron-only fold button: just the icon, no box — no background on hover, focus or press (a tinted square around it read as heavy
   padding). The tap target is a transparent ::after that reaches past the icon, so the visible size stays small. */
.filter-level-fold {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin: 0;
  margin-left: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.filter-level-fold::after { content: ""; position: absolute; inset: -10px; }
.filter-level-fold .chevron { transition: transform .15s ease; }
.filter-level-fold[aria-expanded="false"] .chevron { transform: rotate(-90deg); }
.filter-level-fold:hover,
.filter-level-fold:active,
.filter-level-fold:focus { background: none; color: var(--accent); outline: none; }
.filter-level-fold:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* Folded: only the heading row stays; the chips, the date fields and the lock note are gone. */
.filter-level.is-folded > :not(.filter-level-head),
.sort-level.is-folded > :not(.filter-level-head) { display: none; }

/* Phone: the chips of a filter block stand under each other, each as wide as the card. */
@media (max-width: 720px) {
  .filter-level-fold::after { inset: -12px; }
  .toolbar-card .filter-level-values { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 8px; }
  .toolbar-card .filter-level-values .chip,
  .toolbar-card .filter-level-values .chip-span2 { display: flex; flex: 0 0 auto; width: 100%; box-sizing: border-box; align-items: center; justify-content: space-between; text-align: left; }
}

/* On the report page the unified block is the first thing under the card title: no divider above its first level. */
.toolbar-card .toolbar-card-title + .ufilter > .filter-level:first-child { border-top: 0; padding-top: 0; }
