/* ============================================================
   Soryvex — Security Report
   Loaded AFTER shared style.css. Dark on screen; light for print
   (§6.3 — light palette lives here inside @media print).
   ============================================================ */

.wrap { max-width: 880px; margin: 0 auto; padding: 40px 40px 64px; }
body { background: var(--bg); color: var(--text); }
code {
  font-family: var(--mono); font-size: 0.75rem;
  background: var(--bg-subtle); border: 1px solid var(--border);
  padding: 1px 5px; border-radius: var(--radius-sm); color: var(--text-secondary);
}

/* Top utility row */
.report-nav { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 20px; }
.report-nav .back { display: inline-flex; align-items: center; gap: 6px; font-size: 0.8125rem; color: var(--text-tertiary); }
.report-nav .back:hover { color: var(--text); }

/* Header */
.header { display: flex; justify-content: space-between; align-items: flex-end; padding-bottom: 20px; border-bottom: 2px solid var(--border-strong); margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }
.header .badge {
  display: inline-block; padding: 4px 14px; margin-bottom: 10px;
  background: var(--accent-muted); color: var(--accent);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  border-radius: var(--radius-full);
}
.header h1 { font-size: 1.875rem; font-weight: 700; color: var(--text); letter-spacing: -0.02em; line-height: 1.15; margin: 0; }
.header h1 span { color: var(--text-tertiary); font-weight: 400; font-size: 1rem; }
.header-meta { text-align: right; font-size: 0.75rem; color: var(--text-tertiary); }
.header-meta b { color: var(--text-secondary); font-weight: 600; }
.meta-line + .meta-line { margin-top: 8px; }
/* An assessed URL can be a deep route with a long query, so it must wrap
   rather than stretch the right-aligned metadata column. */
.target-url { word-break: break-all; overflow-wrap: anywhere; }
.pdf-export { display: flex; justify-content: flex-end; margin-top: 12px; }

/* Section headers */
h2.section {
  font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--accent); margin: 36px 0 14px; padding-bottom: 8px;
  border-bottom: 1px solid var(--border); position: relative;
}
h2.section::after { content: ""; position: absolute; bottom: -1px; left: 0; width: 56px; height: 2px; background: var(--accent); }

/* Score row */
.score-row { display: grid; grid-template-columns: auto 1fr; gap: 24px; align-items: center; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 22px 28px; margin: 12px 0 20px; }
.score-num { font-size: 2.75rem; font-weight: 800; color: var(--accent); line-height: 1; display: flex; flex-direction: column; align-items: center; }
.score-num small { font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-tertiary); margin-top: 4px; }
.verdict-badge { display: inline-block; padding: 6px 16px; border-radius: var(--radius-full); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--accent-ink); }
.v-critical { background: var(--sev-critical); }
.v-high { background: var(--sev-high); }
.v-medium { background: var(--sev-medium); }
.v-low { background: var(--sev-low); }
.v-info { background: var(--sev-info); }
.kpis { display: flex; flex-wrap: wrap; gap: 18px 26px; font-size: 0.8125rem; color: var(--text-secondary); margin-top: 12px; }
.kpis b { font-size: 1.125rem; font-weight: 800; display: block; line-height: 1.2; color: var(--text); }

/* One-line summary */
.summary-line { color: var(--text-secondary); margin: 0 0 14px; }
.summary-line strong { color: var(--text); }

/* Change since the previous assessment -------------------------------------
   Composed in web/router.py (_scan_diff_banner) and injected into the stored
   report, so these five classes had no rule anywhere in the product: the
   feature rendered as an unstyled bulleted list of raw check ids and target
   URLs above the report header — on the one page a customer is most likely to
   screenshot, forward or print. Styled here to read as a quiet change note
   rather than as the document's opening paragraph. */
.report-diff {
  /* Injected directly after <body>, so it is a sibling of .wrap rather than a
     child of it. It carries the report's own measure (880px box less .wrap's
     40px gutters = 800px) so its border lines up with the rule under the
     report header instead of spanning the full viewport and reading as a
     separate document sitting on top of the report. */
  max-width: 800px;
  margin: 40px auto 24px;
  padding: 16px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
}
.report-diff-head {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  margin: 0 0 10px;
}
.report-diff-head strong { color: var(--text); font-weight: 700; }
.report-diff-list {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.report-diff-list li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 0.75rem;
  color: var(--text-tertiary);
  min-width: 0;
}
/* The check id is an internal reference, not a headline: keep it monospaced
   but quiet so it does not out-shout the human-readable part. */
