/* Landing page redesign — staging preview only.
 *
 * Loaded only by GET /preview (see webui/routes/site.py), same pattern as
 * static/css/diligence-report.css: kept out of app.css because nothing
 * else on the site would ever use it, and every rule below is scoped
 * under .pv so it cannot leak into the real rail/nav/theme when this page
 * shares base.html's <head> and <body> chrome with the rest of the site.
 *
 * Two things this file deliberately does NOT do, both load-bearing:
 *
 * 1. No style="" attribute anywhere in webui/templates/preview.html. The
 *    site's CSP ships `style-src 'self'` with nothing after it - no
 *    'unsafe-inline', no style-src-attr fallback - specifically because
 *    Firefox does not implement style-src-attr and would fall back to
 *    style-src, silently dropping every inline style on the page (see the
 *    CSP comment block in webui/main.py and CLAUDE.md). Every inline
 *    style="..." in the original mockup (evulnable_landing_redesign.html)
 *    is a named class here instead; tests/test_security_headers.py's
 *    TemplateInlineStyleTests scans every template under webui/templates
 *    and fails the whole suite if a style attribute or a <style> element
 *    comes back, preview.html included.
 *
 * 2. No @font-face for Inter. The mockup asked for Inter, but nothing on
 *    this site fetches a font from anywhere but /static/fonts (font-src
 *    'self' in the CSP, and no Google Fonts <link> exists here the way it
 *    might on a Google-Fonts-hosted mockup) - so pulling in Inter would
 *    mean vendoring a fourth type family for one page. Instrument Sans is
 *    already self-hosted and preloaded site-wide (app.css, variable file,
 *    400-700) and is visually the same lane as Inter - a modern grotesque
 *    with a similar x-height - so it is substituted here rather than
 *    adding a font the rest of the site will never use.
 */

/* Same file, same latin range, same license (SIL OFL 1.1) as app.css's
 * declaration - duplicated rather than shared because this page does not
 * load app.css (see webui/templates/preview.html: it is a standalone
 * document, not a base.html page, so the site's real rail/nav chrome
 * never doubles up alongside the redesign's own top nav). Declaring the
 * font twice costs nothing extra over the wire - the browser fetches the
 * woff2 file once regardless of how many @font-face rules name it.
 */
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/instrument-sans-latin-variable.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

.pv-page {
  --pv-bg: #f6f9fc;
  --pv-panel: #ffffff;
  --pv-panel-2: #f9fbfe;
  --pv-ink: #0b1f3a;
  --pv-muted: #58708c;
  /* A second, slightly stronger secondary-text tier - table headers, the
     mini-dashboard's labels, footer text - that was previously a handful
     of separately hand-picked grays (#59718a, #66809b, #6b829b...) close
     enough to read as the same tier but not actually tied together, so a
     future contrast fix to one would not reach the others. */
  --pv-muted-2: #5f7893;
  --pv-line: #dce7f2;
  --pv-line-2: #edf3f8;
  --pv-blue: #1269e8;
  --pv-blue-2: #0c8bf0;
  /* Light wash of --pv-blue, for icon chips / active nav rows / source
     chips - previously three near-identical hardcoded blues (#edf5ff,
     #ecf4ff, #eaf4ff) that were never meant to differ. */
  --pv-blue-soft-bg: #ecf4ff;
  --pv-navy: #0d2951;
  --pv-green: #17a768;
  --pv-green-soft: #eaf8f1;
  --pv-red: #e74a4a;
  --pv-red-soft: #fff0f1;
  --pv-orange: #f59e0b;
  --pv-orange-soft: #fff7e9;
  --pv-purple: #7c59ff;
  --pv-purple-soft: #f2ecff;
  --pv-teal: #15b8a6;

  /* Pale, saturated-enough-to-read-as-color borders for the tinted source/
     stat tiles below (.pv-tint-*) - a shade between --pv-line (almost
     invisible) and the full accent color (too loud for a 1px border on a
     soft fill). One per accent, reused by both the Trusted Sources tiles
     and the Live Intelligence Summary cards so the two sections share one
     palette rather than inventing color twice. */
  --pv-blue-tile-border: #cfe1fb;
  --pv-red-tile-border: #f6c9cb;
  --pv-orange-tile-border: #f5dfad;
  --pv-green-tile-border: #b9e6cf;
  --pv-purple-tile-border: #ddd0fb;
  --pv-shadow: 0 24px 60px rgba(15, 38, 72, .08);
  --pv-shadow-sm: 0 10px 30px rgba(15, 38, 72, .06);
  --pv-radius: 24px;
  --pv-radius-sm: 16px;
  --pv-max: 1220px;

  /* The priority-feature card's gradient, and the pink-tinted divider/
     border around its score panel - was three literal hex values with no
     dark equivalent. */
  --pv-feature-bg-a: rgba(255, 255, 255, 1);
  --pv-feature-bg-b: rgba(255, 248, 248, 1);
  --pv-feature-border: #f3d6d8;
  --pv-feature-divider: #f1dde1;

  /* The bottom CTA band's gradient + border. */
  --pv-cta-a: #eaf5ff;
  --pv-cta-b: #f7fcff;
  --pv-cta-c: #edf9f3;
  --pv-cta-border: #d7e7f4;

  /* Warning/notice banners (.pv-unavailable-banner, .pv-note-warn) -
     same wash as --pv-orange-soft, plus its own border/text pair. */
  --pv-warn-border: #f3d896;
  --pv-warn-text: #7a5a12;

  /* The mini-dashboard card's sidebar background and the chip/pill-gray
     surface (.pv-chip, .pv-result-score-mini.pv-unscored). */
  --pv-side-bg: #f7fbff;
  --pv-chip-bg: #f3f7fb;
  --pv-chip-text: #4d6984;

  /* A softened border-accent used on a couple of outlined controls
     (.pv-btn.pv-secondary, .wl-save-btn). */
  --pv-accent-border: #a6c9f7;

  /* The sticky header's translucent backdrop - --pv-bg with alpha, not a
     separate hardcoded color, so it always matches the page behind it. */
  --pv-header-bg: rgba(246, 249, 252, .84);

  color-scheme: light;
  font-family: "Instrument Sans", ui-sans-serif, system-ui, -apple-system,
    BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--pv-ink);
  background:
    radial-gradient(circle at 10% 0%, rgba(18, 105, 232, .08), transparent 30%),
    radial-gradient(circle at 90% 8%, rgba(21, 184, 166, .07), transparent 25%),
    var(--pv-bg);
}

/* Dark mode. Same token set, dark values - built from the dark-mode
 * mockup Marc had a second AI draft (evulnable_landing_redesign_dark.html):
 * that file redefined this exact list of surface tokens for a dark
 * palette, so those values are reused here directly rather than picked
 * again from scratch. Selector matches .pv-page's own specificity plus
 * the attribute, so it wins regardless of source order without
 * !important. data-theme lives on the same <body class="pv-page"> element
 * (see redesign/_shell.html) and is stamped server-side from a cookie
 * (webui/routes/redesign.py's _pv_theme), the same way the real site's
 * data-theme avoids a flash of the wrong theme on load.
 *
 * Severity/rank/status colors (risk badges, dots, pill red/orange/blue/
 * gray) are NOT themed here, on purpose - matching the real site's own
 * rule that severity color is never touched by a preset
 * (webui/theme.py's module docstring). A Critical finding stays the same
 * red in both themes.
 */
.pv-page[data-theme="dark"] {
  --pv-bg: #07111f;
  --pv-panel: #0d1828;
  --pv-panel-2: #132235;
  --pv-ink: #e8f1fb;
  --pv-muted: #9db2c9;
  --pv-muted-2: #93a7bd;
  --pv-line: #1e334c;
  --pv-line-2: #20374f;
  --pv-blue: #3b82f6;
  --pv-blue-2: #1aa3ff;
  --pv-blue-soft-bg: rgba(59, 130, 246, .14);
  --pv-navy: #dceaf8;
  --pv-green: #34d399;
  --pv-green-soft: rgba(52, 211, 153, .12);
  --pv-red: #ff6b6b;
  --pv-red-soft: rgba(255, 107, 107, .12);
  --pv-orange: #fbbf24;
  --pv-orange-soft: rgba(251, 191, 36, .13);
  --pv-purple: #a78bfa;
  --pv-purple-soft: rgba(167, 139, 250, .14);
  --pv-teal: #2dd4bf;

  --pv-blue-tile-border: rgba(59, 130, 246, .32);
  --pv-red-tile-border: rgba(255, 107, 107, .32);
  --pv-orange-tile-border: rgba(251, 191, 36, .32);
  --pv-green-tile-border: rgba(52, 211, 153, .32);
  --pv-purple-tile-border: rgba(167, 139, 250, .32);
  --pv-shadow: 0 28px 70px rgba(0, 0, 0, .38);
  --pv-shadow-sm: 0 14px 34px rgba(0, 0, 0, .28);

  --pv-feature-bg-a: rgba(22, 30, 45, .98);
  --pv-feature-bg-b: rgba(34, 20, 24, .98);
  --pv-feature-border: #5a2b31;
  --pv-feature-divider: #51313a;

  --pv-cta-a: rgba(14, 32, 56, .98);
  --pv-cta-b: rgba(11, 24, 41, .98);
  --pv-cta-c: rgba(11, 31, 32, .98);
  --pv-cta-border: #28425d;

  --pv-warn-border: #6b5320;
  --pv-warn-text: #ffd36b;

  --pv-side-bg: #0b1626;
  --pv-chip-bg: #132235;
  --pv-chip-text: #bdd2ea;

  --pv-accent-border: #2c4c73;
  --pv-header-bg: rgba(7, 17, 31, .84);

  color-scheme: dark;
  background:
    radial-gradient(circle at 8% 0%, rgba(59, 130, 246, .16), transparent 28%),
    radial-gradient(circle at 88% 10%, rgba(45, 212, 191, .10), transparent 24%),
    var(--pv-bg);
}

