:root {
  color-scheme: dark;

  /* Surfaces: a cool, near-neutral slate. Low chroma keeps the severity
     colours — the only thing that should shout — legible against it. */
  --bg: #080b11;
  --panel: #10151e;
  --panel-soft: #161d28;
  --line: #1e2733;
  --line-strong: #2c3847;
  --text: #e7ecf3;
  --muted: #8593a6;
  --dim: #5d6b7d;

  /* One accent, an indigo-blue that reads as instrumentation rather than
     decoration. Everything accent-tinted derives from --accent-rgb. */
  --accent-rgb: 82, 130, 246;
  --accent: rgb(var(--accent-rgb));
  --accent-hi: #7ba0f9;
  --accent-hover: #6b93f8;
  --accent-press: #3f6be0;
  --accent-deep: #3159c4;
  --accent-100: #dbe5fe;
  --accent-200: #b4cafc;
  --accent-link: #9cbafb;
  --accent-link-hover: #c3d6fd;
  --accent-soft: rgba(var(--accent-rgb), .14);

  /* Status. Desaturated a step from the neon defaults so a critical finding
     looks urgent instead of the whole page looking urgent. */
  --green: #35c98b;
  --yellow: #e8b23c;
  --orange: #ef8744;
  --red: #ef5a5a;
  --blue: #5b9df7;
  --cyan: #38bdf8;

  --content-width: 1500px;
  --secondary: #1c2532;
  --secondary-hover: #263243;
  --ring: rgba(var(--accent-rgb), .55);
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
  --shadow-md: 0 12px 28px rgba(0, 0, 0, .3);
  --ease: cubic-bezier(.4, 0, .2, 1);
  --speed: 160ms;
  --speed-slow: 260ms;
}

* { box-sizing: border-box; }
/* A class-level `display` outranks the UA stylesheet's [hidden] rule, so any
   component styled with display:grid/flex stays on screen when scripts hide it.
   That is how the download progress bar and the password strength meter both
   rendered at rest. Settle it once, here. */
[hidden] { display: none !important; }
/* Full-bleed bands are 100vw inside a centred column, so the sliver they overhang
   by has to be clipped or the page scrolls sideways.

   The clip belongs on <html>, not <body>. On the root element overflow propagates
   to the viewport and the root itself paints as `visible`, so nothing is left
   holding a clip. Put it on <body> and <body> becomes a clipping box that eats
   its own fixed-position children — which is why the back-to-top button never
   appeared no matter what the script did to it. `clip` rather than `hidden`
   throughout: hidden would make the viewport a scroll container on both axes and
   break every sticky rail. */
/* `scrollbar-gutter` reserves the vertical scrollbar's strip whether or not there
   is one to draw. Without it a tall tab and a short tab lay out against viewports
   ~15px apart, and every switch between them nudged the whole console sideways. */