.report-diff-list .mono { color: var(--text-secondary); }
.report-diff-list .muted { word-break: break-all; overflow-wrap: anywhere; }
/* The finding's own title leads each line; the check id follows in the muted
   mono treatment. This is the sentence a reader can act on, so it carries the
   visual weight and the id is deliberately quiet. */
.report-diff-title {
  color: var(--text-secondary);
  font-weight: 500;
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* Cap the block so a large swing cannot bury the report itself. The router
   already truncates each list to 8 entries. */
.report-diff-new, .report-diff-fixed { padding-left: 0; }
.report-diff-trend {
  margin: 4px 0 0;
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
/* Attached to a fixed finding the customer had already accepted or marked
   resolved. It is the confirmation that the change actually took, so it is
   given the "verified control" green rather than the muted grey the rest of
   the banner uses — this is the one line a customer is waiting to read. */
.report-diff-confirmed { color: var(--sev-low); }
.report-diff .badge { flex: 0 0 auto; }

/* AI analyst */
.ai-review { border: 1px solid var(--border); border-left: 4px solid var(--accent); background: var(--surface); border-radius: var(--radius-sm); padding: 16px 20px; margin: 14px 0; }
.ai-review h3 { font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); margin-bottom: 8px; }
.ai-review p { font-size: 0.8125rem; color: var(--text-secondary); margin: 0; }
.ai-review .ai-review-body > p { margin: 0 0 8px; }
.ai-review .ai-review-body > p:last-child { margin-bottom: 0; }
.ai-review .ai-review-body h2, .ai-review .ai-review-body h3, .ai-review .ai-review-body h4 { font-size: 0.875rem; color: var(--accent); margin: 10px 0 4px; }
.ai-review .ai-review-body ul, .ai-review .ai-review-body ol { margin: 4px 0 8px 18px; padding: 0; font-size: 0.8125rem; color: var(--text-secondary); }
.ai-review .ai-review-body li { margin: 2px 0; }
.ai-review .ai-review-body code { font-size: 0.75rem; background: var(--bg); padding: 1px 4px; border-radius: 3px; color: var(--text); }
.ai-review .ai-review-body strong { color: var(--text); }
.ai-unavailable { color: var(--text-tertiary); font-style: italic; font-size: 0.8125rem; }

/* Simple Explanation cards -----------------------------------------------------
   Lived as an inline <style> block inside report.html with its own palette
   (#4FD1E8 / #E0A83E / #FF5470) — a third severity palette in a product that
   already has --sev-* tokens. Moved here and re-pointed at those tokens so a
   severity looks the same everywhere. */
.sx-se-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.sx-se-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--sev-low);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}
.sx-se-card.sev-info { border-left-color: var(--sev-info); }
.sx-se-card.sev-medium { border-left-color: var(--sev-medium); }
.sx-se-card.sev-high,
.sx-se-card.sev-critical { border-left-color: var(--sev-critical); }
.sx-se-top { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 6px; flex-wrap: wrap; }
.sx-se-id {
  font-family: var(--mono); font-size: 0.6875rem;
  color: var(--text-secondary); background: var(--bg-subtle);
  border: 1px solid var(--border); padding: 2px 7px; border-radius: var(--radius-sm);
}
.sx-se-sev {
  font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px var(--space-2); border-radius: var(--radius-full);
  color: var(--sev-low); background: var(--sev-low-bg);
}
.sx-se-sev.sev-info { color: var(--sev-info); background: var(--sev-info-bg); }
.sx-se-sev.sev-medium { color: var(--sev-medium); background: var(--sev-medium-bg); }
.sx-se-sev.sev-high,
.sx-se-sev.sev-critical { color: var(--sev-critical); background: var(--sev-critical-bg); }
.sx-se-title { font-size: 0.875rem; font-weight: 600; margin: 0 0 var(--space-1); }
.sx-se-body { font-size: 0.8125rem; line-height: 1.6; color: var(--text-secondary); margin: 0; }
.sx-se-body + .sx-se-body { margin-top: var(--space-1); }
.sx-se-body strong { color: var(--text); font-weight: 600; }

/* Category chips */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.chip { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 12px; font-size: 0.75rem; color: var(--text-tertiary); }
.chip b { color: var(--text-secondary); }