.pv-page * { box-sizing: border-box; }
.pv-page { scroll-behavior: smooth; }
.pv-page a { color: inherit; text-decoration: none; }
.pv-skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--pv-blue); color: #fff;
  padding: 10px 16px; border-radius: 0 0 12px 0;
}
.pv-skip-link:focus { left: 0; }
.pv-page img { display: block; max-width: 100%; }
.pv-container { width: min(var(--pv-max), calc(100% - 36px)); margin: auto; }
.pv-muted { color: var(--pv-muted); }
.pv-eyebrow {
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--pv-muted-2);
  font-size: .78rem;
  font-weight: 800;
  margin: 0 0 14px;
}
.pv-page h1, .pv-page h2, .pv-page h3, .pv-page p { margin-top: 0; }
.pv-page h1 { font-size: clamp(2.8rem, 6vw, 5rem); line-height: .97; letter-spacing: -.06em; margin-bottom: 18px; }
.pv-page h2 { font-size: clamp(2rem, 4vw, 3rem); line-height: 1.04; letter-spacing: -.04em; margin-bottom: 14px; }
.pv-page h3 { font-size: 1.02rem; line-height: 1.25; margin-bottom: 8px; }
.pv-page p { line-height: 1.68; }

.pv-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 50px; padding: 0 20px; border-radius: 12px; font-weight: 800;
  border: 1px solid transparent; transition: .2s ease; white-space: nowrap;
}
.pv-btn.pv-primary { color: #fff; background: linear-gradient(135deg, var(--pv-blue), var(--pv-blue-2)); box-shadow: 0 14px 34px rgba(18, 105, 232, .22); }
.pv-btn.pv-primary:hover { transform: translateY(-1px); }
.pv-btn.pv-secondary { color: var(--pv-blue); border-color: var(--pv-accent-border); background: var(--pv-panel); }
.pv-btn.pv-ghost { color: var(--pv-ink); border-color: var(--pv-line); background: var(--pv-panel); }

.pv-header {
  position: sticky; top: 0; z-index: 40;
  backdrop-filter: blur(14px);
  background: var(--pv-header-bg);
  border-bottom: 1px solid var(--pv-line);
}
.pv-nav { display: flex; align-items: center; justify-content: space-between; gap: 20px; height: 82px; }
.pv-brand img { width: 210px; height: auto; }
.pv-navlinks { display: flex; gap: 26px; align-items: center; color: var(--pv-muted-2); font-weight: 700; font-size: .93rem; }
.pv-navlinks a.pv-active { color: var(--pv-blue); position: relative; }
.pv-navlinks a.pv-active:after { content: ""; position: absolute; left: 0; right: 0; bottom: -14px; height: 3px; border-radius: 999px; background: var(--pv-blue); }
/* A redesigned page that has not been built yet. Same convention the real
   rail uses (webui/nav.py, base.html's .nav-item.is-pending): listed so
   the shape of the whole nav is visible, not linked, because a nav full
   of links that 404 is worse than one that says plainly what isn't here. */
.pv-navlinks span.pv-pending { color: var(--pv-muted); cursor: default; opacity: .7; }

/* A secondary page's header - Priority Feed and the rest of the
   redesigned section use this instead of the marketing hero, which only
   the landing page gets. */
.pv-page-head { padding: 44px 0 8px; }
.pv-page-head h1 { font-size: clamp(2.2rem, 4.5vw, 3.4rem); letter-spacing: -.05em; margin-bottom: 6px; }
.pv-unavailable-banner {
  background: var(--pv-orange-soft); border: 1px solid var(--pv-warn-border); border-radius: 16px;
  padding: 14px 18px; font-size: .88rem; color: var(--pv-warn-text); margin-bottom: 18px;
}
.pv-nav-actions { display: flex; gap: 12px; align-items: center; }

.pv-hero { padding: 52px 0 28px; }
.pv-hero-grid { display: grid; grid-template-columns: 1.02fr .98fr; gap: 36px; align-items: center; }
.pv-hero-copy .pv-actions { display: flex; gap: 14px; flex-wrap: wrap; margin: 28px 0 26px; }
/* Used to be four separately-bordered white cards dropped onto the hero's
   own background, then got flattened to plain rows because each boxed
   card read as pasted in from elsewhere - see git history. Brought back
   as tiles on purpose (2026-09-06, Marc: "bring more color to the tile to
   stand out more from the background") - the difference from the original
   boxed version is a soft color wash per source (.pv-tint-*, shared with
   the stat cards below) instead of a plain white/dark panel, so each tile
   reads as *that* source rather than as a generic box. */
.pv-source-row {
  display: flex; flex-wrap: wrap; gap: 18px 28px; margin-top: 18px;
  padding-top: 18px; border-top: 1px solid var(--pv-line);
}
.pv-source-item {
  flex: 1 1 220px; display: flex; gap: 12px; align-items: flex-start;
  background: var(--pv-panel-2); border: 1px solid var(--pv-line); border-radius: 14px;
  padding: 12px 14px;
}
.pv-source-icon { width: 30px; height: 30px; border-radius: 9px; background: var(--pv-blue-soft-bg); color: var(--pv-blue); display: grid; place-items: center; font-weight: 900; font-size: .82rem; flex: 0 0 auto; }
.pv-source-item b { display: block; margin-bottom: 2px; font-size: .92rem; }
.pv-source-item span { font-size: .78rem; color: var(--pv-muted); line-height: 1.45; }

.pv-dash-card {
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 28px; box-shadow: var(--pv-shadow); padding: 18px;
  position: relative; overflow: hidden;
}
.pv-dash-card:before {
  content: ""; position: absolute; inset: -40% auto auto 56%; width: 280px; height: 280px; border-radius: 50%;
  background: radial-gradient(circle, rgba(18, 105, 232, .12), transparent 65%);
  pointer-events: none;
}
.pv-mini-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.pv-mini-title { font-weight: 900; font-size: 1.08rem; }
.pv-mini-label { color: var(--pv-muted-2); font-size: .8rem; font-weight: 700; }
.pv-mini-shell { display: grid; grid-template-columns: 120px 1fr; border: 1px solid var(--pv-line-2); border-radius: 20px; overflow: hidden; }
.pv-mini-side { background: var(--pv-side-bg); border-right: 1px solid var(--pv-line-2); padding: 14px 10px; }
.pv-mini-side a { display: flex; align-items: center; gap: 10px; padding: 10px 8px; border-radius: 10px; font-size: .84rem; color: var(--pv-muted-2); font-weight: 700; }
.pv-mini-side a.pv-active { background: var(--pv-blue-soft-bg); color: var(--pv-blue); }
.pv-mini-main { padding: 16px; }
.pv-list-table { width: 100%; border-collapse: collapse; font-size: .84rem; }
.pv-list-table th, .pv-list-table td { padding: 9px 0; border-bottom: 1px solid var(--pv-line-2); text-align: left; vertical-align: middle; }
.pv-list-table th { color: var(--pv-muted-2); font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; }
/* The rank column was unstyled table text - same weight and color as
   every other cell, no fixed width - so a CVE id too long for its own
   column (the next one over) wrapped to two lines while "#" stayed one,
   and five single-line numbers next to five two-line CVE cells is what
   read as "numbers out of place" rather than a ranked list. nowrap plus
   ellipsis on the id is the fix - every row is one line now, so the rank
   number beside it sits exactly where a reader expects it - and a fixed
   narrow width keeps the rank column from ever competing with the id or
   title columns for space. */
.pv-list-table th:first-child, .pv-list-table td:first-child { width: 20px; color: var(--pv-muted); font-weight: 700; }
.pv-cve-link { font-weight: 700; color: var(--pv-blue); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pv-risk-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 36px; padding: 6px 9px; border-radius: 999px; color: #fff; font-weight: 800; font-size: .76rem; }
/* One class per EVRS band (scoring/evrs.py's EVRS_PRIORITY_BANDS), not per
   score - a real feed's numbers vary, so nothing here can be a literal
   like the mockup's .r97. Same five colors as the backlog dots below. */
.pv-risk-badge.pv-band-immediate { background: #ee4c4c; }
.pv-risk-badge.pv-band-urgent { background: #f97316; }
.pv-risk-badge.pv-band-elevated { background: #f59e0b; }
.pv-risk-badge.pv-band-routine { background: #22c55e; }
.pv-risk-badge.pv-band-informational { background: #94a3b8; }
.pv-mini-quote { margin-top: 14px; background: var(--pv-panel-2); border: 1px solid var(--pv-line-2); border-radius: 14px; padding: 14px; color: var(--pv-muted); font-size: .84rem; line-height: 1.6; }
.pv-mini-foot { margin-top: 12px; font-size: .72rem; color: var(--pv-muted-2); display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-weight: 800; }

.pv-page section { padding: 22px 0; }
.pv-section-head { display: flex; justify-content: space-between; gap: 20px; align-items: flex-end; margin-bottom: 18px; }
.pv-section-head p { margin-bottom: 0; }
.pv-section-head.pv-mb-16 { margin-bottom: 16px; }

/* h2 sizing shared by every section head in the mockup */
.pv-section-h2 { font-size: 2.3rem; margin-bottom: 4px; }

.pv-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
/* A stat card is a filter control on pages that back it with one (Priority
   Feed, Patch Advisories) - see redesign.py's `cut` handling - so it renders
   as an <a>, not a <div>, on those pages; reset the link defaults rather
   than restyle from scratch, and add a focus ring since a colored card on
   a colored page can't rely on the browser's default outline showing up. */
.pv-stat-card { display: block; background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px; padding: 20px; box-shadow: var(--pv-shadow-sm); text-decoration: none; color: inherit; transition: transform .15s ease, box-shadow .15s ease; }
a.pv-stat-card:hover { transform: translateY(-2px); }
a.pv-stat-card:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }
/* The active cut's tile - a colored ring in its own category color, on top
   of whatever tint :has() already gave the card, so "which cut is this"
   reads at a glance rather than from the URL bar. */
.pv-stat-card.is-selected:has(.pv-blue-soft) { box-shadow: 0 0 0 2px var(--pv-blue) inset, var(--pv-shadow-sm); }
.pv-stat-card.is-selected:has(.pv-red-soft) { box-shadow: 0 0 0 2px var(--pv-red) inset, var(--pv-shadow-sm); }
.pv-stat-card.is-selected:has(.pv-orange-soft) { box-shadow: 0 0 0 2px var(--pv-orange) inset, var(--pv-shadow-sm); }
.pv-stat-card.is-selected:has(.pv-green-soft) { box-shadow: 0 0 0 2px var(--pv-green) inset, var(--pv-shadow-sm); }
.pv-stat-top { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.pv-stat-icon { width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center; font-weight: 900; font-size: 1.2rem; }
.pv-blue-soft { background: var(--pv-blue-soft-bg); color: var(--pv-blue); }
.pv-red-soft { background: var(--pv-red-soft); color: var(--pv-red); }
.pv-orange-soft { background: var(--pv-orange-soft); color: var(--pv-orange); }
.pv-green-soft { background: var(--pv-green-soft); color: var(--pv-green); }
.pv-purple-soft { background: var(--pv-purple-soft); color: var(--pv-purple); }
.pv-stat-num { font-size: 2.05rem; font-weight: 900; letter-spacing: -.04em; line-height: 1; }
.pv-stat-card p { margin-bottom: 0; font-size: .9rem; color: var(--pv-muted); }

/* Same soft-wash-plus-border tint as the Trusted Sources tiles, applied
   directly to a source item (.pv-tint-red etc, added in the markup) or
   picked up automatically on a stat card via :has() below. */
.pv-tint-blue { background: var(--pv-blue-soft-bg); border-color: var(--pv-blue-tile-border); }
.pv-tint-red { background: var(--pv-red-soft); border-color: var(--pv-red-tile-border); }
.pv-tint-orange { background: var(--pv-orange-soft); border-color: var(--pv-orange-tile-border); }
.pv-tint-green { background: var(--pv-green-soft); border-color: var(--pv-green-tile-border); }
.pv-tint-purple { background: var(--pv-purple-soft); border-color: var(--pv-purple-tile-border); }

/* "Color the tile with the appropriate criticality color when present"
 * (Marc, 2026-09-06) - every .pv-stat-card across landing/priority-feed/
 * patch-advisories already carries exactly one .pv-*-soft icon-chip class
 * naming its own category (see those templates), so :has() reads that
 * straight off the markup already there instead of a second modifier class
 * that could drift out of sync with the icon beside it. New stat cards on
 * future pages pick this up for free as long as they reuse the same
 * icon-chip markup.
 */
.pv-stat-card:has(.pv-blue-soft) { background: var(--pv-blue-soft-bg); border-color: var(--pv-blue-tile-border); }
.pv-stat-card:has(.pv-red-soft) { background: var(--pv-red-soft); border-color: var(--pv-red-tile-border); }
.pv-stat-card:has(.pv-orange-soft) { background: var(--pv-orange-soft); border-color: var(--pv-orange-tile-border); }
.pv-stat-card:has(.pv-green-soft) { background: var(--pv-green-soft); border-color: var(--pv-green-tile-border); }
.pv-stat-card:has(.pv-green-soft) .pv-stat-num { color: var(--pv-green); }

.pv-priority-grid { display: grid; gap: 14px; }
/* Was a two-column grid - title/description on the left, the score panel
   as a side column on the right. The two never have comparable amounts
   of content (a one-line "why" versus a five-row stat panel), so the
   shorter side just left dead air in whichever column lost, and the
   score panel read as a separate box bolted on rather than part of the
   card. Stacked instead: the panel becomes a divider-topped stat strip
   that only ever takes the height its own content needs. */
.pv-priority-feature {
  background: linear-gradient(90deg, var(--pv-feature-bg-a), var(--pv-feature-bg-b));
  border: 1px solid var(--pv-feature-border); border-left: 5px solid var(--pv-red); border-radius: 22px; padding: 22px;
  box-shadow: var(--pv-shadow-sm); display: flex; flex-direction: column; gap: 4px;
}
.pv-priority-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 10px; }
.pv-pill { display: inline-flex; align-items: center; padding: 6px 10px; border-radius: 999px; font-weight: 800; font-size: .73rem; line-height: 1; }
.pv-pill.pv-red { background: #ffeded; color: #db3131; }
.pv-pill.pv-orange { background: #fff4de; color: #cc7a00; }
.pv-pill.pv-blue { background: #ebf4ff; color: #1768d1; }
.pv-pill.pv-gray { background: var(--pv-chip-bg); color: var(--pv-chip-text); }
.pv-pill.pv-green { background: var(--pv-green-soft); color: var(--pv-green); }
/* The #1 rank marker on the expanded feature card. Every other row's rank
   number (.pv-rank-box, in the collapsed rows below) reads at the body
   font size and font-weight 900 - the featured card is the one item a
   reader should never mistake for lower-priority, so its own rank number
   is sized to match or exceed that, not shrink to chip size alongside
   Exigent/Ransomware-linked/CISA KEV. */
.pv-pill.pv-rank-hero { font-size: 1.15rem; font-weight: 900; padding: 4px 14px; color: var(--pv-navy); }
.pv-priority-feature h3 { font-size: 1.82rem; letter-spacing: -.04em; margin-bottom: 10px; }
.pv-cve-label { font-weight: 900; color: var(--pv-blue); margin-bottom: 8px; }
.pv-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.pv-chip { background: var(--pv-chip-bg); border: 1px solid var(--pv-line); color: var(--pv-chip-text); padding: 7px 10px; border-radius: 10px; font-size: .76rem; font-weight: 700; }
/* A horizontal stat strip under a top divider, not a boxed side panel -
   see the .pv-priority-feature comment above for why. display:contents
   on the two .pv-score-grid wrappers lets their .pv-score-block children
   flow directly into this one flex row instead of sitting in two
   separate fixed-width grids of mismatched width. */
.pv-score-panel {
  margin-top: 14px; padding-top: 16px; border-top: 1px solid var(--pv-feature-divider);
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 18px 32px;
}
.pv-score-grid { display: contents; }
.pv-score-block { flex: 0 0 auto; }
.pv-score-block small { display: block; color: var(--pv-muted-2); font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 4px; }
.pv-score-block b { font-size: 1.85rem; letter-spacing: -.05em; }
.pv-score-block b.pv-b-sm { font-size: 1.1rem; }
.pv-risk-suffix { font-size: .6em; color: var(--pv-red); opacity: .7; }
.pv-score-links {
  flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 8px 18px;
  padding-top: 14px; margin-top: 2px; border-top: 1px solid var(--pv-feature-divider);
  font-size: .86rem; font-weight: 800; color: var(--pv-blue);
}
.pv-priority-row {
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; padding: 14px 16px;
  display: grid; grid-template-columns: 50px 1fr auto auto auto; gap: 16px; align-items: center;
}
.pv-rank-box { font-weight: 900; color: var(--pv-navy); background: var(--pv-panel-2); border-radius: 12px; padding: 10px 0; text-align: center; }
.pv-row-title { font-weight: 800; }
.pv-row-meta { color: var(--pv-muted); font-size: .9rem; }
.pv-row-score { font-weight: 900; color: var(--pv-red); min-width: 34px; text-align: right; }

/* ---- Priority feed rows -------------------------------------------
   The expandable row, replacing the flat .pv-priority-row line. What is
   behind the disclosure is the part the redesign had dropped: the
   measurement chips, the sentence saying why the item ranks where it
   does, and the links out to NVD, CISA KEV and the vendor advisory.

   Severity colour is defined here rather than taken from theme.py's
   [data-sev] tokens, because this section loads preview.css only - the
   app.css that carries those tokens is not on these pages. Same five
   bands, same meanings, expressed in the redesign's own palette.

   Severity colour is never themed - it is the one thing on the site that
   has to mean the same in both themes. */
.pv-feed-list { display: grid; gap: 10px; }
.pv-feed-entry {
  position: relative;
  background: var(--pv-panel);
  border: 1px solid var(--pv-line);
  border-radius: 18px;
}
.pv-feed-entry[open] { border-color: var(--pv-blue); }
.pv-feed-summary {
  display: grid;
  /* Named explicitly, not left to auto-placement. When the score tile
     goes out of flow on an open row, auto-placement would slide every
     later cell one column left and wrap the title inside the 76px score
     track - measured on the classic feed, where it took a 65px summary
     to 380px before the columns were pinned. */
  grid-template-columns: 46px 76px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 12px 16px;
  cursor: pointer;
  list-style: none;
}
.pv-feed-summary::-webkit-details-marker { display: none; }
.pv-feed-rank { grid-column: 1; font-weight: 900; color: var(--pv-navy); background: var(--pv-panel-2); border-radius: 12px; padding: 8px 0; text-align: center; }
.pv-feed-score {
  grid-column: 2;
  display: grid; gap: 0; align-content: center;
  padding: 4px 9px;
  border-left: 3px solid var(--pv-sev, var(--pv-muted));
  border-radius: 0 12px 12px 0;
  background: var(--pv-sev-wash, var(--pv-panel-2));
}
.pv-feed-score b { font-size: 1.2rem; line-height: 1.15; color: var(--pv-sev, var(--pv-ink)); }
.pv-feed-score span { font-size: .68rem; font-weight: 800; color: var(--pv-sev, var(--pv-muted)); }
.pv-feed-score[data-sev="critical"] { --pv-sev: #db3131; --pv-sev-wash: #ffeded; }
.pv-feed-score[data-sev="high"]     { --pv-sev: #cc7a00; --pv-sev-wash: #fff4de; }
.pv-feed-score[data-sev="medium"]   { --pv-sev: #a06a00; --pv-sev-wash: #fff7e9; }
.pv-feed-score[data-sev="low"]      { --pv-sev: #1768d1; --pv-sev-wash: #ebf4ff; }
.pv-feed-score[data-sev="info"]     { --pv-sev: #58708c; --pv-sev-wash: #eef3f9; }
.pv-feed-score[data-sev="exigent"]  { --pv-sev: #7c59ff; --pv-sev-wash: #f2ecff; }

.pv-feed-main { grid-column: 3; display: grid; gap: 3px; min-width: 0; }
.pv-feed-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.pv-feed-cve { font-weight: 800; font-size: .88rem; color: var(--pv-blue); text-decoration: none; }
.pv-feed-cve:hover { text-decoration: underline; }
.pv-feed-flags { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.pv-feed-title { font-size: .9rem; color: var(--pv-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-feed-entry[open] .pv-feed-title { white-space: normal; }

.pv-feed-side { grid-column: 4; display: inline-flex; align-items: center; gap: 10px; justify-self: end; }
.pv-feed-recommend { font-size: .73rem; font-weight: 800; padding: 5px 10px; border-radius: 999px; background: var(--pv-chip-bg); color: var(--pv-chip-text); white-space: nowrap; }
.pv-feed-expand {
  width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 6px solid var(--pv-muted);
  transition: transform 120ms;
}
.pv-feed-entry[open] .pv-feed-expand { transform: rotate(180deg); }

/* The detail indents past the rank and score columns, and the tile runs
   down the strip that leaves - so an opened row reads as one block
   rather than as a small chip above an empty column. */
.pv-feed-detail {
  display: grid; gap: 10px; justify-items: start;
  padding: 0 16px 14px calc(16px + 46px + 14px + 76px + 14px);
}
.pv-feed-entry[open] .pv-feed-score {
  position: absolute;
  top: 12px; bottom: 14px;
  left: calc(16px + 46px + 14px);
  width: 76px;
  align-content: start;
}
.pv-feed-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.pv-meta-pill { display: inline-flex; align-items: center; gap: 5px; font-size: .78rem; font-weight: 700; padding: 5px 10px; border-radius: 999px; background: var(--pv-panel-2); color: var(--pv-ink); }
.pv-meta-pill small { color: var(--pv-muted); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; font-size: .64rem; }
.pv-feed-why { margin: 0; font-size: .85rem; color: var(--pv-muted); max-width: 88ch; }
.pv-feed-links { display: flex; flex-wrap: wrap; gap: 8px; }

@media (max-width: 860px) {
  /* The detail drops its indent and uses the full row here, so a tile
     taken out of flow would sit on top of the text. Back into the grid. */
  .pv-feed-summary { grid-template-columns: 40px 70px minmax(0, 1fr); gap: 10px; padding: 12px 12px; }
  .pv-feed-side { grid-column: 3; justify-self: start; }
  .pv-feed-detail { padding-left: 12px; }
  .pv-feed-entry[open] .pv-feed-score { position: static; width: auto; }
}

/* The feature cards are anchors now; keep them looking like cards. */
a.pv-feature-card { display: block; text-decoration: none; color: inherit; transition: border-color .15s ease, transform .15s ease; }
a.pv-feature-card:hover { border-color: var(--pv-blue); transform: translateY(-2px); }
a.pv-feature-card:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }

/* The four utilities the classic toolkit listed under its five workspaces. */
.pv-utility-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; margin-top: 14px; }
.pv-utility-link {
  display: grid; gap: 2px; text-decoration: none;
  padding: 12px 14px; border: 1px solid var(--pv-line); border-radius: 14px;
  background: var(--pv-panel); color: var(--pv-ink);
}
.pv-utility-link:hover { border-color: var(--pv-blue); }
.pv-utility-link:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }
.pv-utility-link b { font-size: .9rem; }
.pv-utility-link span { font-size: .8rem; color: var(--pv-muted); }

.pv-why-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; align-items: stretch; }
.pv-why-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; padding: 18px; box-shadow: var(--pv-shadow-sm); }
.pv-why-icon { width: 46px; height: 46px; border-radius: 14px; display: grid; place-items: center; font-size: 1.2rem; font-weight: 900; margin-bottom: 12px; }
.pv-why-icon.pv-purple-soft { background: var(--pv-purple-soft); color: var(--pv-purple); }

.pv-action-grid { display: grid; grid-template-columns: 1.3fr .7fr; gap: 18px; }
.pv-feature-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
.pv-feature-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; padding: 18px; box-shadow: var(--pv-shadow-sm); }
.pv-feature-card .pv-why-icon { margin-bottom: 10px; }
.pv-feature-card p { font-size: .88rem; color: var(--pv-muted); margin-bottom: 0; }
.pv-backlog-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px; padding: 20px; box-shadow: var(--pv-shadow-sm); }
.pv-backlog-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.pv-backlog-list { display: grid; gap: 10px; }
.pv-backlog-item { display: flex; justify-content: space-between; gap: 12px; font-size: .92rem; }
.pv-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 10px; position: relative; top: 1px; }
.pv-dot.pv-red { background: #e74a4a; }
.pv-dot.pv-orange { background: #f97316; }
.pv-dot.pv-amber { background: #f59e0b; }
.pv-dot.pv-amber-2 { background: #fbbf24; }
.pv-dot.pv-green { background: #22c55e; }
.pv-dot.pv-gray { background: #94a3b8; }
.pv-dot.pv-slate { background: #64748b; }

.pv-cta { padding: 26px 0 56px; }
.pv-cta-box {
  background: linear-gradient(135deg, var(--pv-cta-a), var(--pv-cta-b) 45%, var(--pv-cta-c) 100%);
  border: 1px solid var(--pv-cta-border); border-radius: 30px; padding: 34px; display: flex; justify-content: space-between; gap: 20px; align-items: center;
}
.pv-cta-actions { display: flex; gap: 12px; flex-wrap: wrap; }

.pv-footer { background: var(--pv-panel); border-top: 1px solid var(--pv-line); padding: 34px 0 24px; }
.pv-footer-top { display: flex; justify-content: space-between; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.pv-footer-brand img { width: 190px; margin-bottom: 8px; }
.pv-footer-links { display: flex; gap: 22px; flex-wrap: wrap; color: var(--pv-muted-2); font-size: .9rem; }
.pv-footer-bottom { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--pv-line); color: var(--pv-muted); font-size: .8rem; display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; }

/* Small text/spacing utilities, standing in for the mockup's one-off
   inline styles - each used in more than one place, or exactly matching
   an existing inline value, so a class earns its keep here. */
.pv-mb-0 { margin-bottom: 0; }
.pv-mb-8 { margin-bottom: 8px; }
.pv-mb-10 { margin-bottom: 10px; }
.pv-mb-16 { margin-bottom: 16px; }
.pv-mb-18 { margin-bottom: 18px; }
.pv-mt-18 { margin-top: 18px; }
.pv-mt-10 { margin-top: 10px; }
.pv-fw-700 { font-weight: 700; }
.pv-mono { font-family: "IBM Plex Mono", ui-monospace, monospace; }
.pv-text-blue { color: var(--pv-blue); }
.pv-text-red { color: var(--pv-red); }
.pv-num-orange { color: var(--pv-orange); }
.pv-lead-760 { max-width: 760px; margin-bottom: 0; }
.pv-lead-560 { max-width: 560px; margin-bottom: 0; }
.pv-row-between { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 8px; }
.pv-block-mb-10 { display: block; margin-bottom: 10px; }
.pv-h3-lg { font-size: 1.45rem; margin-bottom: 4px; }

/* Prose + data-table sections - Methodology, and anything else that is
   mostly reading rather than scanning cards. Kept separate from
   .pv-list-table (the compact mini-dashboard table) because these run
   full-width with more breathing room and a caption row. */
.pv-prose { max-width: 760px; }
.pv-table-card {
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px;
  padding: 6px 22px; box-shadow: var(--pv-shadow-sm); overflow-x: auto; margin: 18px 0;
}
.pv-datatable { width: 100%; border-collapse: collapse; font-size: .92rem; }
.pv-datatable th, .pv-datatable td { padding: 14px 10px; border-bottom: 1px solid var(--pv-line-2); text-align: left; vertical-align: top; }
.pv-datatable thead th { color: var(--pv-muted-2); font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid var(--pv-line); }
.pv-datatable tbody tr:last-child td { border-bottom: 0; }
.pv-datatable td.pv-nowrap, .pv-datatable th.pv-nowrap { white-space: nowrap; }
.pv-datatable td.pv-mono, .pv-datatable th.pv-mono { font-family: "IBM Plex Mono", ui-monospace, monospace; }

.pv-disclosure {
  background: var(--pv-panel-2); border: 1px solid var(--pv-line); border-radius: 16px;
  padding: 16px 20px; margin: 12px 0;
}
.pv-disclosure summary { font-weight: 800; cursor: pointer; }
.pv-disclosure p { font-size: .92rem; margin-top: 10px; }
.pv-disclosure p:first-of-type { margin-top: 12px; }

/* DNS Checker (/vulnerability-management/dns) per-domain cards - one <details> per domain
   in place of the classic page's 43-column table, same reasoning as that
   page's own domain-card component: a cell for everything and a way to
   read none of it helps nobody. Left border carries the tier the same way
   .pv-priority-feature's does (red = needs a look now), rather than a
   fourth ad-hoc color scheme for what is really the same "this needs
   attention" signal every other page on this site already has a color
   for. */
/* Subnet Calculator (/vulnerability-management/subnet) - the fact grid and boundary
   diagram. Ported from app.css's .fact-grid/.bounds-bar (see that file's
   comments for the reasoning - accent colour carries the grouping instead
   of a fourth heading, reserved ends read as unavailable rather than as
   severity so they deliberately don't use pv-red). data-pv-accent/
   data-pv-pct are this section's own attribute names rather than reusing
   the classic site's [data-accent]/[data-pct], which key off variables
   (--section-color, --pct) this stylesheet never defines. */
.pv-fact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin: 16px 0; }
.pv-fact { background: var(--pv-panel-2); border: 1px solid var(--pv-line); border-left: 3px solid var(--pv-line); border-radius: 0 12px 12px 0; padding: 10px 14px; }
.pv-fact[data-pv-accent="a1"] { border-left-color: var(--pv-blue); }
.pv-fact[data-pv-accent="a2"] { border-left-color: var(--pv-orange); }
.pv-fact[data-pv-accent="a3"] { border-left-color: var(--pv-green); }
.pv-fact[data-pv-accent="a4"] { border-left-color: #8a63d2; }
.pv-fact-label { display: block; color: var(--pv-muted-2); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; margin-bottom: 4px; }
.pv-fact-value { display: block; font-size: 1.05rem; font-weight: 800; word-break: break-word; }
.pv-fact-note { display: block; color: var(--pv-muted); font-size: .76rem; margin-top: 3px; }

.pv-bounds { margin: 18px 0 0; }
.pv-bounds-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 8px; }
.pv-bounds-tick { position: relative; height: 18px; }
.pv-bounds-tick span { position: absolute; left: var(--pv-pct, 0%); transform: translateX(-50%); white-space: nowrap; font-size: .78rem; }
.pv-bounds-bar { position: relative; display: flex; height: 30px; border-radius: 10px; overflow: hidden; background: var(--pv-panel-2); }
.pv-bounds-seg { display: block; height: 100%; width: var(--pv-pct, 0%); }
.pv-bounds-seg.pv-is-reserved { background: var(--pv-chip-bg); }
.pv-bounds-seg.pv-is-usable { background: var(--pv-blue-soft-bg); }
.pv-bounds-legend { display: flex; justify-content: space-between; gap: 10px; margin-top: 8px; }
.pv-bounds-legend span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pv-bounds-right { text-align: right; align-items: flex-end; }
.pv-input-prefix { display: flex; gap: 8px; }
.pv-input-prefix .pv-input { flex: 1; }
.pv-prefix-select { flex: 0 0 auto; width: auto; }
@media (max-width: 620px) {
  .pv-bounds-legend { flex-wrap: wrap; }
}

.pv-domain-cards { list-style: none; margin: 0; padding: 0; }
.pv-domain-card { border-left: 5px solid var(--pv-line); border-radius: 16px; margin: 10px 0; overflow: hidden; }
.pv-domain-card.pv-tier-attention { border-left-color: var(--pv-red); }
.pv-domain-card.pv-tier-look { border-left-color: var(--pv-orange); }
.pv-domain-card > details { background: var(--pv-panel); border: 1px solid var(--pv-line); border-left: none; border-radius: 0 16px 16px 0; }
.pv-domain-summary {
  cursor: pointer; padding: 14px 18px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  list-style: none;
}
.pv-domain-summary::-webkit-details-marker { display: none; }
.pv-domain-summary .pv-domain-name { font-weight: 800; }
.pv-domain-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.pv-domain-body { padding: 0 18px 18px; }
.pv-domain-findings { margin: 0 0 12px; padding-left: 18px; font-size: .88rem; }
.pv-domain-groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.pv-domain-group h4 { font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--pv-muted-2); margin-bottom: 6px; }
.pv-domain-group dt { color: var(--pv-muted-2); font-size: .78rem; margin-top: 8px; }
.pv-domain-group dt:first-child { margin-top: 0; }
.pv-domain-group dd { margin: 2px 0 0; font-size: .9rem; word-break: break-word; }

/* Static/legal pages (About, Privacy, Contact, Security, Terms, Notices) -
   plain reading content that otherwise reuses .pv-panel/.pv-prose/
   .pv-datatable/.pv-disclosure/.pv-impact-list above. These two are the
   only genuinely new shapes those pages need: a labeled field list for
   the Notices page's per-source metadata, and a quoted block for the
   license text some of those sources require be reproduced verbatim. */
.pv-field-list { margin: 10px 0 0; }
.pv-field-list dt { color: var(--pv-muted-2); font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; margin-top: 10px; }
.pv-field-list dt:first-child { margin-top: 0; }
.pv-field-list dd { margin: 2px 0 0; }
.pv-quote {
  background: var(--pv-panel-2); border-left: 3px solid var(--pv-line); border-radius: 0 12px 12px 0;
  padding: 12px 16px; margin: 10px 0 0; font-size: .88rem; white-space: pre-line;
}

/* Investigate (/vulnerability-management/investigate) - the search form, the starting-
   points list, the result rows, and the reused Watchlist. The Watchlist's
   ids and classes (#wl-panel, #wl-list, .wl-save-btn, .wl-item, ...) are
   not this section's to rename: static/js/watchlist.js queries them by
   exact name, unmodified, against the same real localStorage key search.html
   uses - only their look is new here. */
.pv-note-small { font-size: .86rem; }
.pv-search-form { margin: 18px 0 6px; }
.pv-search-row { display: flex; gap: 12px; flex-wrap: wrap; }
.pv-input {
  flex: 1 1 320px; min-height: 52px; padding: 0 18px; border-radius: 12px;
  border: 1px solid var(--pv-line); background: var(--pv-panel); font: inherit; color: var(--pv-ink);
}
.pv-input:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 1px; }

.pv-note { border-radius: 14px; padding: 12px 16px; font-size: .9rem; margin: 12px 0; }
.pv-note-warn { background: var(--pv-orange-soft); border: 1px solid var(--pv-warn-border); color: var(--pv-warn-text); }
.pv-note-muted { background: var(--pv-panel-2); border: 1px solid var(--pv-line); color: var(--pv-muted); }

.pv-start-list { list-style: none; padding: 0; margin: 14px 0 0; display: grid; gap: 10px; }
.pv-start-item {
  display: flex; align-items: center; gap: 16px; background: var(--pv-panel);
  border: 1px solid var(--pv-line); border-radius: 16px; padding: 12px 16px; box-shadow: var(--pv-shadow-sm);
}
.pv-start-item .pv-risk-badge { min-width: 46px; padding: 8px 6px; font-size: .92rem; }
.pv-start-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pv-start-cve { font-weight: 800; color: var(--pv-blue); }
.pv-start-title { color: var(--pv-muted); font-size: .88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.pv-kpi-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin: 16px 0; }
.pv-kpi-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 16px; padding: 14px 16px; box-shadow: var(--pv-shadow-sm); }
.pv-kpi-value { font-size: 1.7rem; font-weight: 900; letter-spacing: -.03em; display: block; }
.pv-kpi-label { color: var(--pv-muted); font-size: .82rem; font-weight: 700; }
.pv-kpi-sub { display: block; color: var(--pv-muted); font-size: .72rem; }

.pv-result-list { list-style: none; padding: 0; margin: 18px 0; display: grid; gap: 12px; }
.pv-result-row { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; overflow: hidden; box-shadow: var(--pv-shadow-sm); }
.pv-result-row.pv-is-kev { border-color: var(--pv-feature-border); }
.pv-result-summary {
  display: flex; align-items: center; gap: 16px; padding: 14px 18px; cursor: pointer; list-style: none;
}
.pv-result-summary::-webkit-details-marker { display: none; }
.pv-result-score-mini {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 62px; padding: 8px 4px; border-radius: 14px; color: #fff; font-weight: 900; flex: 0 0 auto;
}
.pv-result-score-mini b { font-size: 1.25rem; line-height: 1; }
.pv-result-score-mini span { font-size: .64rem; text-transform: uppercase; letter-spacing: .04em; text-align: center; }
.pv-result-score-mini.pv-unscored { background: var(--pv-chip-bg); color: var(--pv-muted-2); }
.pv-result-main { flex: 1; min-width: 0; }
.pv-result-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 4px; }
.pv-result-title { color: var(--pv-muted); font-size: .9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-result-side { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.pv-result-expand {
  width: 9px; height: 9px; border-right: 2px solid var(--pv-muted); border-bottom: 2px solid var(--pv-muted);
  transform: rotate(45deg); transition: transform .2s ease; flex: 0 0 auto;
}
details[open] > .pv-result-summary .pv-result-expand { transform: rotate(225deg); }

.pv-result-body { padding: 4px 18px 22px; border-top: 1px solid var(--pv-line-2); }
.pv-result-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }
.pv-result-content p { font-size: .92rem; }
.pv-impact-list { margin: 8px 0; padding-left: 18px; font-size: .9rem; }
.pv-search-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 14px 0; }
.pv-source-chip {
  display: inline-flex; align-items: center; gap: 4px; font-size: .8rem; font-weight: 800;
  color: var(--pv-blue); background: var(--pv-blue-soft-bg); border-radius: 10px; padding: 8px 12px;
}
.pv-pager { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin: 18px 0; }
.pv-pager ul { display: flex; gap: 10px; list-style: none; padding: 0; margin: 0; }

/* --- Watchlist: JS-owned ids/classes below, styled but not renamed. --- */
.wl-save-btn {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px;
  border-radius: 12px; font-weight: 800; border: 1px solid var(--pv-accent-border); background: var(--pv-panel); color: var(--pv-blue);
  cursor: pointer;
}
.wl-save-btn.is-saved { background: var(--pv-blue); color: #fff; border-color: var(--pv-blue); }
#wl-panel {
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px;
  padding: 4px 22px; box-shadow: var(--pv-shadow-sm); margin: 22px 0;
}
#wl-panel summary { font-weight: 800; cursor: pointer; padding: 16px 0; }
.watchlist-head { display: flex; justify-content: flex-end; margin-bottom: 4px; }
.watchlist-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.wl-export-btn {
  min-height: 40px; padding: 0 14px; border-radius: 10px; font-weight: 700; font-size: .86rem;
  border: 1px solid var(--pv-line); background: var(--pv-panel); color: var(--pv-ink); cursor: pointer;
}
.wl-export-btn:disabled { opacity: .5; cursor: not-allowed; }
.wl-list { list-style: none; padding: 0; margin: 12px 0 18px; display: grid; gap: 10px; }
.wl-item {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  background: var(--pv-panel-2); border: 1px solid var(--pv-line-2); border-radius: 14px; padding: 12px 14px;
}
.wl-item-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wl-item-cve { font-weight: 800; color: var(--pv-blue); }
.wl-item-meta { color: var(--pv-muted); font-size: .82rem; }
.wl-item-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.wl-icon-btn {
  min-height: 34px; padding: 0 12px; border-radius: 9px; font-size: .8rem; font-weight: 700;
  border: 1px solid var(--pv-line); background: var(--pv-panel); color: var(--pv-ink); cursor: pointer;
}

/* Compare (/vulnerability-management/compare) - a hub linking out to the three real
   vendor-research tools, each still filterable and exportable only on
   its own real page (see webui/routes/redesign.py's redesign_compare). */
.pv-tool-section { margin-bottom: 30px; }
.pv-tool-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.pv-tool-head h2 { margin-bottom: 2px; }
.pv-vendor-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.pv-vendor-card {
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 16px; padding: 16px;
  box-shadow: var(--pv-shadow-sm); display: block;
}
.pv-vendor-name { font-weight: 800; display: block; margin-bottom: 4px; }
.pv-vendor-tagline { color: var(--pv-muted); font-size: .86rem; line-height: 1.5; }
.pv-tool-more { font-weight: 800; color: var(--pv-blue); white-space: nowrap; }

@media (max-width: 860px) {
  .pv-kpi-row { grid-template-columns: repeat(2, 1fr); }
  .pv-result-summary { flex-wrap: wrap; }
  .pv-vendor-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .pv-table-card { padding: 4px 14px; }
  .pv-datatable th, .pv-datatable td { padding: 10px 8px; font-size: .86rem; }
  .pv-kpi-row { grid-template-columns: 1fr; }
  #wl-panel { padding: 4px 16px; }
}

@media (max-width: 1120px) {
  .pv-hero-grid, .pv-action-grid { grid-template-columns: 1fr; }
  .pv-feature-grid { grid-template-columns: repeat(3, 1fr); }
  .pv-why-grid { grid-template-columns: repeat(3, 1fr); }
  .pv-stats-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 860px) {
  .pv-navlinks { display: none; }
  .pv-nav { height: auto; padding: 14px 0; flex-wrap: wrap; }
  .pv-brand img { width: 185px; }
  .pv-feature-grid, .pv-why-grid { grid-template-columns: repeat(2, 1fr); }
  .pv-mini-shell { grid-template-columns: 1fr; }
  .pv-mini-side { border-right: 0; border-bottom: 1px solid var(--pv-line-2); display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .pv-priority-row { grid-template-columns: 50px 1fr; }
  .pv-priority-row > :nth-child(n+3) { justify-self: start; }
  .pv-backlog-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .pv-container { width: min(var(--pv-max), calc(100% - 22px)); }
  .pv-nav-actions { width: 100%; }
  .pv-nav-actions .pv-btn { flex: 1; }
  .pv-stats-grid, .pv-feature-grid, .pv-why-grid { grid-template-columns: 1fr; }
  .pv-cta-box, .pv-footer-top, .pv-footer-bottom { flex-direction: column; align-items: flex-start; }
  .pv-hero { padding-top: 34px; }
}

/* Analyze (/vulnerability-management/analyze) - a 0-100 discrete data-pv-pct attribute
   set on each bar-fill span, the same technique app.css uses for its own
   chart bars (data-pct): CSS cannot read a percentage out of an inline
   style attribute the site's CSP already forbids, so every whole percent
   gets its own rule setting a custom property, and .pv-bar-fill reads it. */
[data-pv-pct="0"] { --pv-pct: 0%; }
[data-pv-pct="1"] { --pv-pct: 1%; }
[data-pv-pct="2"] { --pv-pct: 2%; }
[data-pv-pct="3"] { --pv-pct: 3%; }
[data-pv-pct="4"] { --pv-pct: 4%; }
[data-pv-pct="5"] { --pv-pct: 5%; }
[data-pv-pct="6"] { --pv-pct: 6%; }
[data-pv-pct="7"] { --pv-pct: 7%; }
[data-pv-pct="8"] { --pv-pct: 8%; }
[data-pv-pct="9"] { --pv-pct: 9%; }
[data-pv-pct="10"] { --pv-pct: 10%; }
[data-pv-pct="11"] { --pv-pct: 11%; }
[data-pv-pct="12"] { --pv-pct: 12%; }
[data-pv-pct="13"] { --pv-pct: 13%; }
[data-pv-pct="14"] { --pv-pct: 14%; }
[data-pv-pct="15"] { --pv-pct: 15%; }
[data-pv-pct="16"] { --pv-pct: 16%; }
[data-pv-pct="17"] { --pv-pct: 17%; }
[data-pv-pct="18"] { --pv-pct: 18%; }
[data-pv-pct="19"] { --pv-pct: 19%; }
[data-pv-pct="20"] { --pv-pct: 20%; }
[data-pv-pct="21"] { --pv-pct: 21%; }
[data-pv-pct="22"] { --pv-pct: 22%; }
[data-pv-pct="23"] { --pv-pct: 23%; }
[data-pv-pct="24"] { --pv-pct: 24%; }
[data-pv-pct="25"] { --pv-pct: 25%; }
[data-pv-pct="26"] { --pv-pct: 26%; }
[data-pv-pct="27"] { --pv-pct: 27%; }
[data-pv-pct="28"] { --pv-pct: 28%; }
[data-pv-pct="29"] { --pv-pct: 29%; }
[data-pv-pct="30"] { --pv-pct: 30%; }
[data-pv-pct="31"] { --pv-pct: 31%; }
[data-pv-pct="32"] { --pv-pct: 32%; }
[data-pv-pct="33"] { --pv-pct: 33%; }
[data-pv-pct="34"] { --pv-pct: 34%; }
[data-pv-pct="35"] { --pv-pct: 35%; }
[data-pv-pct="36"] { --pv-pct: 36%; }
[data-pv-pct="37"] { --pv-pct: 37%; }
[data-pv-pct="38"] { --pv-pct: 38%; }
[data-pv-pct="39"] { --pv-pct: 39%; }
[data-pv-pct="40"] { --pv-pct: 40%; }
[data-pv-pct="41"] { --pv-pct: 41%; }
[data-pv-pct="42"] { --pv-pct: 42%; }
[data-pv-pct="43"] { --pv-pct: 43%; }
[data-pv-pct="44"] { --pv-pct: 44%; }
[data-pv-pct="45"] { --pv-pct: 45%; }
[data-pv-pct="46"] { --pv-pct: 46%; }
[data-pv-pct="47"] { --pv-pct: 47%; }
[data-pv-pct="48"] { --pv-pct: 48%; }
[data-pv-pct="49"] { --pv-pct: 49%; }
[data-pv-pct="50"] { --pv-pct: 50%; }
[data-pv-pct="51"] { --pv-pct: 51%; }
[data-pv-pct="52"] { --pv-pct: 52%; }
[data-pv-pct="53"] { --pv-pct: 53%; }
[data-pv-pct="54"] { --pv-pct: 54%; }
[data-pv-pct="55"] { --pv-pct: 55%; }
[data-pv-pct="56"] { --pv-pct: 56%; }
[data-pv-pct="57"] { --pv-pct: 57%; }
[data-pv-pct="58"] { --pv-pct: 58%; }
[data-pv-pct="59"] { --pv-pct: 59%; }
[data-pv-pct="60"] { --pv-pct: 60%; }
[data-pv-pct="61"] { --pv-pct: 61%; }
[data-pv-pct="62"] { --pv-pct: 62%; }
[data-pv-pct="63"] { --pv-pct: 63%; }
[data-pv-pct="64"] { --pv-pct: 64%; }
[data-pv-pct="65"] { --pv-pct: 65%; }
[data-pv-pct="66"] { --pv-pct: 66%; }
[data-pv-pct="67"] { --pv-pct: 67%; }
[data-pv-pct="68"] { --pv-pct: 68%; }
[data-pv-pct="69"] { --pv-pct: 69%; }
[data-pv-pct="70"] { --pv-pct: 70%; }
[data-pv-pct="71"] { --pv-pct: 71%; }
[data-pv-pct="72"] { --pv-pct: 72%; }
[data-pv-pct="73"] { --pv-pct: 73%; }
[data-pv-pct="74"] { --pv-pct: 74%; }
[data-pv-pct="75"] { --pv-pct: 75%; }
[data-pv-pct="76"] { --pv-pct: 76%; }
[data-pv-pct="77"] { --pv-pct: 77%; }
[data-pv-pct="78"] { --pv-pct: 78%; }
[data-pv-pct="79"] { --pv-pct: 79%; }
[data-pv-pct="80"] { --pv-pct: 80%; }
[data-pv-pct="81"] { --pv-pct: 81%; }
[data-pv-pct="82"] { --pv-pct: 82%; }
[data-pv-pct="83"] { --pv-pct: 83%; }
[data-pv-pct="84"] { --pv-pct: 84%; }
[data-pv-pct="85"] { --pv-pct: 85%; }
[data-pv-pct="86"] { --pv-pct: 86%; }
[data-pv-pct="87"] { --pv-pct: 87%; }
[data-pv-pct="88"] { --pv-pct: 88%; }
[data-pv-pct="89"] { --pv-pct: 89%; }
[data-pv-pct="90"] { --pv-pct: 90%; }
[data-pv-pct="91"] { --pv-pct: 91%; }
[data-pv-pct="92"] { --pv-pct: 92%; }
[data-pv-pct="93"] { --pv-pct: 93%; }
[data-pv-pct="94"] { --pv-pct: 94%; }
[data-pv-pct="95"] { --pv-pct: 95%; }
[data-pv-pct="96"] { --pv-pct: 96%; }
[data-pv-pct="97"] { --pv-pct: 97%; }
[data-pv-pct="98"] { --pv-pct: 98%; }
[data-pv-pct="99"] { --pv-pct: 99%; }
[data-pv-pct="100"] { --pv-pct: 100%; }

.pv-bar-chart { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; padding: 18px; box-shadow: var(--pv-shadow-sm); }
.pv-bar-chart h3 { font-size: 1rem; margin-bottom: 12px; }
.pv-bars { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.pv-bar-row { display: grid; grid-template-columns: 140px 1fr 56px; gap: 10px; align-items: center; font-size: .84rem; }
.pv-bar-label { color: var(--pv-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-bar-track { background: var(--pv-panel-2); border-radius: 999px; height: 10px; overflow: hidden; }
.pv-bar-fill { display: block; height: 100%; width: var(--pv-pct, 0%); background: var(--pv-blue); border-radius: 999px; }
.pv-bar-fill.pv-red { background: var(--pv-red); }
.pv-bar-fill.pv-orange { background: var(--pv-orange); }
.pv-bar-value { text-align: right; font-weight: 800; }

/* A bar row that is a link. .pv-bar-row is already a grid, so it only
   needs the anchor defaults removing and a hover that says it is one. */
a.pv-bar-row { text-decoration: none; color: inherit; border-radius: 8px; padding: 2px 4px; margin: -2px -4px; }
a.pv-bar-row:hover { background: var(--pv-panel-2); }
a.pv-bar-row:hover .pv-bar-label { color: var(--pv-blue); }
a.pv-bar-row:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 1px; }

/* The section navigator. Anchors within one long document, not tabs -
   the page is one document and a control that looked like a tab would
   promise panes it does not have. */
.pv-section-nav { display: flex; flex-wrap: wrap; gap: 8px; }
.pv-section-nav a {
  font-size: .8rem; font-weight: 700; text-decoration: none;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--pv-line); background: var(--pv-panel);
  color: var(--pv-ink);
}
.pv-section-nav a:hover { border-color: var(--pv-blue); color: var(--pv-blue); }
.pv-section-nav a:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }
.pv-chart-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin: 16px 0; }
.pv-chip-line { display: flex; flex-wrap: wrap; gap: 8px; }

.pv-progress { background: var(--pv-panel-2); border-radius: 999px; height: 14px; overflow: hidden; margin: 14px 0 6px; }
.pv-progress-fill { display: block; height: 100%; width: var(--pv-pct, 0%); background: linear-gradient(90deg, var(--pv-blue), var(--pv-blue-2)); border-radius: 999px; }

.pv-upload-grid { display: grid; grid-template-columns: 1.2fr .9fr; gap: 24px; align-items: start; margin: 18px 0; }
.pv-file-input {
  display: block; width: 100%; padding: 12px 14px; border-radius: 12px; border: 1px dashed var(--pv-line);
  background: var(--pv-panel); font: inherit; color: var(--pv-ink); margin-bottom: 10px;
}
.pv-settings-note { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 18px; padding: 18px; box-shadow: var(--pv-shadow-sm); }
.pv-settings-note p { font-size: .86rem; }
.pv-settings-note p:first-child { margin-top: 0; }

.pv-downloads { display: flex; flex-wrap: wrap; gap: 12px; margin: 14px 0; }

.pv-finding-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px; padding: 20px; box-shadow: var(--pv-shadow-sm); margin-bottom: 14px; }
.pv-finding-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; flex-wrap: wrap; margin-bottom: 6px; }
.pv-finding-id { display: flex; align-items: center; gap: 12px; }
.pv-finding-rank { font-weight: 900; color: var(--pv-navy); background: var(--pv-panel-2); border-radius: 10px; padding: 6px 12px; }
.pv-finding-product { font-weight: 800; font-size: 1.05rem; }
.pv-finding-flags { display: flex; gap: 6px; flex-wrap: wrap; }
.pv-finding-fix { color: var(--pv-muted); margin-bottom: 12px; }
.pv-finding-body { display: grid; grid-template-columns: .8fr 1.5fr; gap: 20px; }
.pv-finding-meta .pv-chip { margin: 0 6px 6px 0; display: inline-flex; }
.pv-finding-why { font-size: .9rem; }

.pv-tag-table-wrap { overflow-x: auto; margin: 14px 0; }
.pv-tag-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.pv-tag-table th, .pv-tag-table td { padding: 10px 8px; border-bottom: 1px solid var(--pv-line-2); text-align: left; }
.pv-tag-table thead th { color: var(--pv-muted-2); font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; }
.pv-select {
  padding: 8px 10px; border-radius: 8px; border: 1px solid var(--pv-line); background: var(--pv-panel); font: inherit; color: var(--pv-ink);
}
.pv-checkbox { width: 18px; height: 18px; }

.pv-filter-bar { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; margin: 14px 0; background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 16px; padding: 16px; }
.pv-filter-group { display: flex; flex-direction: column; gap: 6px; }
.pv-filter-group.pv-grow { flex: 1; min-width: 200px; }
.pv-filter-label { font-size: .78rem; font-weight: 800; color: var(--pv-muted); text-transform: uppercase; letter-spacing: .04em; }
.pv-sev-toggles { display: flex; flex-wrap: wrap; gap: 12px; }
.pv-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: .86rem; font-weight: 700; }

/* Priority Feed's item-count chooser (Top 50/100/200) plus its CSV
   export link, 2026-09-07 - segmented pills, not the checkbox-label rows
   .pv-toggle above is for, hence the separate name. */
.pv-count-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.pv-count-toggle {
  display: inline-flex; align-items: center;
  font-size: .82rem; font-weight: 700;
  padding: 6px 12px;
  border: 1px solid var(--pv-line);
  border-radius: 999px;
  background: var(--pv-panel);
  color: var(--pv-muted);
}
.pv-count-toggle.is-on { background: var(--pv-blue); border-color: var(--pv-blue); color: #fff; }
.pv-count-toggle:hover { border-color: var(--pv-blue); }
.pv-count-export { background: var(--pv-panel-2); }

@media (max-width: 860px) {
  .pv-upload-grid, .pv-finding-body, .pv-chart-grid { grid-template-columns: 1fr; }
  .pv-bar-row { grid-template-columns: 90px 1fr 44px; }
}

/* Analyze - a few small utility classes the ready-stage markup needs that
   no earlier redesign page happened to. */
.pv-panel { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 20px; padding: 28px 32px; box-shadow: var(--pv-shadow-sm); margin-bottom: 22px; }
.pv-panel h2 { font-size: 1.3rem; margin-bottom: 10px; }
.pv-section { padding-bottom: 40px; }
.pv-section > section { margin-bottom: 28px; }
.pv-lead-sm { font-size: .95rem; margin-bottom: 6px; }
.pv-bullet-list { list-style: disc; padding-left: 22px; display: grid; gap: 6px; }
.pv-chip-warn { background: var(--pv-red-soft); border-color: var(--pv-feature-border); color: var(--pv-red); }
.pv-inline-head { display: inline; }
.pv-mb-12 { margin-bottom: 12px; }
.pv-input-wide { width: 100%; }
.pv-kpi-card-fit { width: fit-content; }
.pv-kpi-suffix { font-size: .5em; }

/* The "back to classic" link in the redesign header - flex child
   alongside the brand, the nav links and the theme toggle, so
   space-between already places it at the right without a margin hack. */
.pv-classic-link { font-size: .82rem; font-weight: 700; color: var(--pv-muted-2); white-space: nowrap; }
.pv-classic-link:hover { color: var(--pv-blue); }
@media (max-width: 900px) {
  .pv-classic-link { display: none; }
}

/* Light/dark switch (redesign/_shell.html, static/js/pv-theme-toggle.js).
   The mark is a text glyph swapped by data-theme rather than two <img>s or
   an inline SVG with a fill="" the CSP's img-src/style-src would not let
   JS touch anyway - a content: swap is themeable from this stylesheet
   alone, no script beyond flipping the attribute. */
.pv-theme-toggle {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid var(--pv-line); background: var(--pv-panel);
  display: grid; place-items: center; cursor: pointer;
  transition: border-color .2s ease, background .2s ease;
}
.pv-theme-toggle:hover { border-color: var(--pv-blue); }
.pv-theme-toggle:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }
.pv-theme-toggle-mark { font-size: 1rem; line-height: 1; }
/* Moon shown in light mode - click to go dark; sun shown in dark mode -
   click to go back to light. The mode the click leads to, not the one
   you're in. */
.pv-theme-toggle-mark:before { content: "\1F319"; }
.pv-page[data-theme="dark"] .pv-theme-toggle-mark:before { content: "\2600"; }

/* Compare rebuild (/vulnerability-management/compare/pentest, /vuln-mgmt, /patch-mgmt) -
   2026-09-07. Full-parity filterable, one-profile-at-a-time vendor
   comparison across three tools, same data and business logic
   (webui/vendor_view.py, webui/routes/compare.py) as the classic
   /compare/* pages, rendered in this vocabulary instead of app.css's. */
.pv-check-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px 14px; margin-top: 8px; }
.pv-check-grid label { display: flex; align-items: center; gap: 8px; font-size: .86rem; }
.pv-table-shell { margin: 14px 0; }
.pv-cell-prose { display: inline-block; max-width: 32ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.pv-scroll-hint { font-size: .76rem; color: var(--pv-muted-2); margin: 6px 2px 0; }
.pv-badge { display: inline-flex; align-items: center; font-size: .74rem; font-weight: 700; padding: 3px 9px; border-radius: 999px; background: var(--pv-panel-2); color: var(--pv-muted); border: 1px solid var(--pv-line); }
.pv-badge-yes { background: var(--pv-green-soft); color: var(--pv-green); border-color: transparent; }
.pv-result-count { font-size: .86rem; color: var(--pv-muted); margin-top: 8px; }
.pv-vendor-picker-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.pv-vendor-toggle { display: inline-flex; align-items: center; font-size: .82rem; font-weight: 700; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--pv-line); background: var(--pv-panel); color: var(--pv-muted); }
.pv-vendor-toggle.is-on { background: var(--pv-blue); border-color: var(--pv-blue); color: #fff; }
.pv-vendor-toggle:hover { border-color: var(--pv-blue); }
.pv-profile-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; list-style: none; margin: 14px 0; padding: 0; }
.pv-profile-kpis li { background: var(--pv-panel-2); border-radius: 12px; padding: 10px 12px; }
.pv-profile-kpi-value { display: block; font-size: 1.05rem; font-weight: 800; }
.pv-profile-kpi-label { font-size: .74rem; color: var(--pv-muted-2); }
.pv-profile-fields { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; font-size: .88rem; margin: 14px 0; }
.pv-profile-fields dt { color: var(--pv-muted); white-space: nowrap; }
.pv-profile-fields dd { margin: 0; }
.pv-profile-note { display: block; font-size: .78rem; color: var(--pv-muted-2); }
.pv-source-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0; }
.pv-source-chip { display: inline-flex; align-items: center; gap: 4px; font-size: .78rem; font-weight: 700; padding: 5px 10px; border-radius: 999px; background: var(--pv-chip-bg); color: var(--pv-chip-text); }
.pv-guidance-list { margin: 8px 0 14px; padding-left: 20px; font-size: .9rem; }
.pv-guidance-list li { margin: 4px 0; }
.pv-scenario-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin: 14px 0; }
.pv-scenario-card { background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 16px; padding: 16px 18px; }
.pv-scenario-card h3 { font-size: .98rem; margin-bottom: 8px; }
.pv-qlist { counter-reset: pvq; display: grid; gap: 10px; margin: 10px 0; }
.pv-qlist .pv-q { counter-increment: pvq; display: grid; grid-template-columns: 30px 1fr; gap: 10px; padding: 12px 14px; background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 12px; }
.pv-qlist .pv-q::before { content: counter(pvq, decimal-leading-zero); font-weight: 800; color: var(--pv-blue); }
.pv-qlist .pv-q span b { font-weight: 700; }
.pv-profile-name { font-size: 1.15rem; margin-bottom: 2px; }
.pv-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Back to top. Fixed bottom-right, hidden until the reader is past a
   screenful - see back-to-top.js for why 600px.

   Hidden with visibility rather than display:none so it can fade, and so
   it is out of the tab order while invisible: a control nobody can see
   should not be the next thing the keyboard lands on.

   Bottom-left is where a "skip to content" link would go and where a
   cookie banner usually sits; bottom-right is the convention and, on a
   phone, the thumb. It sits above the footer rather than inside it, so it
   is reachable from the middle of a 111KB page, which is the only place
   it is needed. */
.pv-to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--pv-line);
  background: var(--pv-panel);
  color: var(--pv-ink);
  font-size: 1.1rem;
  font-weight: 800;
  text-decoration: none;
  box-shadow: var(--pv-shadow-sm);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.pv-to-top.is-visible { opacity: 1; visibility: visible; transform: none; }
.pv-to-top:hover { border-color: var(--pv-blue); color: var(--pv-blue); }
.pv-to-top:focus-visible { outline: 2px solid var(--pv-blue); outline-offset: 2px; }

/* 44px is the smallest comfortable touch target; on a phone give it a
   little more room from the edge so it does not sit under the browser's
   own chrome. */
@media (max-width: 860px) {
  .pv-to-top { right: 14px; bottom: 16px; }
}

/* Someone who has asked for less motion gets the control appearing
   without the slide, and back-to-top.js leaves the jump instant for
   them too. */
@media (prefers-reduced-motion: reduce) {
  .pv-to-top { transition: none; transform: none; }
}