html { scroll-behavior: smooth; overflow-x: clip; scrollbar-gutter: stable; }
body {
  margin: 0;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background:
    radial-gradient(900px 520px at 50% -8%, rgba(var(--accent-rgb), .1), transparent 70%),
    linear-gradient(180deg, #10151f 0, var(--bg) 46%) fixed;
  color: var(--text);
  font: 14px/1.55 Inter, ui-sans-serif, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: var(--accent-link); text-underline-offset: 3px; transition: color var(--speed) var(--ease); }
a:hover { color: var(--accent-link-hover); }
code, pre { font-family: "Cascadia Code", Consolas, monospace; }
main { flex: 1 0 auto; width: min(var(--content-width), calc(100% - 32px)); margin: 24px auto 72px; }

:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---------- motion ---------- */
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes reveal { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes pop-in { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: none; } }

/* Scroll-revealed sections opt out: a filled animation would pin opacity to 1
   and defeat the reveal's starting state. */
main > *:not([data-reveal]) { animation: rise var(--speed-slow) var(--ease) both; }
details[open] > *:not(summary) { animation: reveal 220ms var(--ease); }

/* Scroll reveals. Scoped to .js-ready so the content is never stranded
   invisible when the script does not run. Reversible: scrolling back up returns
   each element to its starting position, so the page reads the same in both
   directions rather than going flat once seen. */
.js-ready [data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  will-change: opacity, transform;
}
.js-ready [data-reveal="left"] { transform: translateX(-38px); }
.js-ready [data-reveal="right"] { transform: translateX(38px); }
.js-ready [data-reveal="zoom"] { transform: scale(.94); }
.js-ready [data-reveal="rise"] { transform: translateY(44px); }
.js-ready [data-reveal].is-visible { opacity: 1; transform: none; }
.js-ready [data-reveal-delay="1"] { transition-delay: 80ms; }
.js-ready [data-reveal-delay="2"] { transition-delay: 160ms; }
.js-ready [data-reveal-delay="3"] { transition-delay: 240ms; }
.js-ready [data-reveal-delay="4"] { transition-delay: 320ms; }
.js-ready [data-reveal-delay="5"] { transition-delay: 400ms; }

/* A side-in reveal only makes sense while the thing it reveals sits beside
   something else. Once the showcase folds to a single column each half is already
   the full width of the page, so translating it 38px to the right simply hangs it
   over the edge — which is the horizontal scrollbar that appeared on phones. Below
   the fold-point they all rise instead. */
@media (max-width: 1100px) {
  .js-ready [data-reveal="left"],
  .js-ready [data-reveal="right"] { transform: translateY(26px); }
}

/* ---------- top bar ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  flex: 0 0 auto;
  border-bottom: 1px solid var(--line);
  background: rgba(9, 12, 18, .78);
  backdrop-filter: blur(14px);
}
.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  width: min(var(--content-width), calc(100% - 32px));
  margin: 0 auto;
  padding: 16px 0;
}
.topnav { display: flex; gap: 4px; align-items: center; }
.topnav a, .linklike {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 0 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.topnav a:hover, .linklike:hover { background: rgba(255, 255, 255, .06); color: var(--text); }
.inline-form { display: inline-flex; margin: 0; }

h1, h2, p { margin-top: 0; }
h1 { margin-bottom: 0; font-size: 23px; font-weight: 650; letter-spacing: -.01em; }
h2 { margin-bottom: 0; font-size: 19px; font-weight: 600; letter-spacing: -.01em; }
.eyebrow {
  margin-bottom: 4px;
  color: var(--accent-link);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* ---------- status pills ---------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
  animation: pop-in 200ms var(--ease) both;
}
.pill::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 7px currentColor;
}
/* Each status/severity class publishes its own RGB triple, so every component
   that carries one (pill, score chip, icon tile) can derive a matching tint,
   border, and ring from a single source instead of restating the colour. */
.risk-low, .pill.active { --sev: 53, 201, 139; color: var(--green); }
.risk-medium, .pill.warning { --sev: 232, 178, 60; color: var(--yellow); }
.risk-high { --sev: 239, 135, 68; color: var(--orange); }
.risk-critical, .pill.revoked { --sev: 239, 90, 90; color: var(--red); }
.pill.neutral { --sev: 133, 147, 166; color: #c3cddb; }

.risk-low, .risk-medium, .risk-high, .risk-critical { background: rgba(var(--sev), .1); }
.pill.active, .pill.revoked, .pill.warning, .pill.neutral,
.pill.risk-low, .pill.risk-medium, .pill.risk-high, .pill.risk-critical {
  border-color: rgba(var(--sev), .3);
  background: rgba(var(--sev), .1);
}

/* ---------- surfaces ---------- */
.summary-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}
.summary-grid article, .panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(145deg, rgba(23, 31, 45, .96), rgba(13, 18, 27, .96));
  box-shadow: var(--shadow-md);
}
.summary-grid article {
  padding: 18px;
  animation: rise 320ms var(--ease) both;
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.summary-grid article:hover { border-color: var(--line-strong); transform: translateY(-2px); }
.summary-grid article:nth-child(2) { animation-delay: 50ms; }
.summary-grid article:nth-child(3) { animation-delay: 100ms; }
.summary-grid article:nth-child(4) { animation-delay: 150ms; }
.summary-grid article:nth-child(5) { animation-delay: 200ms; }
.summary-grid article:nth-child(6) { animation-delay: 250ms; }
.summary-grid span { display: block; color: var(--muted); }
.summary-grid strong { display: block; margin-top: 8px; font-size: 28px; font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.summary-grid code { display: block; margin-top: 10px; overflow-wrap: anywhere; }
.panel { margin-bottom: 18px; overflow: hidden; animation: rise 320ms var(--ease) both; }
.panel-heading { padding: 18px 20px; border-bottom: 1px solid var(--line); }
.panel-body { padding: 20px; }

/* ---------- forms ---------- */
.upload-form { display: grid; gap: 20px; padding: 22px 20px; }
.upload-group {
  min-width: 0;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 8px 18px 18px;
  background: rgba(8, 11, 16, .3);
}
.upload-group legend {
  padding: 0 8px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.upload-fields {
  display: grid;
  grid-template-columns: repeat(3, minmax(190px, 1fr));
  gap: 18px;
  align-items: start;
}
label { color: var(--muted); font-weight: 600; }
input, select {
  width: 100%;
  margin-top: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: #0c1119;
  color: var(--text);
  font: inherit;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background var(--speed) var(--ease);
}
/* The rule above is written for text fields. A checkbox is not one: left to
   inherit it, it stretches to the width of its row and grows a 6px top margin,
   which is why the profile module list read as a column of smeared boxes. */
input[type="checkbox"], input[type="radio"] {
  width: auto;
  margin-top: 0;
  padding: 0;
  accent-color: var(--accent);
}
input:hover, select:hover { border-color: var(--line-strong); }
input:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18);
}
input::placeholder { color: var(--dim); }

/* The native control parks its arrow hard against the right edge, far from the
   value. Draw our own and inset it instead. */
select {
  appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%2394a3b8' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 11px 8px;
  cursor: pointer;
}
select:hover, select:focus { background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23eef3fa' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75'/%3E%3C/svg%3E"); }
select option { background: #0c1119; color: var(--text); }

input[type="file"] { padding: 6px; }
input[type="file"]::file-selector-button {
  min-height: 34px;
  margin-right: 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 7px 12px;
  background: var(--secondary);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--speed) var(--ease);
}
input[type="file"]::file-selector-button:hover { background: var(--secondary-hover); }
.upload-fields label { display: flex; flex-direction: column; min-width: 0; }
.upload-fields label small { margin-top: 6px; color: var(--muted); font-weight: 400; }

/* ---------- buttons ---------- */
button, .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  border: 1px solid transparent;
  border-radius: 10px;
  padding: 10px 16px;
  /* White-on-accent must clear 4.5:1, which the bright accent does not (3.6).
     The ramp below runs 5.6 → 6.3 at rest and 4.8 → 5.6 on hover, so the button
     still lifts visibly without dropping under AA. The bright accent stays for
     borders, icons, and links, where the rules are looser. */
  background: linear-gradient(180deg, #3760cf, var(--accent-deep));
  color: #fff;
  font: inherit;
  font-weight: 600;
  letter-spacing: .01em;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .18);
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease),
    color var(--speed) var(--ease), transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
button:hover, .button:hover {
  background: linear-gradient(180deg, var(--accent-press), #3760cf);
  transform: translateY(-1px);
  box-shadow: 0 8px 20px rgba(var(--accent-rgb), .3), inset 0 1px 0 rgba(255, 255, 255, .22);
}
button:active, .button:active {
  background: linear-gradient(180deg, var(--accent-deep), #2a4dae);
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}
button:disabled, .button[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; box-shadow: none; }

button.secondary, .button.secondary {
  border-color: var(--line-strong);
  background: var(--secondary);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}
button.secondary:hover, .button.secondary:hover {
  border-color: #384657;
  background: var(--secondary-hover);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .25);
}
button.secondary:active, .button.secondary:active { background: var(--secondary); }
button.compact, .button.compact { min-height: 34px; padding: 7px 12px; font-size: 13px; }
button.danger {
  border-color: rgba(239, 90, 90, .4);
  background: rgba(239, 90, 90, .12);
  color: #fecdd3;
  box-shadow: none;
}
button.danger:hover { background: rgba(239, 90, 90, .2); color: #fff; box-shadow: 0 4px 14px rgba(239, 90, 90, .2); }
button.danger:active { background: rgba(239, 90, 90, .28); }

/* ---------- tables ---------- */
.table-wrap { overflow-x: auto; }
/* A wide table scrolls, and with a mouse it has to *look* like it scrolls: the
   default bar fades out when the pointer is elsewhere, leaving a table that is
   merely truncated as far as the reader can tell. Only for fine pointers, though.
   Touch devices scroll a table by dragging it and draw an overlay bar while you
   do; styling ::-webkit-scrollbar there replaces that with a permanent classic
   bar under every table, which is clutter nobody asked for. */
@media (pointer: fine) {
  .table-wrap {
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
  }
  .table-wrap::-webkit-scrollbar { height: 9px; }
  .table-wrap::-webkit-scrollbar-track { background: transparent; }
  .table-wrap::-webkit-scrollbar-thumb { border-radius: 999px; background: var(--line-strong); }
  .table-wrap::-webkit-scrollbar-thumb:hover { background: #3d4b5e; }
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 13px 16px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
th {
  background: rgba(8, 11, 16, .45);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
tbody tr { transition: background var(--speed) var(--ease); }
tbody tr:hover { background: rgba(255, 255, 255, .025); }
tbody tr:last-child td { border-bottom: 0; }
td small { display: block; color: var(--muted); }
td code { overflow-wrap: anywhere; }
.empty { padding: 28px; color: var(--muted); text-align: center; }
.empty strong { display: block; margin-bottom: 4px; color: var(--text); font-weight: 600; }

/* ---------- flashes ---------- */
.flash {
  margin-bottom: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  cursor: pointer;
  overflow: hidden;
  /* A concrete height to animate from: max-height cannot transition out of
     `none`, and the flash is a line or two of text. */
  max-height: 12rem;
  animation: rise 300ms var(--ease) both;
  transition: opacity var(--speed-slow) var(--ease), transform var(--speed-slow) var(--ease),
    margin var(--speed-slow) var(--ease), max-height var(--speed-slow) var(--ease);
}
/* Collapse the row as it goes, so the content below does not jump. */
.flash.is-leaving {
  max-height: 0;
  margin-bottom: 0;
  padding-block: 0;
  border-width: 0;
  opacity: 0;
  transform: translateY(-6px);
}
.flash.success { border-color: rgba(53, 201, 139, .35); background: rgba(53, 201, 139, .08); }
.flash.error { border-color: rgba(239, 90, 90, .35); background: rgba(239, 90, 90, .08); }
pre {
  margin: 0;
  overflow: auto;
  padding: 18px;
  background: #06090e;
  color: #d8e2f0;
}
.finding { border-top: 1px solid var(--line); padding: 16px 20px; }
.finding summary { display: flex; gap: 10px; align-items: center; cursor: pointer; }
.finding pre { margin-top: 14px; border-radius: var(--radius-sm); }

/* ---------- headings ---------- */
.page-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin: 10px 0 22px;
}
.page-heading h2 { font-size: clamp(24px, 3vw, 32px); font-weight: 650; letter-spacing: -.02em; }
.page-subtitle { margin: 8px 0 0; color: var(--muted); }
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 18px;
  color: var(--muted);
  text-decoration: none;
  transition: color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.back-link:hover { color: var(--text); transform: translateX(-2px); }
/* The block sits at the right of the heading (the flex row puts it there), but
   its text reads left-aligned: right-aligning a sentence that wraps leaves a
   ragged left edge, which is what made the account help text look crooked. */
.heading-help { max-width: 480px; margin: 0; color: var(--muted); }
.split-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; }

/* ---------- expand all ----------
   Always the last thing on the right of the panel's own heading — never beside the
   title on one panel and out at the edge on another. It governs that panel, so its
   antecedent is obvious, and it lands in the same place on every one of them. */
.heading-actions { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; justify-content: flex-end; }
.heading-actions .heading-help { text-align: right; }
.expand-toggle {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--secondary);
  color: var(--muted);
  box-shadow: none;
}
.finding-group-heading .expand-toggle { margin-left: auto; }
.expand-toggle:hover:not(:disabled) {
  border-color: rgba(var(--accent-rgb), .5);
  background: var(--accent-soft);
  color: var(--accent-100);
  transform: none;
  box-shadow: none;
}
.expand-toggle:active { transform: none; box-shadow: none; }
.expand-toggle svg { grid-area: 1 / 1; transition: opacity var(--speed) var(--ease), transform var(--speed-slow) var(--ease); }
.expand-toggle .collapse-glyph,
.expand-toggle[aria-expanded="true"] .expand-glyph { opacity: 0; transform: scale(.7); }
.expand-toggle[aria-expanded="true"] .collapse-glyph { opacity: 1; transform: none; }
.expand-toggle[aria-expanded="true"] { border-color: rgba(var(--accent-rgb), .45); background: var(--accent-soft); color: var(--accent-100); }
.muted { color: var(--muted); }
.field-label { display: block; margin-bottom: 5px; color: var(--muted); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; }

.risk-badge {
  display: grid;
  grid-template-columns: auto auto;
  align-items: baseline;
  min-width: 150px;
  border: 1px solid rgba(var(--sev, 133, 147, 166), .35);
  border-radius: var(--radius-lg);
  padding: 12px 16px;
  background: rgba(var(--sev, 133, 147, 166), .12);
  box-shadow: inset 0 0 24px rgba(var(--sev, 133, 147, 166), .1);
}
.risk-badge span { grid-column: 1 / -1; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; }
.risk-badge strong { font-size: 38px; font-weight: 650; line-height: 1; letter-spacing: -.02em; }
.risk-badge small { margin-left: 3px; }
.summary-grid .summary-text { font-size: 20px; }
/* Seven tiles sharing a column with the rail: auto-fit keeps them on one row
   when they fit and reflows them evenly when they do not, rather than stranding
   the last tile alone on a second row. */
.scan-summary { grid-template-columns: repeat(auto-fit, minmax(146px, 1fr)); }
.scan-summary strong { font-size: 24px; }
.scan-summary .summary-text { font-size: 16px; }

/* Coverage is one status story, not seven unrelated dashboard cards. The
   completion figure anchors the row while exceptions remain easy to scan. */
.coverage-overview {
  display: grid;
  grid-template-columns: minmax(250px, .8fr) minmax(520px, 1.8fr);
  align-items: stretch;
  border-bottom: 1px solid var(--line);
  background: rgba(8, 12, 19, .24);
}
.coverage-completion {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 22px 24px;
  border-right: 1px solid var(--line);
}
.coverage-completion-value { display: flex; align-items: baseline; min-width: max-content; }
.coverage-completion-value strong {
  color: var(--accent-100);
  font-size: 38px;
  font-weight: 680;
  line-height: 1;
  letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}
.coverage-completion-value span { margin-left: 5px; color: var(--muted); font-size: 16px; }
.coverage-completion-label { display: block; font-weight: 650; }
.coverage-completion p { margin: 5px 0 0; color: var(--muted); font-size: 13px; }
.coverage-metrics {
  display: grid;
  grid-template-columns: repeat(6, minmax(92px, 1fr));
  align-items: stretch;
  margin: 0;
}
.coverage-metrics > div {
  display: flex;
  min-width: 0;
  flex-direction: column-reverse;
  justify-content: center;
  gap: 6px;
  padding: 18px 16px;
  border-right: 1px solid rgba(133, 147, 166, .12);
}
.coverage-metrics > div:last-child { border-right: 0; }
.coverage-metrics dt { color: var(--muted); font-size: 12px; line-height: 1.35; }
.coverage-metrics dd {
  margin: 0;
  color: var(--text);
  font-size: 22px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.coverage-metrics dd.has-exception { color: var(--red); }
.coverage-metrics dd.has-warning { color: var(--yellow); }
.coverage-details { background: rgba(8, 12, 19, .18); }
.coverage-details > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 15px 24px;
  color: var(--accent-link);
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.coverage-details > summary::-webkit-details-marker { display: none; }
.coverage-details > summary::before {
  content: '';
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--speed) var(--ease);
}
.coverage-details[open] > summary::before { transform: rotate(45deg) translate(-1px, -1px); }
.coverage-details > summary:hover { color: var(--accent-link-hover); background: var(--accent-soft); }
.coverage-details > summary small { margin-left: auto; color: var(--muted); font-weight: 500; }
.coverage-details .evidence-summary { padding-top: 0; }

/* ---------- tabs ---------- */
.tab-list {
  position: sticky;
  top: calc(var(--topbar-h, 90px) + 8px);
  z-index: 10;
  display: flex;
  gap: 6px;
  margin: 0 0 18px;
  padding: 6px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #0b0f16;
  scrollbar-width: thin;
}
.tab-list button {
  flex: 1 0 auto;
  min-width: 120px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  font-weight: 500;
  box-shadow: none;
}
.tab-list button:hover {
  background: rgba(255, 255, 255, .05);
  color: var(--text);
  transform: none;
  box-shadow: none;
}
.tab-list button span {
  margin-left: 6px;
  border-radius: 999px;
  padding: 2px 8px;
  background: rgba(255, 255, 255, .08);
  font-size: 11px;
  font-weight: 600;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.tab-list button[aria-selected="true"] {
  border-color: rgba(var(--accent-rgb), .45);
  background: var(--accent-soft);
  color: var(--accent-100);
  font-weight: 600;
}
.tab-list button[aria-selected="true"] span { background: rgba(var(--accent-rgb), .3); color: var(--accent-100); }
.tab-panel { scroll-margin-top: calc(var(--topbar-h, 90px) + 76px); }
.tab-panel:focus { outline: none; }
.tab-panel:not([hidden]) { animation: rise var(--speed-slow) var(--ease) both; }

/* ---------- workspace rail ----------
   Two levels of navigation in one column: the category tabs, and the sections
   inside whichever tab is open. A scan can carry hundreds of rows per tab, so
   every section is reachable without scrolling the panel end to end. */
.workspace { display: grid; grid-template-columns: 252px minmax(0, 1fr); gap: 20px; align-items: start; }
.workspace-main { min-width: 0; }
.workspace-rail {
  position: sticky;
  top: calc(var(--topbar-h, 90px) + 16px);
  z-index: 10;
  display: grid;
  gap: 12px;
  max-height: calc(100vh - var(--topbar-h, 90px) - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
/* Opaque, not blurred. A backdrop-filter on a sticky element repaints its whole
   area on every scroll frame; the top bar already pays that cost, and a second
   blurred layer sitting over a page of evidence tables is what made scrolling
   stutter. Nothing shows through behind the rail anyway. */
.tab-rail {
  display: grid;
  gap: 3px;
  padding: 7px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #0b0f16;
}
.tab-rail button {
  justify-content: space-between;
  min-height: 38px;
  padding: 8px 12px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  font-weight: 500;
  text-align: left;
  box-shadow: none;
}
.tab-rail button:hover { background: rgba(255, 255, 255, .05); color: var(--text); transform: none; box-shadow: none; }
.tab-rail button[aria-selected="true"] {
  border-color: rgba(var(--accent-rgb), .45);
  background: var(--accent-soft);
  color: var(--accent-100);
  font-weight: 600;
}
.rail-count {
  border-radius: 999px;
  padding: 2px 8px;
  background: rgba(255, 255, 255, .08);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.tab-rail button[aria-selected="true"] .rail-count { background: rgba(var(--accent-rgb), .3); color: var(--accent-100); }

.section-rail { border: 1px solid var(--line); border-radius: 14px; padding: 12px 10px; background: rgba(9, 12, 18, .6); }
.rail-title {
  margin: 0 0 8px;
  padding-left: 12px;
  color: var(--dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.section-links { display: grid; gap: 2px; }
.section-link {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
  border-left: 2px solid var(--line);
  padding: 7px 10px;
  color: var(--muted);
  font-size: 13px;
  text-decoration: none;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.section-link:hover { border-left-color: var(--line-strong); background: rgba(255, 255, 255, .04); color: var(--text); }
.section-link.is-current { border-left-color: var(--accent); background: var(--accent-soft); color: var(--accent-100); font-weight: 600; }
.rail-tally {
  flex: 0 0 auto;
  border-radius: 999px;
  padding: 1px 7px;
  background: rgba(255, 255, 255, .07);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
/* The rail parks a section under the sticky top bar, not behind it. */
[data-section] { scroll-margin-top: calc(var(--topbar-h, 90px) + 20px); }

.required-dot { display: inline-block; width: 8px; height: 8px; margin-right: 5px; border-radius: 50%; background: var(--red); }
.field-required, .field-optional { margin-left: 4px; font-size: 10px; letter-spacing: .05em; text-transform: uppercase; }
.field-required { color: #fda4af; font-weight: 600; }
.field-optional { color: var(--dim); font-weight: 500; }
.form-action { grid-column: 1 / -1; display: flex; align-items: center; justify-content: flex-end; gap: 14px; padding-top: 4px; }
.form-action small { margin-right: auto; color: var(--muted); }

/* ---------- score list ----------
   Two different facts sit in one row and must not be confused. The score badge
   is tinted for contribution size; the pill is tinted for finding severity. */
.panel-collapse-details > summary {
  cursor: pointer;
  list-style: none;
  transition: background var(--speed) var(--ease);
}
.panel-collapse-details > summary::-webkit-details-marker { display: none; }
.panel-collapse-details > summary:hover { background: rgba(255, 255, 255, .025); }
.panel-collapse-details:not([open]) > summary { border-bottom: 0; }
.panel-collapse-title, .panel-collapse-title > span { display: block; }
.panel-collapse-heading {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.01em;
}
.panel-collapse-summary { display: flex; gap: 16px; align-items: center; }
.panel-collapse-disclosure {
  flex: 0 0 auto;
  width: 13px;
  height: 9px;
  background: currentColor;
  color: var(--muted);
  clip-path: polygon(0 12%, 50% 65%, 100% 12%, 100% 42%, 50% 95%, 0 42%);
  transition: transform var(--speed-slow) var(--ease), color var(--speed) var(--ease);
}
.panel-collapse-details:not([open]) .panel-collapse-disclosure { transform: rotate(-90deg); }
.panel-collapse-details > summary:hover .panel-collapse-disclosure { color: var(--text); }
.score-list {
  --score-columns: minmax(190px, 1fr) 92px 92px 84px;
  display: grid;
}
.score-legend {
  display: grid;
  grid-template-columns: var(--score-columns);
  gap: 16px;
  align-items: center;
  padding: 10px 20px;
  border-bottom: 1px solid var(--line);
  background: rgba(8, 11, 16, .45);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.score-row {
  display: grid;
  grid-template-columns: var(--score-columns);
  gap: 16px;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid var(--line);
  transition: background var(--speed) var(--ease);
}
.score-row:hover { background: rgba(255, 255, 255, .02); }
.score-row:last-child { border-bottom: 0; }
/* A shared fixed track keeps every finding aligned while leaving enough room
   for the full 0–100 contribution range. */
.score-value {
  display: grid;
  place-items: center;
  min-height: 40px;
  padding: 6px 12px;
  border: 1px solid rgba(var(--sev, 133, 147, 166), .28);
  border-radius: 10px;
  background: rgba(var(--sev, 133, 147, 166), .14);
  font-size: 15px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.score-row > .pill { justify-self: start; }
.score-copy strong, .score-copy span { display: block; }
.score-copy strong { font-weight: 600; }
.score-copy span { color: var(--muted); font-size: 12px; }
.score-confidence {
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* ---------- findings ----------
   `minmax(0, 1fr)`, not the implicit `auto` track. An auto track refuses to go
   below the min-content width of what is in it, and a finding holding a wide
   evidence table has a min-content width of about 1200px — so the track grew to
   1200, the card overflowed the panel, and `.panel { overflow: hidden }` quietly
   painted the last column away. Nothing could scroll, because as far as the table's
   own wrapper was concerned it had all the room it asked for. Capping the track at
   the panel's width pushes the overflow back down into .table-wrap, which is the
   one box on the page equipped to scroll it. */
.finding-list {
  --finding-columns: 8px minmax(180px, 1fr) 110px 92px 24px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.finding-legend {
  display: grid;
  grid-template-columns: var(--finding-columns);
  gap: 14px;
  align-items: center;
  padding: 10px 20px;
  border-bottom: 1px solid var(--line);
  background: rgba(8, 11, 16, .45);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.finding-legend .legend-confidence, .confidence { justify-self: end; }
.finding-card { border-bottom: 1px solid var(--line); transition: background var(--speed) var(--ease); }
.finding-card:last-child { border-bottom: 0; }
.finding-card > summary {
  display: grid;
  grid-template-columns: var(--finding-columns);
  gap: 14px;
  align-items: center;
  padding: 17px 20px;
  cursor: pointer;
  list-style: none;
  transition: background var(--speed) var(--ease);
}
.finding-card > summary:hover { background: rgba(255, 255, 255, .03); }
.finding-card > summary::-webkit-details-marker { display: none; }
.finding-card[open] > summary { background: var(--accent-soft); }
.finding-marker { width: 8px; height: 36px; border-radius: 999px; background: currentColor; }
.finding-title strong, .finding-title small { display: block; }
.finding-title strong { font-weight: 600; }
.finding-title small, .confidence { color: var(--muted); font-size: 12px; }
.disclosure { font-size: 22px; color: var(--muted); transition: transform var(--speed-slow) var(--ease), color var(--speed) var(--ease); }
.finding-card[open] .disclosure { transform: rotate(45deg); color: var(--text); }
.finding-evidence { padding: 0 20px 20px 42px; }
.finding-evidence h3 { margin: 8px 0 12px; font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .07em; }
/* The subject is what tells two findings of the same type apart, so it sits on
   the headline itself rather than inside the evidence. */
.finding-headline { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; min-width: 0; }
.finding-subject { color: var(--accent-link); font-size: 13px; overflow-wrap: anywhere; }
.finding-reason { margin: 14px 0; color: #dbeafe; }
/* Each finding category is its own panel so the rail can jump straight to it. */
.finding-group-heading { display: flex; align-items: center; gap: 12px; }
.finding-group-heading h2, .finding-group-heading p { margin: 0; }
.finding-group-heading p { color: var(--muted); font-size: 12px; }
.category-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
}
.finding-group-network .category-icon { background: rgba(56, 189, 248, .12); color: var(--cyan); }
.finding-group-device_integrity .category-icon { background: rgba(239, 90, 90, .12); color: #fda4af; }
.finding-group-applications .category-icon { background: rgba(91, 157, 247, .12); color: #93c5fd; }
.finding-group-configuration .category-icon { background: rgba(232, 178, 60, .12); color: var(--yellow); }

/* ---------- expandable evidence tables ----------
   DNS packets, VPN flows, and installed apps are all the same shape of problem:
   hundreds of rows you want to sort and scan, each hiding a page of evidence.
   One component: a sortable table whose rows open in place.

   Fixed layout with a shared column rhythm, so the three tables line up as one
   system. Auto layout sized each column to whatever happened to be in it, which
   is why DOMAIN and PACKAGE sprawled while their neighbours were squeezed. Each
   table declares its own <colgroup>: one flexible identifier column, the rest on
   the scale below. */
.evidence-table[data-expandable] { table-layout: fixed; width: 100%; }
.evidence-table[data-expandable] th,
.evidence-table[data-expandable] td { overflow-wrap: anywhere; }

/* A floor, one per table: the sum of its colgroup plus a usable identifier column.

   Given column widths that add up to more than the table is allowed to be, a fixed
   layout does not overflow — it quietly scales every column down, past the width
   its own heading needs, and the headings wrap. That is what stacked the DNS header
   row three lines deep on a 1280px screen while the apps table beside it stayed on
   one, and it is why the three tables stopped looking like one system. Below its
   floor a table scrolls in its wrapper instead, and every header row on the page
   stays exactly one line tall, whatever the window is doing. */
#vpn-flows .evidence-table { min-width: 1030px; }
#dns-packets .evidence-table { min-width: 1110px; }
#apps-list .evidence-table { min-width: 900px; }
.col-time { width: 190px; }
.col-badge { width: 116px; }
/* Wide enough for the longest header on the scale ("DECLARED", "ANSWERS") plus the
   cell padding *and* the room the sort arrow reserves on its right: a column that
   cannot hold its own heading wraps it. */
.col-narrow { width: 114px; }
.col-medium { width: 132px; }
.col-expand { width: 52px; }
/* The nested permission table keeps auto layout: it has its own shape. */
.evidence-detail .evidence-table { table-layout: auto; }

.evidence-row { cursor: pointer; }
.evidence-row > td { transition: background var(--speed) var(--ease); }
.evidence-row:hover > td { background: rgba(255, 255, 255, .035); }
.evidence-row.is-open > td { background: var(--accent-soft); border-bottom-color: transparent; }
.evidence-row > td > code { overflow-wrap: anywhere; }
.expand-cell { width: 46px; padding-left: 0 !important; text-align: right; }
.row-expand {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  box-shadow: none;
}
.row-expand:hover { border-color: var(--line-strong); background: rgba(255, 255, 255, .06); color: var(--text); transform: none; box-shadow: none; }
.row-expand:active { transform: none; box-shadow: none; }
.evidence-row.is-open .disclosure { transform: rotate(45deg); color: var(--accent-100); }

.evidence-detail > td {
  padding: 4px 20px 20px;
  background: rgba(8, 11, 16, .55);
  border-bottom: 1px solid var(--line-strong);
}
.evidence-detail .evidence-summary { padding: 12px 0 0; }
.evidence-detail .dns-sections { padding: 8px 0 0; }
.evidence-detail .dns-route { padding: 12px 0 0; }
.permission-name { overflow-wrap: break-word; }

/* ---------- evidence search ---------- */
.evidence-search {
  display: flex;
  gap: 10px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--line);
  background: rgba(8, 11, 16, .35);
}
.evidence-search input { margin: 0; }
.evidence-search button, .evidence-search .button { flex: 0 0 auto; min-width: 104px; }

/* ---------- evidence ---------- */
.evidence-summary { padding: 16px 20px; }
/* Inside a finding the wrapper already carries the inset, so the summary must
   not add its own — that is what pushed the evidence cards out of line with the
   "Evidence" heading above them. */
.finding-evidence .evidence-summary { padding: 0; }
/* One rule decides how wide any evidence field is, everywhere in the console:

     a field is as wide as its own value needs, and no wider.

   An auto-fit grid could not express that — every column came out the same width,
   so a one-word resolver address got the same box as a SHA-256, and a field
   holding a single chip was promoted to a full row because a longer list might
   have needed one. Flex sizes each box from its content (flex-basis: auto), lets
   the boxes on a line share what is left over, and hands the full row only to the
   values that genuinely are row-sized things: a table, a nested record, or a list
   long enough to have been collapsed. */
.evidence-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0;
}
.evidence-grid > div {
  flex: 1 1 auto;
  min-width: min(160px, 100%);
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: rgba(8, 11, 16, .45);
  transition: border-color var(--speed) var(--ease);
}
.evidence-grid > div:hover { border-color: var(--line-strong); }
.evidence-grid.compact > div { min-width: min(130px, 100%); }
.evidence-grid dt { margin-bottom: 4px; color: var(--muted); font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; }
.evidence-grid dd { margin: 0; min-width: 0; }
.evidence-value { white-space: normal; overflow-wrap: anywhere; color: #dbeafe; }
.metric-value { font-weight: 600; font-variant-numeric: tabular-nums; }

/* The three row-sized values. Anything else keeps the width of its content. */
.evidence-grid > div:has(.table-wrap),
.evidence-grid > div:has(.evidence-grid),
.evidence-grid > div:has([data-collapsible]) { flex-basis: 100%; }

.evidence-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.evidence-list > li { min-width: 0; border-radius: 6px; padding: 3px 8px; background: #1a2230; }
.evidence-list > li:has(.evidence-grid) { width: 100%; padding: 0; background: transparent; }

/* ---------- long lists ----------
   Forty granted permissions used to be penned into a fixed height with their own
   scrollbar: it hid how many there were, and putting the pointer over it stole
   the page's scroll. Show a couple of rows, fade the cut so it plainly continues,
   and let the reader ask for the rest. Every item stays in the DOM, so find-in-page
   and printing still see the whole list. */
.evidence-list-wrap { display: grid; gap: 8px; justify-items: start; min-width: 0; }
.evidence-list-wrap.is-collapsed .evidence-list {
  max-height: 82px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, #000 46px, transparent);
  mask-image: linear-gradient(180deg, #000 46px, transparent);
}
.list-toggle {
  min-height: 26px;
  padding: 3px 10px;
  border-color: var(--line-strong);
  border-radius: 999px;
  background: var(--secondary);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  box-shadow: none;
}
.list-toggle:hover {
  background: var(--secondary-hover);
  color: var(--text);
  transform: none;
  box-shadow: none;
}

/* The frame and its rounding belong to the scroll box, not to the table.
   `overflow: hidden` on a <table> makes the table clip its own cells: the table
   box stays at width:100% while the columns inside need more, so the last column
   is simply painted away — and the wrapper never sees an overflow to scroll. That
   is what cut "Is System" off the end of the app evidence table. Move the frame
   out to the wrapper, which can actually scroll. */
.table-wrap:has(> .record-table), .table-wrap:has(> .answer-table) {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.record-table { border-collapse: collapse; }
/* Headers keep the console's header scale. At 13px these read a size larger than
   every other table on the page. */
.record-table th, .record-table td { padding: 9px 12px; vertical-align: middle; }
.record-table td { font-size: 13px; }
/* Headers sit on one baseline, at the bottom of the header row. Centred, a header
   that fits on one line ("Is System") floats to the middle of a row whose height is
   set by one that takes two ("Permissions Truncated") — so no two headers in the
   row start at the same height. Bottom-aligned they all land on the line directly
   above the data they label, however many lines each of them needs. */
.record-table th, .answer-table th { vertical-align: bottom; }
.record-table tbody tr:last-child td { border-bottom: 0; }
/* `anywhere` lets a cell shrink to one character wide, which is how a column of
   package names ended up crushed to four letters beside a column of permission
   chips. `break-word` keeps the longest word as the column's minimum, so an
   identifier gets the width it needs and the table scrolls if the row is wide. */
.record-table code { overflow-wrap: break-word; color: #dbeafe; }
/* A floor, not a target. At 180px five of these columns alone came to 900px and
   the table could not fit any panel it was put in; 130px still holds a package
   name without crushing it, and leaves the chip column the room it actually needs. */
.record-table td:not(:has(.evidence-list)) { min-width: 130px; }

/* ---------- indicators ---------- */
/* See .finding-list: an auto track will not shrink below its content's min-content
   width, and these hold provider evidence that can be very wide. */
.card-table { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 16px 20px; }
.indicator-card {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) 130px auto;
  gap: 18px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 14px;
  background: rgba(8, 11, 16, .4);
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.indicator-card:hover { border-color: var(--line-strong); transform: translateY(-1px); }
.indicator-main code { display: block; margin-top: 4px; font-size: 14px; overflow-wrap: anywhere; }
.indicator-main p { margin: 6px 0 0; color: var(--muted); }
.indicator-type { color: var(--cyan); font-size: 10px; font-weight: 700; letter-spacing: .08em; }
.indicator-provider span, .indicator-provider strong { display: block; }
.indicator-provider span { color: var(--muted); font-size: 11px; }
.indicator-provider strong { font-weight: 600; }
.indicator-score {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 1px;
  border: 1px solid rgba(var(--sev, 133, 147, 166), .3);
  border-radius: var(--radius-md);
  padding: 8px 12px;
  background: rgba(var(--sev, 133, 147, 166), .14);
}
.indicator-score strong { font-size: 24px; font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.indicator-score span { color: var(--muted); font-size: 12px; }
.inline-details { grid-column: 1 / -1; }
.inline-details > summary {
  width: fit-content;
  border-radius: 6px;
  padding: 2px 6px;
  color: var(--accent-link);
  cursor: pointer;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.inline-details > summary:hover { background: var(--accent-soft); color: var(--accent-link-hover); }
.inline-details .evidence-grid { margin-top: 10px; }

.protocol-badge, .direction-badge {
  display: inline-flex;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 4px 8px;
  background: rgba(133, 147, 166, .12);
  color: #c3cddb;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
}
.direction-badge { border-color: rgba(91, 157, 247, .28); background: rgba(91, 157, 247, .12); color: #93c5fd; }
.protocol-tcp { border-color: rgba(91, 157, 247, .3); background: rgba(91, 157, 247, .14); color: #93c5fd; }
.protocol-udp { border-color: rgba(167, 139, 250, .32); background: rgba(167, 139, 250, .16); color: var(--accent-200); }
.protocol-icmp { border-color: rgba(239, 135, 68, .3); background: rgba(239, 135, 68, .14); color: #fdba74; }
.traffic-up, .traffic-down { display: block; white-space: nowrap; font-variant-numeric: tabular-nums; }
.traffic-up { color: var(--yellow); }
.traffic-down { color: #67e8f9; }
.evidence-table small { display: block; color: var(--muted); }

/* ---------- dns ---------- */
.direction-badge.response { background: rgba(53, 201, 139, .12); color: var(--green); }
.dns-route { display: flex; gap: 12px; align-items: center; padding: 14px 20px 0; color: var(--muted); }

/* ---------- downloads / artifacts ---------- */
.download-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; padding: 20px; }
.download-card {
  display: grid;
  grid-template-columns: 54px 1fr auto;
  gap: 16px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 18px;
  background: rgba(8, 11, 16, .42);
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.download-card:hover { border-color: var(--line-strong); transform: translateY(-2px); }
.download-card h3 { margin: 0 0 5px; font-size: 15px; font-weight: 600; }
.download-card p { margin: 0; color: var(--muted); }
.download-icon, .artifact-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
}
.delivery-row { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
.delivery-row strong, .delivery-row small { display: block; }
.delivery-row small { margin-top: 4px; color: var(--muted); }

/* ---------- search ---------- */
.search-panel { border-bottom: 1px solid var(--line); background: rgba(8, 11, 16, .35); }
.primary-search { padding: 18px 20px 14px; }
.primary-search > label { display: block; margin-bottom: 8px; }
.search-input { display: flex; gap: 10px; }
.search-input input { margin: 0; font-size: 15px; }
.search-input button { min-width: 120px; }
.advanced-filters { border-top: 1px solid var(--line); }
.advanced-filters > summary {
  padding: 12px 20px;
  color: var(--muted);
  cursor: pointer;
  font-weight: 600;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.advanced-filters > summary:hover { background: rgba(255, 255, 255, .03); color: var(--text); }
.active-filter-dot {
  margin-left: 8px;
  border-radius: 999px;
  padding: 2px 8px;
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.filter-grid { display: grid; grid-template-columns: repeat(5, minmax(140px, 1fr)); gap: 12px; align-items: end; padding: 4px 20px 18px; }
.audit-filter-grid { grid-template-columns: repeat(4, minmax(160px, 1fr)); padding: 18px 20px; }
.filter-actions { display: flex; gap: 8px; }
.results-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 10px 20px; color: var(--muted); }
.results-meta strong { color: var(--text); font-weight: 600; }
/* Pushed to the far end of the strip: it governs the rows below, but it is a
   setting, not a result. */
.rows-per-page { display: flex; gap: 8px; align-items: center; margin-left: auto; font-size: 13px; font-weight: 500; }
.rows-per-page .select { margin-top: 0; }
.rows-per-page select, .rows-per-page .select-button { min-height: 32px; margin-top: 0; padding: 4px 10px; font-size: 13px; }
.rows-per-page .select-button { padding-right: 8px; }
.filter-chip {
  border: 1px solid rgba(var(--accent-rgb), .35);
  border-radius: 999px;
  padding: 2px 10px;
  background: var(--accent-soft);
  color: var(--accent-100);
  font-size: 12px;
  font-weight: 600;
}
/* Score only — the tint carries the severity. Fixed width and tabular figures
   keep "5" and "100" aligned down the column. */
.risk-score {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 1px;
  width: 82px;
  padding: 6px 10px;
  border: 1px solid rgba(var(--sev, 133, 147, 166), .28);
  border-radius: 999px;
  background: rgba(var(--sev, 133, 147, 166), .1);
  font-variant-numeric: tabular-nums;
}
.risk-score strong { font-size: 17px; font-weight: 650; letter-spacing: -.02em; }
.risk-score-max { color: var(--muted); font-size: 10px; font-weight: 500; }
.evidence-count { display: inline-flex; gap: 4px; margin: 0 8px 4px 0; color: var(--muted); white-space: nowrap; }
.evidence-count strong { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.is-loading { opacity: .5; pointer-events: none; transition: opacity var(--speed) var(--ease); }

/* ---------- module picker ----------
   The steps of a profile are chosen here. The server orders them by the order the
   checkboxes appear in the form, so the badge on each selected card is its real
   execution step — the script numbers them in DOM order, never in click order. */
.picker-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}
.picker-count { margin: 0; color: var(--muted); }
.picker-count strong { color: var(--text); font-variant-numeric: tabular-nums; }
.picker-count small { display: block; margin-top: 2px; color: var(--dim); font-size: 12px; }
.picker-actions { display: flex; gap: 8px; }

.picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.picker-card {
  /* The real checkbox is taken out of flow below, so the card has to be the box
     it is taken out of — otherwise it lands against some far-off ancestor and
     sits, invisible but clickable, over whatever happens to be there. */
  position: relative;
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  background: rgba(8, 11, 16, .45);
  color: var(--text);
  font-weight: 400;
  cursor: pointer;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.picker-card:hover { border-color: var(--line-strong); background: rgba(255, 255, 255, .03); }
.picker-card:has(:checked) { border-color: rgba(var(--accent-rgb), .5); background: var(--accent-soft); }
/* The native box is kept — it is what submits, and what a screen reader reads —
   but it draws differently on every platform, so hide it and draw the indicator
   beside it. The focus ring has to be re-hung on the card, or keyboard users lose
   the only cue they had. */
.picker-card input[type="checkbox"] { position: absolute; opacity: 0; width: 18px; height: 18px; }
.picker-card:has(:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
.picker-box {
  width: 18px;
  height: 18px;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: #0c1119;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.picker-card:has(:checked) .picker-box {
  border-color: var(--accent);
  background: var(--accent) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.4'/%3E%3C/svg%3E") center / 10px no-repeat;
}
.picker-copy { min-width: 0; }
.picker-copy strong, .picker-copy small { display: block; overflow-wrap: anywhere; }
.picker-copy strong { font-weight: 600; }
.picker-copy small { color: var(--muted); font-size: 12px; }
/* Empty until the card is chosen, so an unselected list carries no phantom steps. */
.picker-order {
  display: grid;
  place-items: center;
  min-width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb), .3);
  color: var(--accent-100);
  font-size: 12px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.picker-order:empty { background: none; }

/* Seven columns of device telemetry beside a 252px rail is the tightest fit in the
   console. At the shared 16px the cell padding alone came to 224px of the row —
   enough on its own to push the table off the panel on a 1200px screen. */
.stack-table th, .stack-table td { padding-inline: 12px; }

/* ---------- profile steps ---------- */
/* Steps is the only column that should absorb the slack. Left to itself it took
   most of the row, and Status and Action ended up at the far right edge, a long
   way from the profile they describe. */
.col-profile { width: 260px; }
.profile-table { table-layout: fixed; }
.profile-table th, .profile-table td { overflow-wrap: anywhere; }
.step-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.step-chips li {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 10px 3px 3px;
  background: rgba(8, 11, 16, .5);
  font-size: 12px;
}
.step-index {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-100);
  font-size: 11px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* ---------- device assignment ----------
   The cell reports what the device is running, and nothing more. Choosing a scanner
   needs three selects, and three selects in a table cell is what dragged the devices
   table wider than the page — so that lives in a dialog, reached from the Action
   column with every other control that acts on the device. */
.mode-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.mode-chip {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
  background: rgba(8, 11, 16, .5);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
/* Unassign is destructive and belongs at the far end from Save, not next to it. */
.modal-foot-start { margin-right: auto; }

/* ---------- audit timeline ---------- */
/* See .finding-list: audit details render the same evidence tables. */
.audit-list { display: grid; grid-template-columns: minmax(0, 1fr); }
.audit-event {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  gap: 6px 16px;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
  animation: rise 320ms var(--ease) both;
  transition: background var(--speed) var(--ease);
}
.audit-event:hover { background: rgba(255, 255, 255, .025); }
.audit-event:last-child { border-bottom: 0; }
.audit-event:nth-child(2) { animation-delay: 40ms; }
.audit-event:nth-child(3) { animation-delay: 80ms; }
.audit-event:nth-child(4) { animation-delay: 120ms; }
.audit-event:nth-child(5) { animation-delay: 160ms; }
.audit-event:nth-child(n + 6) { animation-delay: 200ms; }
.audit-rail {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.audit-copy { min-width: 0; }
.audit-copy strong { display: block; font-weight: 600; }
.audit-actor, .audit-copy small {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 12px;
  overflow-wrap: anywhere;
}
.audit-time {
  justify-self: end;
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.audit-time small { display: block; margin-top: 2px; color: var(--dim); }
.audit-details { grid-column: 2 / -1; margin-top: 4px; }

/* ---------- account ---------- */
.account-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.account-card { overflow: visible; }
.account-details { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.account-details > div {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px;
  background: rgba(8, 11, 16, .4);
  transition: border-color var(--speed) var(--ease);
}
.account-details > div:hover { border-color: var(--line-strong); }
.account-details span, .account-details strong { display: block; }
.account-details span { margin-bottom: 5px; color: var(--muted); font-size: 12px; }
.account-details strong { font-weight: 600; overflow-wrap: anywhere; }
.session-action { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.session-action p { margin: 5px 0 0; color: var(--muted); }
.session-action form { flex: 0 0 auto; margin: 0; }

/* ---------- credentials ---------- */
.password-form { display: grid; gap: 18px; max-width: 520px; padding: 22px 20px; }
.password-form .form-action { justify-content: space-between; }

.session-list { display: grid; }
.session-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 16px;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
  transition: background var(--speed) var(--ease);
}
.session-row:last-child { border-bottom: 0; }
.session-row:hover { background: rgba(255, 255, 255, .02); }
/* The session you are reading this in: tinted so revoking the wrong one is hard. */
.session-row.is-current { background: rgba(var(--accent-rgb), .07); }
.session-row form { margin: 0; }
.session-copy { min-width: 0; }
.session-copy strong { display: block; font-weight: 600; overflow-wrap: anywhere; }
.session-copy span, .session-copy small { display: block; margin-top: 3px; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
.session-copy small { color: var(--dim); }

/* `minmax(0, 1fr)`, not the implicit `auto` track — see .finding-list. The facts
   row below holds a 64-character digest, so its min-content width is far wider
   than a phone; an auto track would size to that and push the whole panel off the
   side of the screen. */
.artifact-panel-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }
.artifact-summary { display: grid; grid-template-columns: 54px minmax(0, 1fr) auto; gap: 16px; align-items: center; }
.artifact-summary strong, .artifact-action strong, .artifact-action small { display: block; }
.artifact-summary strong { font-weight: 600; }
.artifact-summary p { margin: 4px 0 0; color: var(--muted); }
.artifact-action small { color: var(--muted); font-weight: 400; }
.artifact-action { display: flex; align-items: center; justify-content: space-between; gap: 18px; border-top: 1px solid var(--line); padding-top: 18px; }
.artifact-action strong { font-weight: 600; }
/* Same principle as the evidence grid: each fact is as wide as its value needs.
   The digest is 64 characters and the other two are a handful, so they are not
   given matching columns. */
.artifact-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  border-top: 1px solid var(--line);
  padding-top: 18px;
}
.artifact-facts > div {
  flex: 1 1 auto;
  min-width: min(180px, 100%);
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  background: rgba(8, 11, 16, .45);
}
.artifact-fact-wide { flex-basis: 340px; }
.artifact-facts code { color: #dbeafe; font-size: 12px; }
.artifact-facts strong { font-weight: 600; font-variant-numeric: tabular-nums; }
.download-hash { display: grid; gap: 4px; }
.download-hash code { overflow: hidden; color: #dbeafe; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.inline-notice { margin: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 12px 14px; background: rgba(8, 11, 16, .4); color: var(--muted); }

/* ---------- device groups ---------- */
.group-manager { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; padding: 18px 20px; }
.group-rename-form { display: flex; gap: 7px; align-items: center; }
.group-rename-form input { min-width: 150px; margin: 0; padding: 8px 10px; }
/* Every control that acts on a device lives here, so it has to hold four of them
   without making the row twice the height of the data beside it. Two columns, each
   button stretched to its cell, so the group reads as one block rather than four
   buttons of four different widths. */
.action-stack {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  /* Wide enough for the longest label on it ("Edit details") in both columns, and
     not a pixel wider: this column and the device id were between them costing the
     table more width than the page had to give it. */
  min-width: 220px;
}
.action-stack form { margin: 0; }
.action-stack button, .action-stack .button { width: 100%; }
/* The one primary action when there is nothing else to pair it with. */
.action-wide { grid-column: 1 / -1; }
.local-time { display: inline-block; white-space: nowrap; }
.audit-time .local-time { white-space: normal; }

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
}
.filter-bar input,
.filter-bar select { min-width: 160px; }

.pagination {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: center;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
  color: var(--muted);
}
.pagination a {
  border-radius: 7px;
  padding: 6px 10px;
  text-decoration: none;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.pagination a:hover { background: var(--accent-soft); }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- back to top ----------
   Visibility rides on .is-visible, never the [hidden] attribute: the global
   `[hidden] { display: none !important }` rule would win over any display here
   and there would be nothing to transition from. */
.to-top {
  position: fixed;
  right: 26px;
  bottom: 26px;
  z-index: 40;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  min-height: 0;
  padding: 0;
  border: 1px solid rgba(var(--accent-rgb), .55);
  border-radius: 50%;
  /* Filled with the accent, not tinted glass. A dark circle on a dark page at the
     very edge of the viewport is there but not *seen*, which is indistinguishable
     from broken. */
  background: linear-gradient(180deg, #3760cf, var(--accent-deep));
  color: #fff;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .5), 0 0 0 5px rgba(var(--accent-rgb), .12);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(12px) scale(.92);
  transition: opacity var(--speed-slow) var(--ease), transform var(--speed-slow) var(--ease),
    visibility var(--speed-slow) var(--ease), border-color var(--speed) var(--ease),
    background var(--speed) var(--ease);
}
.to-top.is-visible { opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
.to-top:hover {
  border-color: var(--accent-hi);
  background: linear-gradient(180deg, var(--accent-hover), var(--accent-press));
  color: #fff;
  transform: translateY(-3px);
  box-shadow: 0 14px 30px rgba(var(--accent-rgb), .45), 0 0 0 6px rgba(var(--accent-rgb), .16);
}
.to-top:active { transform: none; box-shadow: var(--shadow-sm); }

/* ---------- brand ---------- */
.brand-block {
  display: flex;
  gap: 12px;
  align-items: center;
  border-radius: var(--radius-md);
  padding: 6px 10px 6px 6px;
  color: inherit;
  text-decoration: none;
  transition: background var(--speed) var(--ease);
}
.brand-block:hover { background: rgba(255, 255, 255, .05); }
.brand-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(var(--accent-rgb), .45);
  border-radius: 11px;
  background: linear-gradient(150deg, rgba(var(--accent-rgb), .35), rgba(var(--accent-rgb), .12));
}
.brand-glyph { width: 24px; height: 24px; transition: transform var(--speed-slow) var(--ease); }
.brand-block:hover .brand-glyph { transform: scale(1.06); }
.brand-text { display: grid; }
.brand-text .eyebrow { margin-bottom: 1px; }
/* "Mobile Hexii" is the product's name, not a paragraph: let the nav beside it
   wrap instead of breaking the wordmark across two lines. */
.brand-name { color: var(--text); font-size: 19px; font-weight: 650; letter-spacing: -.01em; white-space: nowrap; }

/* ---------- dropzones ---------- */
.dropzone-field { display: flex; flex-direction: column; min-width: 0; }
.dropzone-caption { margin-bottom: 6px; }
.dropzone {
  position: relative;
  display: block;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-md);
  padding: 20px 16px;
  background: rgba(8, 11, 16, .45);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.dropzone:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), .06); }
.dropzone input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
}
.dropzone.is-focused { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18); }
.dropzone.is-dragging {
  border-color: var(--accent);
  border-style: solid;
  background: rgba(var(--accent-rgb), .14);
  transform: scale(1.005);
}
.dropzone.has-file { border-style: solid; border-color: rgba(53, 201, 139, .45); background: rgba(53, 201, 139, .07); }
.dropzone-body { display: grid; gap: 5px; justify-items: center; pointer-events: none; }
.dropzone-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 15px;
  font-weight: 700;
}
.dropzone.has-file .dropzone-icon { background: rgba(53, 201, 139, .16); color: var(--green); }
.dropzone-title { color: var(--text); font-weight: 600; }
.dropzone-browse { color: var(--accent-link); text-decoration: underline; text-underline-offset: 2px; }
.dropzone-hint { color: var(--muted); font-size: 12px; font-weight: 400; overflow-wrap: anywhere; }
.dropzone-hint strong { color: var(--text); font-weight: 600; }

/* ---------- modal ---------- */
dialog.modal {
  width: min(440px, calc(100vw - 32px));
  margin: auto;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  padding: 0;
  background: linear-gradient(160deg, #19212f, #0e131c);
  color: var(--text);
  box-shadow: 0 30px 70px rgba(0, 0, 0, .55);
}
dialog.modal::backdrop {
  background: rgba(4, 6, 10, .68);
  backdrop-filter: blur(3px);
  animation: fade-in 200ms var(--ease);
}
dialog.modal[open] { animation: pop-in 200ms var(--ease); }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.modal-head h3 { margin: 0; font-size: 18px; font-weight: 600; }
.modal-close {
  min-height: 32px;
  padding: 0 10px;
  border-color: transparent;
  background: transparent;
  color: var(--muted);
  font-size: 20px;
  box-shadow: none;
}
.modal-close:hover { background: rgba(255, 255, 255, .07); color: var(--text); transform: none; box-shadow: none; }
.modal-body { display: grid; gap: 16px; padding: 20px; }
.modal-context { display: grid; gap: 2px; margin: 0; }
.modal-context code { overflow-wrap: anywhere; color: var(--muted); }
.modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid var(--line); background: rgba(8, 11, 16, .4); }

.field { display: grid; gap: 6px; }
.field label { color: var(--muted); font-size: 13px; font-weight: 600; }
/* The field already spaces its own rows; the control must not add a second gap.
   `.select` is the scripted listbox that stands in for a native <select>. */
.field input, .field select, .field .select { margin-top: 0; }
.field-hint { color: var(--muted); font-size: 12px; }

/* ---------- copy to clipboard ---------- */
.copyable, .has-copy { display: inline-flex; align-items: center; gap: 5px; min-width: 0; max-width: 100%; }
.copyable > code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copy-button {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  min-height: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--dim);
  box-shadow: none;
  opacity: .75;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease), opacity var(--speed) var(--ease);
}
.copy-button:hover {
  border-color: var(--line);
  background: rgba(255, 255, 255, .06);
  color: var(--text);
  opacity: 1;
  transform: none;
  box-shadow: none;
}
.copy-button:active { transform: none; box-shadow: none; }
.copy-button svg { grid-area: 1 / 1; transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease); }
.copy-button .copy-tick, .copy-button.is-copied .copy-icon { opacity: 0; transform: scale(.8); }
.copy-button.is-copied { border-color: rgba(53, 201, 139, .4); background: rgba(53, 201, 139, .14); color: var(--green); opacity: 1; }
.copy-button.is-copied .copy-tick { opacity: 1; transform: none; }
.copy-button.is-failed { border-color: rgba(239, 90, 90, .4); color: var(--red); opacity: 1; }
td .copy-button, .modal-context .copy-button { opacity: .55; }
tr:hover .copy-button, td .copy-button:focus-visible, .copyable:hover .copy-button { opacity: 1; }

/* ---------- download progress ---------- */
.download-progress {
  display: grid;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 16px;
  background: rgba(8, 11, 16, .45);
  animation: rise 260ms var(--ease) both;
}
.download-progress-head { display: flex; flex-wrap: wrap; gap: 8px; justify-content: space-between; }
.download-progress-head strong { font-weight: 600; }
.download-progress-head span { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.download-progress small { color: var(--dim); font-size: 12px; }

.progress-track {
  width: 100%;
  height: 8px;
  border: 0;
  border-radius: 999px;
  overflow: hidden;
  background: #1a2230;
  appearance: none;
}
.progress-track::-webkit-progress-bar { border-radius: 999px; background: #1a2230; }
.progress-track::-webkit-progress-value {
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--cyan));
  transition: width var(--speed) linear;
}
.progress-track::-moz-progress-bar {
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--cyan));
}
/* No `value` yet: the server is still signing. Sweep a band to show it is alive
   rather than sitting at a misleading 0%. */
.progress-track:not([value]) {
  background:
    linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .85), transparent) 0 0 / 40% 100% no-repeat,
    #1a2230;
  animation: progress-sweep 1.3s var(--ease) infinite;
}
@keyframes progress-sweep {
  from { background-position: -45% 0; }
  to { background-position: 145% 0; }
}

.spinner {
  display: none;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.is-loading-action { pointer-events: none; opacity: .85; }
.is-loading-action .spinner { display: inline-block; }

/* ---------- service status ---------- */
.status-banner {
  display: flex;
  gap: 12px;
  align-items: center;
  border: 1px solid rgba(var(--sev), .3);
  border-radius: var(--radius-lg);
  padding: 14px 18px;
  background: rgba(var(--sev), .1);
}
.status-banner.is-healthy { --sev: 53, 201, 139; color: var(--green); }
.status-banner.is-degraded { --sev: 239, 90, 90; color: var(--red); }
.status-banner strong { display: block; font-weight: 650; }
.status-banner small { display: block; margin-top: 2px; color: var(--muted); }
.status-banner .status-dot { flex: 0 0 auto; width: 9px; height: 9px; background: currentColor; box-shadow: 0 0 10px currentColor; }

.check-list { display: grid; margin: 0; padding: 0; list-style: none; }
.check-row {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
  transition: background var(--speed) var(--ease);
}
.check-row:last-child { border-bottom: 0; }
.check-row:hover { background: rgba(255, 255, 255, .02); }
.check-icon {
  width: 22px;
  height: 22px;
  border: 1px solid rgba(var(--sev), .4);
  border-radius: 50%;
  background: rgba(var(--sev), .16) center / 11px no-repeat;
}
.check-row.is-ok { --sev: 53, 201, 139; }
.check-row.is-warn { --sev: 232, 178, 60; }
.check-row.is-down { --sev: 239, 90, 90; }
.check-row.is-ok .check-icon {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none' stroke='%2335c98b' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.4'/%3E%3C/svg%3E");
}
.check-row.is-warn .check-icon {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='%23e8b23c' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M5 2v4M5 8.2v.1'/%3E%3C/svg%3E");
}
.check-row.is-down .check-icon {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='%23ef5a5a' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M2.5 2.5 7.5 7.5M7.5 2.5 2.5 7.5'/%3E%3C/svg%3E");
}
.check-copy strong { display: block; font-weight: 600; }
.check-copy span { display: block; margin-top: 2px; color: var(--muted); font-size: 13px; overflow-wrap: anywhere; }

/* ---------- uptime strip ---------- */
.uptime-list { display: grid; }
.uptime-legend, .uptime-row {
  display: grid;
  grid-template-columns: minmax(190px, 240px) minmax(0, 1fr) 132px;
  gap: 20px;
  align-items: center;
  padding: 14px 20px;
}
.uptime-legend {
  padding-block: 10px;
  border-bottom: 1px solid var(--line);
  background: rgba(8, 11, 16, .45);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.uptime-row { border-bottom: 1px solid var(--line); transition: background var(--speed) var(--ease); }
.uptime-row:last-child { border-bottom: 0; }
.uptime-row:hover { background: rgba(255, 255, 255, .02); }
.uptime-copy { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; min-width: 0; }
.uptime-copy strong { font-weight: 600; }
.uptime-copy small { flex: 1 0 100%; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }

/* Ticks flex to share the row: the strip is always padded to a fixed slot count,
   so every component's ticks line up column-for-column down the page. */
.uptime-strip { display: flex; gap: 2px; align-items: stretch; height: 30px; min-width: 0; }
.uptime-tick {
  flex: 1 1 0;
  min-width: 2px;
  border-radius: 2px;
  background: var(--green);
  opacity: .82;
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.uptime-tick:hover { opacity: 1; transform: scaleY(1.12); }
.uptime-tick.is-down { background: var(--red); opacity: 1; }
/* Not yet recorded — present so the strip keeps its width, but plainly not a
   healthy check. */
.uptime-tick.is-empty { background: var(--line-strong); opacity: .5; }
.uptime-tick.is-empty:hover { transform: none; }
/* Same two columns in the legend and every row, so the headings sit exactly
   above their figures. */
.uptime-figures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.uptime-figure { font-size: 13px; font-weight: 600; }
.uptime-figure.is-perfect { color: var(--green); }
.uptime-figure.is-fair { color: var(--yellow); }
.uptime-figure.is-poor { color: var(--red); }
.uptime-note { margin: 0; padding: 14px 20px; border-top: 1px solid var(--line); color: var(--dim); font-size: 12px; }

/* ---------- error page ---------- */
.error-page {
  display: grid;
  justify-items: center;
  gap: 6px;
  max-width: 560px;
  margin: 8vh auto 6vh;
  text-align: center;
}
.error-code {
  font-size: clamp(64px, 12vw, 104px);
  font-weight: 650;
  line-height: 1;
  letter-spacing: -.04em;
  background: linear-gradient(180deg, var(--accent-200), rgba(var(--accent-rgb), .25));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.error-page h2 { margin: 12px 0 0; font-size: clamp(22px, 3vw, 28px); }
.error-detail { margin: 10px 0 22px; color: var(--muted); }

/* ---------- live status chip ---------- */
.status-chip { display: inline-flex; align-items: center; gap: 7px; color: var(--muted); font-size: 13px; }
.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 8px var(--green);
}

/* ---------- custom select ----------
   The native select is kept in the DOM (still named, still submitted, still the
   no-JS fallback) and hidden once the scripted listbox takes over. */
.select { position: relative; margin-top: 6px; }
.select-native { display: none; }
.select-button {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 42px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: #0c1119;
  color: var(--text);
  font-weight: 500;
  text-align: left;
  box-shadow: none;
}
.select-button:hover {
  border-color: var(--line-strong);
  background: #111823;
  color: var(--text);
  transform: none;
  box-shadow: none;
}
.select-button:active { background: #0c1119; transform: none; box-shadow: none; }
.select-button[aria-expanded="true"] {
  border-color: var(--accent);
  background: #0c1119;
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18);
}
.select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-arrow {
  flex: 0 0 auto;
  width: 11px;
  height: 8px;
  background: currentColor;
  color: var(--muted);
  clip-path: polygon(50% 100%, 0 15%, 12% 0, 50% 72%, 88% 0, 100% 15%);
  transition: transform var(--speed-slow) var(--ease), color var(--speed) var(--ease);
}
.select-button:hover .select-arrow { color: var(--text); }
.select-button[aria-expanded="true"] .select-arrow { transform: rotate(180deg); color: var(--accent-200); }

.select-menu {
  position: fixed;
  z-index: 60;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 5px;
  background: #151c27;
  box-shadow: 0 20px 44px rgba(0, 0, 0, .5);
  scrollbar-width: thin;
  animation: select-in 140ms var(--ease);
}
@keyframes select-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.select-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border-radius: 7px;
  padding: 9px 11px;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.select-option:hover, .select-option.is-active { background: rgba(255, 255, 255, .06); color: var(--text); }
.select-option[aria-selected="true"] { background: var(--accent-soft); color: var(--accent-100); font-weight: 600; }
.select-option[aria-selected="true"]::after {
  content: "";
  width: 12px;
  height: 9px;
  background: currentColor;
  clip-path: polygon(0 52%, 12% 40%, 42% 70%, 88% 0, 100% 12%, 42% 96%);
}

/* ---------- sortable tables ----------
   The arrow is positioned, not inlined. Inline it shared the header's line box, so
   a header that fit its column on its own no longer did once the arrow was added:
   "DECLARED" plus an arrow overran a 106px column by two pixels, the arrow wrapped
   onto a line of its own, and that one cell made the whole header row of the apps
   table taller than the identical row on DNS and VPN. Out of flow it cannot break a
   line, cannot change a row's height, and cannot make one header sit lower than its
   neighbour. The space it needs is reserved with padding instead. */
th.sortable {
  position: relative;
  padding-right: 26px;
  cursor: pointer;
  user-select: none;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
th.sortable:hover { background: rgba(255, 255, 255, .04); color: var(--text); }
th.sortable::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 10px;
  width: 0;
  height: 0;
  margin-top: -2px;
  border-inline: 4px solid transparent;
  border-bottom: 5px solid currentColor;
  opacity: 0;
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease);
}
th.sortable:hover::after { opacity: .35; }
th.sortable[aria-sort="ascending"]::after { opacity: 1; color: var(--accent-200); }
th.sortable[aria-sort="descending"]::after { opacity: 1; color: var(--accent-200); transform: rotate(180deg); }

/* ---------- password fields ---------- */
.password-field { position: relative; display: flex; }
.password-field input { padding-right: 74px; }
.password-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  min-height: 30px;
  padding: 0 10px;
  transform: translateY(-50%);
  border-color: var(--line-strong);
  background: var(--secondary);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  box-shadow: none;
}
.password-toggle:hover { background: var(--secondary-hover); color: var(--text); transform: translateY(-50%); box-shadow: none; }
.password-toggle:active { transform: translateY(-50%); }

.strength { display: grid; gap: 6px; margin-top: 2px; }
.strength-track { height: 5px; overflow: hidden; border-radius: 999px; background: #1a2230; }
.strength-fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 999px;
  background: var(--red);
  transition: width var(--speed-slow) var(--ease), background var(--speed-slow) var(--ease);
}
.strength-fill[data-score="2"] { background: var(--orange); }
.strength-fill[data-score="3"] { background: var(--yellow); }
.strength-fill[data-score="4"] { background: #a3e635; }
.strength-fill[data-score="5"] { background: var(--green); }
.strength-label { margin: 0; color: var(--muted); font-size: 12px; }
.strength-rules { display: grid; gap: 4px; margin: 8px 0 0; padding: 0; list-style: none; }
.strength-rules li {
  display: flex;
  gap: 7px;
  align-items: center;
  color: var(--muted);
  font-size: 12px;
  transition: color var(--speed) var(--ease);
}
.strength-rules li::before {
  content: "";
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.strength-rules li.met { color: var(--green); }
.strength-rules li.met::before {
  border-color: var(--green);
  background: var(--green) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none' stroke='%23081018' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.4'/%3E%3C/svg%3E") center / 9px no-repeat;
}

/* ---------- dns detail ---------- */
.dns-sections { display: grid; gap: 18px; padding: 16px 20px 20px; }
.dns-section + .dns-section { border-top: 1px solid var(--line); padding-top: 18px; }
.dns-section h4 {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.section-count { border-radius: 999px; padding: 1px 8px; background: rgba(255, 255, 255, .07); color: var(--text); font-size: 11px; letter-spacing: 0; }
.dns-section .evidence-grid { padding: 0; }
.dns-section-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.parse-error { margin: 10px 0 0; border-radius: var(--radius-sm); padding: 9px 12px; background: rgba(239, 90, 90, .1); color: #fecdd3; font-size: 12px; }

/* See .record-table: the frame lives on the scroll box, never on the table. */
.answer-table { border-collapse: collapse; }
.answer-table th, .answer-table td { padding: 9px 12px; vertical-align: middle; }
.answer-table td { font-size: 13px; }
.answer-table tbody tr:last-child td { border-bottom: 0; }
.answer-table code { color: #dbeafe; }
.answer-value { overflow-wrap: anywhere; }
.record-type {
  display: inline-flex;
  border: 1px solid rgba(56, 189, 248, .28);
  border-radius: 6px;
  padding: 2px 7px;
  background: rgba(56, 189, 248, .12);
  color: var(--cyan);
  font-size: 11px;
  font-weight: 600;
}
.chip-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.chip-list li {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 10px;
  background: rgba(8, 11, 16, .5);
  font-size: 12px;
}
.chip-list code { color: #dbeafe; }
.confidence-empty { color: var(--dim); }

/* ---------- auth ---------- */
.auth-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 460px);
  gap: 48px;
  align-items: center;
  margin: 32px 0;
}
.auth-aside { max-width: 460px; }
.auth-aside h2 { font-size: clamp(26px, 3vw, 34px); font-weight: 650; letter-spacing: -.02em; }
.auth-aside-lede { margin: 12px 0 20px; color: var(--muted); font-size: 15px; }
.auth-points { display: grid; gap: 10px; margin: 0 0 22px; padding: 0; list-style: none; }
.auth-points li { display: flex; gap: 10px; align-items: flex-start; color: var(--muted); }
.auth-points li::before {
  content: "";
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin-top: 3px;
  border-radius: 50%;
  background: var(--accent-soft) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none' stroke='%23c4b5fd' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.4'/%3E%3C/svg%3E") center / 10px no-repeat;
}
.auth-aside-link { font-weight: 600; text-decoration: none; }
.auth-aside-link:hover { text-decoration: underline; }
.auth-card { width: 100%; margin: 0; }
.auth-form { display: grid; gap: 16px; padding: 20px; }
.auth-form button[type="submit"] { margin-top: 4px; }
.auth-alt { padding: 0 20px 20px; color: var(--muted); }

/* ---------- landing ---------- */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 48px;
  align-items: center;
  margin: 28px 0 56px;
}
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: 999px;
  padding: 5px 12px;
  background: var(--accent-soft);
  color: var(--accent-100);
  font-size: 12px;
  font-weight: 600;
}
.hero-title { font-size: clamp(32px, 4.6vw, 50px); font-weight: 650; line-height: 1.1; letter-spacing: -.03em; }
.hero-lede { max-width: 56ch; margin: 18px 0 26px; color: var(--muted); font-size: 16px; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.hero-actions .button { min-height: 44px; padding: 12px 20px; }
.hero-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin: 40px 0 0; }
.hero-stats dt { margin-bottom: 4px; color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.hero-stats dd { margin: 0; font-weight: 600; }

.hero-panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 20px;
  background: linear-gradient(150deg, rgba(23, 31, 45, .96), rgba(13, 18, 27, .96));
  box-shadow: var(--shadow-md);
  animation: rise 500ms var(--ease) both;
  animation-delay: 120ms;
}
.hero-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.hero-score { display: flex; align-items: baseline; gap: 2px; margin: 14px 0 18px; color: var(--yellow); }
.hero-score strong { font-size: 46px; font-weight: 650; letter-spacing: -.03em; line-height: 1; }
.hero-score span { color: var(--muted); }
.hero-findings { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.hero-findings li {
  display: flex;
  gap: 12px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 12px;
  background: rgba(8, 11, 16, .45);
}
.hero-findings .finding-marker { height: 30px; }
.hero-findings strong, .hero-findings small { display: block; }
.hero-findings strong { color: var(--text); font-weight: 600; }
.hero-findings small { color: var(--muted); font-size: 12px; }

.feature-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 56px; }
.feature-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 22px;
  background: linear-gradient(150deg, rgba(23, 31, 45, .8), rgba(13, 18, 27, .8));
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.feature-card:hover { border-color: var(--line-strong); transform: translateY(-3px); }
.feature-card h3 { margin: 14px 0 8px; font-size: 16px; font-weight: 600; }
.feature-card p { margin: 0; color: var(--muted); }
.feature-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
}

/* ---------- landing showcase ---------- */
.showcase {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
  margin-bottom: 72px;
}
.showcase-flip .showcase-copy { order: 2; }
.showcase-copy h2 { margin: 14px 0 12px; font-size: clamp(24px, 2.8vw, 32px); font-weight: 650; letter-spacing: -.02em; }
.showcase-copy p { margin: 0 0 18px; color: var(--muted); font-size: 15px; }
.step-chip {
  display: inline-flex;
  border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: 999px;
  padding: 4px 11px;
  background: var(--accent-soft);
  color: var(--accent-100);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.showcase-points { display: grid; gap: 9px; margin: 0; padding: 0; list-style: none; }
.showcase-points li { display: flex; gap: 10px; align-items: flex-start; color: var(--muted); }
.showcase-points li::before {
  content: "";
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin-top: 3px;
  border-radius: 50%;
  background: var(--accent-soft) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none' stroke='%23c4b5fd' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.4'/%3E%3C/svg%3E") center / 10px no-repeat;
}

.mock-window {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(150deg, rgba(23, 31, 45, .96), rgba(10, 14, 21, .96));
  box-shadow: 0 30px 60px rgba(0, 0, 0, .4);
}
.mock-bar { display: flex; gap: 6px; padding: 12px 14px; border-bottom: 1px solid var(--line); background: rgba(8, 11, 16, .6); }
.mock-bar span { width: 10px; height: 10px; border-radius: 50%; background: #26303e; }
.mock-bar span:first-child { background: rgba(239, 90, 90, .5); }
.mock-bar span:nth-child(2) { background: rgba(232, 178, 60, .5); }
.mock-bar span:nth-child(3) { background: rgba(53, 201, 139, .5); }
.mock-body { display: grid; gap: 10px; padding: 18px; }
.mock-row {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 11px 12px;
  background: rgba(8, 11, 16, .45);
}
.mock-row.is-dim { opacity: .55; }
.mock-row b, .mock-row i { display: block; }
.mock-row b { font-size: 13px; font-weight: 600; }
.mock-row i { color: var(--muted); font-size: 11px; font-style: normal; }
.mock-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: var(--accent-soft);
  color: var(--accent-200);
  font-size: 10px;
  font-weight: 700;
}
.mock-progress { height: 6px; margin-top: 4px; overflow: hidden; border-radius: 999px; background: #1a2230; }
.mock-progress span { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--cyan)); }
.mock-caption { margin: 0; color: var(--muted); font-size: 12px; }
.mock-stream { gap: 8px; }
.mock-packet {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  background: rgba(8, 11, 16, .45);
  font-size: 12px;
}
.mock-packet code { overflow: hidden; color: #dbeafe; text-overflow: ellipsis; white-space: nowrap; }
.mock-score { display: flex; justify-content: center; padding: 6px 0 10px; }
.mock-score .risk-score { width: auto; padding: 8px 16px; }
.mock-score .risk-score strong { font-size: 26px; }
.mock-contrib {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: rgba(8, 11, 16, .45);
  font-size: 13px;
}
.mock-contrib b { border-radius: 6px; padding: 2px 8px; font-variant-numeric: tabular-nums; }

.steps { margin-bottom: 40px; }
.steps .panel-heading { padding-inline: 0; border-bottom: 0; }
.step-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.step-list li {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 20px;
  background: rgba(8, 11, 16, .4);
}
.step-number {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-100);
  font-weight: 650;
}
.step-list strong { display: block; margin-bottom: 6px; font-weight: 600; }
.step-list p { margin: 0; color: var(--muted); }

/* ---------- full-bleed bands ----------
   A strip that breaks out of the centred content column to the viewport edges,
   separating one part of the story from the next. `overflow-x: clip` on <body>
   contains the 100vw width without creating a scroll container, which is what
   `overflow-x: hidden` would do — and that would kill every sticky rail. */
.band {
  width: 100vw;
  /* The band separates one part of the story from the next, so it needs air on
     both sides — without it the strip collides with the section above and below. */
  margin-block: 64px;
  margin-inline: calc(50% - 50vw);
  border-block: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(8, 11, 16, .86), rgba(10, 14, 21, .94));
}
/* The strip runs to the viewport edges, but its columns do not: on a very wide
   screen a full-width grid drags the four items so far apart they stop reading
   as one row. Wider than the content column, capped well short of the viewport. */
.band-inner {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  width: min(1560px, calc(100% - 48px));
  margin-inline: auto;
}
.band-item {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 22px 24px;
  border-left: 1px solid var(--line);
}
.band-item:first-child { border-left: 0; padding-left: 0; }
.band-item:last-child { padding-right: 0; }
.band-item h3 { margin: 0 0 5px; font-size: 14px; font-weight: 650; }
.band-item p { margin: 0; color: var(--muted); font-size: 13px; }
.band-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(var(--accent-rgb), .32);
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-200);
}
.band-icon svg { width: 16px; height: 16px; }

/* The status band reads as a live readout, not a feature. */
.status-band .band-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: min(1200px, calc(100% - 48px));
  padding: 20px 0;
}
.status-band strong { display: block; font-weight: 650; }
.status-band p { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
.status-band .status-chip { font-size: 14px; }

@media (max-width: 1100px) {
  .hero, .auth-shell, .showcase { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .showcase { margin-bottom: 48px; }
  .showcase-flip .showcase-copy { order: 0; }
  .auth-aside { max-width: none; }
  .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .step-list { grid-template-columns: minmax(0, 1fr); }
  .band-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two columns: the left-hand divider only belongs on odd items now. */
  .band-item:nth-child(odd) { border-left: 0; padding-left: 4px; }
  .band-item:nth-child(n + 3) { border-top: 1px solid var(--line); }

  /* No room for a side rail: both levels lay out as horizontal strips that
     still stick under the top bar, so navigation survives the narrow layout.

     The strips are swiped, not dragged by a scrollbar, and a bar drawn under each
     of them is two grey lines of chrome pinned below the navigation on every
     screen. A tab cut off mid-chip already says "there is more this way", and the
     buttons stay reachable by keyboard, which is what actually matters here. */
  .workspace { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .workspace-rail { top: calc(var(--topbar-h, 90px) + 8px); gap: 8px; max-height: none; overflow: visible; }
  .tab-rail, .section-rail { scrollbar-width: none; }
  .tab-rail::-webkit-scrollbar, .section-rail::-webkit-scrollbar { display: none; }
  .tab-rail { grid-auto-flow: column; grid-auto-columns: minmax(118px, 1fr); gap: 6px; overflow-x: auto; }
  .tab-rail button { justify-content: center; text-align: center; }
  .section-rail { display: flex; gap: 10px; align-items: center; overflow-x: auto; padding: 9px 12px; }
  .rail-title { flex: 0 0 auto; margin: 0; padding: 0; }
  .section-links { display: flex; gap: 6px; }
  .section-link {
    flex: 0 0 auto;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 5px 12px;
    white-space: nowrap;
  }
  .section-link:hover, .section-link.is-current { border-color: var(--line-strong); }
  .section-link.is-current { border-color: rgba(var(--accent-rgb), .45); }
}

@media (max-width: 900px) {
  .summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .coverage-overview { grid-template-columns: minmax(0, 1fr); }
  .coverage-completion { border-right: 0; border-bottom: 1px solid var(--line); }
  .coverage-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .coverage-metrics > div:nth-child(3) { border-right: 0; }
  .coverage-metrics > div:nth-child(-n+3) { border-bottom: 1px solid rgba(133, 147, 166, .12); }
  .upload-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dns-section-split { grid-template-columns: minmax(0, 1fr); }
  .filter-bar input, .filter-bar select { min-width: 0; flex: 1 1 100%; }
  .scan-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .indicator-card { grid-template-columns: 1fr auto; }
  .indicator-provider { display: none; }
  .download-grid { grid-template-columns: 1fr; }
  .filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .artifact-summary { grid-template-columns: 52px minmax(0, 1fr); }
  .artifact-summary .pill { grid-column: 2; width: fit-content; }
}

@media (max-width: 620px) {
  main { width: min(100% - 20px, 1500px); margin-top: 14px; }
  .topbar-inner { align-items: flex-start; padding-block: 14px; }
  .topnav { flex-wrap: wrap; justify-content: flex-end; gap: 4px; }
  .tab-list, .workspace-rail { position: static; }
  .evidence-search { flex-wrap: wrap; }
  .evidence-search input { flex: 1 1 100%; }
  .evidence-search button, .evidence-search .button { flex: 1 1 auto; }
  .feature-grid { grid-template-columns: minmax(0, 1fr); }
  .band-inner { grid-template-columns: minmax(0, 1fr); }
  .band-item, .band-item:nth-child(odd) { border-left: 0; padding: 20px 4px; }
  .band-item + .band-item { border-top: 1px solid var(--line); }
  .status-band .band-inner { flex-direction: column; align-items: flex-start; }
  .hero-stats { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .hero-actions .button { width: 100%; }
  .brand-text .eyebrow { display: none; }
  .page-heading { align-items: flex-start; flex-direction: column; }
  .risk-badge { width: 100%; }
  .summary-grid, .scan-summary, .account-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .coverage-completion { padding-inline: 16px; }
  .coverage-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .coverage-metrics > div:nth-child(3) { border-right: 1px solid rgba(133, 147, 166, .12); }
  .coverage-metrics > div:nth-child(even) { border-right: 0; }
  .coverage-metrics > div:nth-child(-n+4) { border-bottom: 1px solid rgba(133, 147, 166, .12); }
  .coverage-details > summary { padding-inline: 16px; }
  .summary-grid strong { font-size: 24px; }
  .split-heading { align-items: flex-start; flex-direction: column; }
  .panel-collapse-summary { width: 100%; }
  .panel-collapse-summary .heading-help { flex: 1 1 auto; }
  .score-list { --score-columns: repeat(3, minmax(0, 1fr)); }
  /* Preserve the desktop reading order while wrapping the descriptive finding
     above its three compact metrics. */
  .score-legend span:first-child, .score-copy { grid-column: 1 / -1; grid-row: 1; }
  .score-legend span:nth-child(n+2),
  .score-row > .pill,
  .score-confidence,
  .score-value { grid-row: 2; }
  .finding-list { --finding-columns: 7px minmax(100px, 1fr) 92px; }
  .finding-legend, .finding-card > summary { row-gap: 8px; }
  .finding-legend .legend-marker, .finding-marker { grid-column: 1; grid-row: 1 / 3; }
  .finding-legend > span:nth-child(2), .finding-title { grid-column: 2 / -1; grid-row: 1; }
  .finding-legend .legend-confidence, .finding-card .confidence {
    display: block;
    grid-column: 2;
    grid-row: 2;
    justify-self: start;
  }
  .finding-legend > span:nth-child(4), .finding-card > summary > .pill {
    grid-column: 3;
    grid-row: 2;
    justify-self: start;
  }
  .finding-legend .legend-disclosure, .finding-card .disclosure { display: none; }
  .finding-evidence { padding-left: 20px; }
  .download-card { grid-template-columns: 44px 1fr; }
  .download-card .button { grid-column: 1 / -1; }
  .delivery-row, .session-action { align-items: stretch; flex-direction: column; }
  .delivery-row button, .session-action button { width: 100%; }
  .session-row { grid-template-columns: minmax(0, 1fr) auto; }
  .session-row form { grid-column: 1 / -1; }
  .session-row button { width: 100%; }
  .search-input { flex-direction: column; }
  .upload-fields { grid-template-columns: 1fr; }
  .filter-grid, .account-details { grid-template-columns: 1fr; }
  .filter-actions { flex-direction: column; }
  .audit-event { grid-template-columns: 10px minmax(0, 1fr); }
  .audit-time { grid-column: 2; justify-self: start; text-align: left; }
  .audit-details { grid-column: 2; }
  .form-action, .artifact-action { align-items: stretch; flex-direction: column; }
  .form-action small { margin-right: 0; }
  .artifact-action .button, .form-action button { width: 100%; }
  .group-manager, .group-rename-form { align-items: stretch; flex-direction: column; }
  .group-manager > div { margin-right: 0; }
  .group-rename-form input { min-width: 0; }

  /* Dense evidence tables scroll sideways instead of crushing every column into
     an unreadable sliver. Column alignment is the whole point of those, and you
     open a row to read it properly anyway. */
  .table-wrap > table { min-width: 680px; }
  .table-wrap > .answer-table, .table-wrap > .record-table { min-width: 520px; }

  /* The console's own tables are a different matter. They are not something you
     read across, they are something you act on — and a Scan now button parked
     three columns off the right edge of a phone may as well not exist. Below this
     width each row becomes a card and each cell names itself from the header it
     came from, so nothing is reachable only by scrolling sideways. */
  .table-wrap > .stack-table { min-width: 0; table-layout: auto; }
  .stack-table, .stack-table > tbody, .stack-table > tbody > tr, .stack-table > tbody > tr > td { display: block; }
  .stack-table > thead { display: none; }
  .stack-table > tbody > tr {
    margin-bottom: 10px;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    padding: 4px 14px;
    background: rgba(8, 11, 16, .4);
  }
  .stack-table > tbody > tr:hover { background: rgba(8, 11, 16, .4); }
  .stack-table > tbody > tr > td {
    display: grid;
    grid-template-columns: minmax(0, 36%) minmax(0, 1fr);
    gap: 4px 14px;
    align-items: start;
    padding: 11px 0;
    border-bottom: 1px solid var(--line);
  }
  .stack-table > tbody > tr > td::before {
    content: attr(data-label);
    grid-area: 1 / 1;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    line-height: 1.6;
  }
  /* The label owns the first column and nothing else may enter it. A cell that
     holds a value and a note beneath it — a device and its id, a platform and its
     app version — would otherwise let the note wrap back under the label, which
     reads as a second label rather than as part of the value. */
  .stack-table > tbody > tr > td > * { grid-column: 2; }
  .stack-table > tbody > tr > td:last-child { border-bottom: 0; }
  /* The "nothing here" row is a message, not a record: it has no header to name. */
  .stack-table > tbody > tr > td.empty { display: block; }
  .stack-table > tbody > tr > td.empty::before { content: none; }
  .stack-table > tbody > tr:has(> td.empty) { border: 0; background: none; }
  .stack-table .action-stack { min-width: 0; }
  .panel-heading, .panel-body, .upload-form, .evidence-summary { padding-inline: 16px; }
  .dns-sections { padding-inline: 16px; }

  dialog.modal { width: calc(100vw - 24px); }
  .modal-foot { flex-direction: column-reverse; }
  .modal-foot button { width: 100%; }
}

@media (max-width: 430px) {
  main { margin-top: 10px; }
  .topbar-inner { gap: 12px; }
  .brand-name { font-size: 17px; }
  .summary-grid, .scan-summary, .account-summary { grid-template-columns: minmax(0, 1fr); }
  .hero-title { font-size: 30px; }
  .tab-list button { min-width: 0; flex: 1 0 auto; padding-inline: 12px; }
  .page-heading h2 { font-size: 24px; }
  .risk-score { width: 78px; }
  .action-stack { grid-template-columns: minmax(0, 1fr); min-width: 0; }
  .dropzone { padding: 16px 12px; }
  .pagination { flex-wrap: wrap; gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  button:hover, .button:hover, .summary-grid article:hover, .feature-card:hover,
  .download-card:hover, .indicator-card:hover, .back-link:hover,
  .dropzone.is-dragging { transform: none; }
}