/* Severity pills — dark on screen. Print pairs defined at bottom. */
.sev { display: inline-block; padding: 2px 10px; border-radius: var(--radius-full); font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; }

/* Findings */
.finding { border: 1px solid var(--border); border-radius: var(--radius-sm); margin: 14px 0; overflow: hidden; background: var(--surface); }
.finding-head { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.finding-title { font-size: 0.9375rem; font-weight: 600; color: var(--text); flex: 1; min-width: 200px; }
.finding-title .count { color: var(--text-tertiary); font-size: 0.75rem; font-weight: 400; }
.finding-meta { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: 0.75rem; color: var(--text-tertiary); margin-top: 4px; }
.finding-meta b { color: var(--text-tertiary); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.6875rem; }
.finding-body { padding: 14px 18px; }
.finding-row { margin: 8px 0; font-size: 0.8125rem; color: var(--text-secondary); }
.finding-row b {
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-tertiary); display: block; margin-bottom: 3px;
}

/* Evidence */
.evidence-list { margin: 6px 0; }
.evidence-item {
  font-family: var(--mono); font-size: 0.75rem; color: var(--text-secondary);
  background: var(--bg-subtle); border: 1px solid var(--border);
  padding: 8px 12px; border-radius: var(--radius-sm); margin: 6px 0;
  overflow-wrap: anywhere;
}

/* Grouped rows */
.group { margin: 14px 0; }
.group .group-name { font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.04em; }
.rows { display: flex; flex-direction: column; }
.row { display: flex; gap: 10px; align-items: baseline; padding: 7px 4px; border-bottom: 1px solid var(--border); font-size: 0.8125rem; color: var(--text-secondary); }
/* A flex row's children default to min-width:auto, so the `<span>` beside a
   check id refuses to shrink below its longest word, and a word like
   "no_command_parameters" - 20 characters, no space - is wider than a 320px
   viewport. `min-width: 0` lets it shrink so `overflow-wrap` has somewhere to
   happen; without it the *document* scrolls sideways, not the row.

   Deliberately not applied to `th`/`td`: a table that has a scroll box should
   keep its own layout and pan, and force-breaking its cells wraps every column
   header and URL character by character. */
.row > * { min-width: 0; overflow-wrap: anywhere; }
.rows > .row:last-child { border-bottom: none; }
.row code { flex-shrink: 0; color: var(--text-tertiary); }
.row strong { color: var(--text); font-weight: 600; }
.row .desc { color: var(--text-tertiary); }

/* Tables */
/* Wrapper added by report.js so a wide findings table pans on its own instead
   of dragging the whole report sideways. The right padding keeps the last
   column off the scroll edge, and the focus ring is the only affordance a
   keyboard user gets for a region they have to scroll. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
  margin-top: 8px;
}
.table-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; margin-top: 8px; }
th { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border-strong); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-tertiary); font-weight: 600; }
td { padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; color: var(--text-secondary); }
td strong { color: var(--text); font-weight: 600; }

.muted { color: var(--text-tertiary); }
.finding-sub { font-size: 0.75rem; margin-top: 2px; color: var(--text-tertiary); font-weight: 400; }
.empty { color: var(--text-tertiary); font-style: italic; padding: 10px 0; margin: 0; }

/* States */
.state { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 60vh; text-align: center; padding: 48px 24px; }
.state .spinner { width: 56px; height: 56px; border: 4px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 20px; }
.state h2 { font-size: 1.375rem; font-weight: 600; margin-bottom: 8px; }
.state p { color: var(--text-tertiary); font-size: 0.875rem; max-width: 420px; margin: 0 0 16px; }
.state .progress { max-width: 360px; margin: 0 auto 8px; }
.progress-text { font-size: 0.75rem; color: var(--text-tertiary); margin-bottom: 16px; }

/* Methodology */
.m-list { margin-left: 18px; }
.m-list li { margin: 5px 0; color: var(--text-secondary); font-size: 0.8125rem; }

/* Footer / disclaimer */
.disclaimer { margin-top: 40px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 0.6875rem; color: var(--text-tertiary); line-height: 1.7; }
.disclaimer strong { color: var(--text-secondary); }
.disclaimer p { margin: 6px 0 0; color: var(--text-tertiary); font-size: 0.6875rem; }
.print-footer { display: flex; justify-content: space-between; font-size: 0.6875rem; color: var(--text-tertiary); padding-top: 16px; margin-top: 24px; border-top: 1px solid var(--border); }

/* ============================================================
   PRINT — light theme (§6.3). Screen design never prints.
   ============================================================ */

/* -------------------------------------------------------------- CONTENTS
   A large report is roughly twenty screens tall. This is the way into it.
   `no-print` keeps it out of the PDF, which is generated from this same
   document and paginates itself. */
.report-toc {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 16px 18px 18px;
  margin: 0 0 28px;
}
.report-toc-title {
  font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 10px;
}
.report-toc-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 24px;
}
.report-toc-list li { min-width: 0; }
.report-toc-list a {
  display: block; padding: 5px 0;
  font-size: 0.8125rem; color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.report-toc-list a:hover { color: var(--text); border-bottom-color: var(--border); }
.report-toc-list a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A fragment jump should not land with the heading flush to the viewport top. */
.section[id] { scroll-margin-top: 24px; }

/* ==========================================================================
   What the score means
   The score is the number this product leads with on every surface, and the
   report printed it bare. This block is mounted by report.html from the same
   deterministic model that produced the score (reporting._score_explanation),
   so it describes the arithmetic rather than editorialising around it.
   ========================================================================== */
.score-meaning {
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 16px 20px;
  margin: 16px 0 22px;
}
.score-meaning-lead {
  margin: 0 0 10px;
  font-size: 0.875rem;
  color: var(--text-secondary);
  line-height: 1.55;
}
.score-meaning-title {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 8px;
}
.score-meaning-lead b { color: var(--text); font-weight: 700; }
.score-meaning-list { margin: 0; padding-left: 18px; font-size: 0.8125rem; color: var(--text-secondary); }
.score-meaning-list li { margin: 5px 0; line-height: 1.55; }
.score-meaning-list b { color: var(--text); font-weight: 600; }
.score-meaning-note {
  margin: 12px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 0.75rem;
  color: var(--text-tertiary);
  line-height: 1.55;
}

/* ==========================================================================
   Fix these first
   Every entry links to a finding printed further down the same document, so
   the list cannot point at something the report does not show.
   ========================================================================== */
.priority {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: 18px 22px;
  margin: 18px 0 8px;
}
.priority-title {
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 4px;
  letter-spacing: -0.01em;
}
.priority-note { margin: 0 0 14px; font-size: 0.8125rem; color: var(--text-tertiary); line-height: 1.5; }
.priority-list { list-style: none; margin: 0; padding: 0; counter-reset: priority; }
.priority-item {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 11px 0;
  border-top: 1px solid var(--border);
}
.priority-item:first-child { border-top: none; }
.priority-item .sev { flex: 0 0 auto; margin-top: 2px; }
.priority-body { min-width: 0; flex: 1; }
.priority-link {
  display: block;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  line-height: 1.4;
}
.priority-link:hover { color: var(--accent); text-decoration: underline; }
.priority-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.priority-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin-top: 5px;
  font-size: 0.75rem;
  color: var(--text-tertiary);
  align-items: center;
}
.priority-tag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  font-weight: 600;
}
/* An attack path is several findings behaving as one, so it is called out
   rather than left to be read as a category name. */
.priority-tag.is-chain { border-color: var(--accent); color: var(--accent); }

/* ==========================================================================
   Attack paths
   The chain module already produced these findings; this is the same data
   with each component resolved to its real title and linked, so a reader can
   see that two separate rows above are one problem.
   ========================================================================== */
.chain-list { display: flex; flex-direction: column; gap: 14px; margin: 14px 0; }
.chain {
  border: 1px solid var(--border);
  border-left: 3px solid var(--sev-high);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 16px 20px;
  scroll-margin-top: 24px;
}
.chain-head { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; margin-bottom: 10px; }
.chain-head .sev { flex: 0 0 auto; }
.chain-title { font-size: 0.9375rem; font-weight: 700; color: var(--text); margin: 0; line-height: 1.4; }
.chain-label {
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  margin: 0 0 8px;
}
.chain-steps { list-style: none; margin: 0 0 10px; padding: 0; counter-reset: chain; }
.chain-step {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0 7px 26px;
  border-top: 1px dashed var(--border);
  position: relative;
  font-size: 0.8125rem;
}
/* Numbered, so the chain reads as a sequence rather than an unordered set. */
.chain-step::before {
  counter-increment: chain;
  content: counter(chain);
  position: absolute;
  left: 0;
  top: 7px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg-subtle);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  font-size: 0.625rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chain-step:first-child { border-top: none; }
.chain-step a { color: var(--text); font-weight: 500; text-decoration: none; overflow-wrap: anywhere; }
.chain-step a:hover { color: var(--accent); text-decoration: underline; }
.chain-step a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A chain step whose finding is not in this report. It was a link and went
   nowhere, next to a sentence promising that every link in the section is real,
   so it lost its href and kept its words - and the styling follows the link it
   replaced, so it still reads as a finding rather than as a different kind of
   thing. */
.chain-step .report-deadref { color: var(--text); font-weight: 500; overflow-wrap: anywhere; }
.report-deadref { color: var(--text); }
.chain-step .sev-quiet { opacity: 0.75; flex: 0 0 auto; }
.chain-note { margin: 0; font-size: 0.75rem; color: var(--text-tertiary); line-height: 1.55; }

/* ==========================================================================
   Asset inventory
   What the assessment found under the customer's domain. Built from evidence
   records rather than from prose, so every host listed was observed.
   ========================================================================== */
.inventory {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 16px 20px;
  margin: 14px 0;
}
.inventory-title { font-size: 0.9375rem; font-weight: 700; color: var(--text); margin: 0 0 6px; }
.inventory-note { margin: 0 0 12px; font-size: 0.8125rem; color: var(--text-secondary); line-height: 1.55; }
.inventory-warn {
  border: 1px solid var(--sev-high);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 12px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  line-height: 1.55;
}
.inventory-warn b { color: var(--text); }
.inventory-table { margin-top: 0; }
.inventory-table tr.is-dangling td { background: var(--bg-subtle); }
.inventory-flag { color: var(--text); font-weight: 600; }

/* ==========================================================================
   Finding filters
   Mounted by report.js (the container ships `hidden`, and only the script
   reveals it), so a report served without JavaScript shows the full document
   rather than a permanently invisible filter bar.
   ========================================================================== */
.finding-filter {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 14px 16px;
  margin: 12px 0 18px;
}
.finding-filter-row { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: center; }
.finding-filter-field { display: flex; flex: 1 1 200px; min-width: 0; }
.finding-filter-input, .finding-filter-select {
  width: 100%;
  font-family: inherit;
  font-size: 0.8125rem;
  color: var(--text);
  background: var(--bg-subtle);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
}
.finding-filter-input:focus-visible, .finding-filter-select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.finding-filter-field:has(.finding-filter-input) { flex: 2 1 260px; }
.finding-filter-check {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  white-space: nowrap;
}
.finding-filter-check input { accent-color: var(--accent); }
.finding-filter-reset {
  font-family: inherit;
  font-size: 0.8125rem;
  color: var(--text-tertiary);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 7px 12px;
  cursor: pointer;
}
.finding-filter-reset:hover { color: var(--text); border-color: var(--border-strong); }
.finding-filter-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.finding-filter-count {
  margin: 10px 0 0;
  font-size: 0.75rem;
  color: var(--text-tertiary);
}
.finding-filter-count:empty { display: none; }
.finding-filter-count b { color: var(--text-secondary); }
/* Filtered out, not removed: the element stays in the document (and in the
   printed PDF, which is generated from this same markup) so a filter can
   never make the report under-report what was found. */
.finding.is-filtered { display: none; }

/* ==========================================================================
   Per-finding status and lifecycle controls
   Both are mounted into placeholders the template emits, because a stored
   report is rendered once and served verbatim — report.js attaches on the way
   out, which is what lets these reach reports already in the database.
   ========================================================================== */
.finding-flags { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.finding-flag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-full);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--border-strong);
  color: var(--text-tertiary);
}
.finding-flag.is-new { border-color: var(--accent); color: var(--accent); }
.finding-flag.is-accepted { border-color: var(--sev-medium); color: var(--text-secondary); }
.finding-flag.is-resolved { border-color: var(--sev-low); color: var(--text-secondary); }

.finding-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.finding-actions:empty { display: none; }
.finding-action {
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--bg-subtle);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 5px 12px;
  cursor: pointer;
}
.finding-action:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.finding-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.finding-action:disabled { opacity: 0.55; cursor: default; }
.finding-action.is-primary { background: var(--accent-muted); border-color: var(--accent); color: var(--accent-ink); }
.finding-action.is-done { color: var(--sev-low); border-color: var(--sev-low); cursor: default; }
.finding-action-status {
  font-size: 0.75rem;
  color: var(--text-tertiary);
  align-self: center;
}
.finding-action-status.is-error { color: var(--sev-critical); }

/* The business-impact line is the answer to "why does this matter", so it is
   given the accent rather than being rendered as one more grey row. */
.finding-impact {
  border-left: 2px solid var(--accent);
  padding: 8px 0 8px 12px;
  margin: 10px 0 12px;
  background: var(--bg-subtle);
  color: var(--text-secondary);
}
.finding-impact b { color: var(--accent); }

@media print {
  .no-print { display: none !important; }
  .wrap { max-width: none; padding: 0; }
  .page-break { page-break-before: always; }
  body { font-size: 10.5px; background: #ffffff; color: #111827; }

  /* Re-point the design tokens at a light palette rather than overriding
     individual selectors. tokens.css is a dark theme (--text is #fafafa), so
     every `color: var(--text)` in this file resolved to near-white and
     vanished against the white page -- the bold spans in .summary-line and the
     whole .verify-note box were unreadable. The per-selector overrides below
     were an incomplete patch of that: each one added a rule for whatever
     element had already been reported, and the next unreported element was
     still invisible. Redefining the tokens fixes every current and future
     rule at once, and applies to the browser's own Print -> Save as PDF and to
     the WeasyPrint-generated PDF alike. */
  :root {
    --bg:              #ffffff;
    --bg-subtle:       #f8fafc;
    --surface:         #f8fafc;
    --surface-raised:  #ffffff;
    --surface-2:       #f1f5f9;

    --border:          #e5e7eb;
    --border-strong:   #cbd5e1;

    --text:            #0f172a;
    --text-secondary:  #475569;
    --text-tertiary:   #64748b;

    --accent:          #0369a1;
    --accent-hover:    #075985;
    --accent-muted:    #e0f2fe;
    --accent-ink:      #ffffff;

    --success:         #15803d;
    --success-muted:   #dcfce7;
    --success-text:    #166534;
    --warning:         #a16207;
    --warning-muted:   #fef9c3;
    --warning-text:    #854d0e;
    --danger:          #b91c1c;
    --danger-muted:    #fee2e2;
    --danger-text:     #991b1b;
    --info:            #0369a1;
    --info-muted:      #e0f2fe;
    --info-text:       #075985;
  }

  /* Light pill pairs (existing report print palette) */
  .sev.critical { background: #fee2e2; color: #991b1b; }
  .sev.high { background: #ffedd5; color: #9a3412; }
  .sev.medium { background: #fef9c3; color: #854d0e; }
  .sev.low { background: #dbeafe; color: #1e40af; }
  .sev.info { background: #f3f4f6; color: #374151; }

  .header { border-bottom: 4px solid #0f172a; }
  .header .badge { background: #0f172a; color: #fff; }
  .header h1 { color: #0f172a; }
  .header h1 span, .header-meta, .muted, .summary-line { color: #6b7280; }
  .score-row, .ai-review, .chip, .finding, .state .progress { background: #f8fafc; border-color: #e5e7eb; }
  .score-num, h2.section { color: #0f172a; }
  .ai-review h3, .finding-title, .row strong, td strong, .kpis b { color: #0f172a; }
  .kpis b, .score-num b { color: #0f172a; }
  /* .summary-line strong is not in the list above; it is dark by default now
     via the token, which is the point of re-pointing them. */
  code { background: #f1f5f9; border-color: #e5e7eb; color: #334155; }
  /* Evidence stays a dark code block, so it must not follow the token flip --
     it is a deliberate high-contrast panel, and light-on-light would be worse
     than the original. */
  .evidence-item { background: #0f172a; color: #e2e8f0; border-color: #0f172a; }
  .verify-note { background: #f8fafc; border-color: #e5e7eb; color: #0f172a; }
  .verify-note a { color: #0369a1; }
  .verify-note-text[data-ownership-verified="1"] { border-color: #15803d; }
  th { border-bottom-color: #d1d5db; }
  td, .row, .finding-row, .m-list li { color: #374151; }
  .finding-row b, th, .group .group-name { color: #6b7280; }
  .verdict-badge { color: #0b1220; }
  .state h2 { color: #111827; }
  .print-footer, .disclaimer, .disclaimer p, .ai-unavailable, .empty, .progress-text { color: #6b7280; }
  .report-nav { display: none; }

  /* The customer-facing blocks added above have to survive into the PDF, which
     is generated from this same document. Their dark-mode tokens would
     otherwise print as dark-on-dark, so they are re-pointed at the light print
     palette here alongside the blocks that already were. */
  .score-meaning, .priority, .chain, .inventory, .finding-filter, .finding-impact {
    background: #f8fafc; border-color: #e5e7eb;
  }
  .score-meaning-lead b, .score-meaning-list b, .score-meaning-list,
  .priority-title, .priority-link, .chain-title, .inventory-title,
  .inventory-note, .inventory-warn, .inventory-warn b, .chain-step a,
  .finding-impact { color: #0f172a; }
  .score-meaning-title { color: #0f172a; }
  .score-meaning-note, .priority-note, .priority-meta, .chain-label,
  .chain-note, .inventory-note, .finding-filter-count, .finding-action-status {
    color: #6b7280;
  }
  .chain-step::before { background: #ffffff; border-color: #d1d5db; color: #374151; }
  .inventory-flag { color: #0f172a; }
  .finding-impact { border-left-color: #0f172a; }
  .finding-impact b { color: #6b7280; }
  /* A finding the reader has marked resolved stays in the printed report, but
     it should not read as an open item on paper. */
  .finding.is-resolved-print { opacity: 0.62; }
  /* The lifecycle controls and the filter bar are screen-only. `.no-print`
     already hides the filter; the buttons are hidden here because a printed
     page cannot act on them and an inert button on paper is noise. */
  .finding-actions { display: none !important; }

  /* Keep a table row's cells together and stop rows splitting across a page,
     which is what produced the shredded-looking tables in the generated PDF. */
  tr, .finding, .group, .sx-se-card, .disclaimer, .chain, .priority-item,
  .inventory-warn, .score-meaning, .priority { break-inside: avoid; page-break-inside: avoid; }
  h2.section, h3, .finding-head { break-after: avoid; page-break-after: avoid; }
  thead { display: table-header-group; }

  /* Flatten flex/grid to block for print. WeasyPrint re-runs layout for every
     page, and its flexbox implementation is the single most expensive thing in
     the render: profiling a 36-page report put ~53 of 54 cumulative seconds in
     flex_layout, with text line-breaking underneath it, and the download took
     28s. A paged document does not need flex to arrange two side-by-side
     blocks -- inline-block and a plain block flow lay out in a fraction of the
     time and paginate correctly. The screen layout above is untouched. */
  .score-row, .score-num, .kpis, .finding-head, .finding-meta, .row,
  .sx-se-list, .sx-se-top, .chips, .rows, .print-footer, .header, .state,
  .priority-item, .priority-body, .chain-head, .chain-step,
  .finding-filter-row, .finding-actions, .finding-flags {
    display: block;
  }
  /* .report-nav and .pdf-export are deliberately NOT in that list: they are
     screen-only chrome and the print rules above hide them. Flattening them to
     block here would win on source order and put the "Back to assessments"
     arrow and the Download button back into the printed page. */
  .kpis > div, .chips > .chip, .sx-se-top > *, .finding-meta > span {
    display: inline-block; vertical-align: top;
  }
  /* The flex `gap` above does not survive flattening to inline-block, so the
     spacing has to be re-stated or the KPI labels collide into one run of text
     ("2 Confirmed Findings 0 Critical / High 47 Observations"). */
  .kpis > div { margin: 0 26px 10px 0; }
  .sx-se-top > * { margin: 0 10px 0 0; }
  /* Same reason: the flattened flex rows above lose their `gap`. */
  .priority-item .sev, .chain-head .sev, .chain-step .sev-quiet,
  .finding-flags > * { margin: 0 8px 0 0; }
  .chain-step { padding-left: 26px; }
  .score-row { padding: 18px 22px; }
  .score-num { text-align: left; margin-bottom: 12px; }
  .row { padding: 5px 2px; }
  .row > code { margin-right: 8px; }
  .finding-meta > span { margin: 0 18px 4px 0; }
  .chips > .chip { margin: 0 8px 8px 0; }
  .print-footer > span { display: block; }
  .header { padding-bottom: 14px; }
  .header-meta { margin-top: 10px; }
  /* 36px of top margin per section is generous on screen and, once the flex
     rows above are stacked, reads as a gap of nothing between sections. */
  h2.section { margin: 22px 0 12px; }
}