/* Base + primitives for tamp.findings.

   Hand-rolled on purpose. The design needs about a dozen primitives and no
   complex widgets — no date picker, no rich combobox, no data grid. A Blazor
   component library would bring rounded Material surfaces, elevation and its
   own type scale, all of which fight square corners, hairline borders and
   registration marks. See docs/redesign/README.md "Fidelity".

   Classes here are consumed by the .razor primitives in Components/Primitives.
   Screens should use those components, not these class names directly. */

@import url('fonts.css');
@import url('tokens.css');

/* ── Reset ──────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0;
}

h1 { font-size: var(--text-screen-title); }
h2 { font-size: var(--text-card-title); }
h3 { font-size: var(--text-emphasis); }

p { margin: 0 0 var(--space-3); }
a { color: var(--color-accent-700); text-underline-offset: 3px; }
code, kbd, samp, pre { font-family: var(--font-mono); }

/* Never the browser default. */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* ── Text roles ─────────────────────────────────────────────────────── */

.kicker {
  font-size: var(--text-kicker);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-neutral-500);
}
.muted { color: var(--color-neutral-500); }
.mono  { font-family: var(--font-mono); }
.num   { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Column headers: 9px uppercase, the widest tracking in the system. */
.col-head {
  font-size: var(--text-column);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-neutral-500);
  font-weight: 400;
  text-align: left;
}

/* ── Blueprint card ─────────────────────────────────────────────────────
   Cards are transparent line drawings: a hairline border, no fill, and four
   "+" registration marks that sit OUTSIDE the box.

   That last part matters: a scroll container counts the marks as overflow,
   so never put overflow on .blueprint itself — put it on an inner wrapper.
   The POA&M dialog is the case that bites. */

.blueprint {
  position: relative;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius);
  background: transparent;
}
.blueprint > .corner {
  position: absolute;
  width: 11px; height: 11px;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
  pointer-events: none;
}
.blueprint > .corner::before,
.blueprint > .corner::after { content: ""; position: absolute; background: currentColor; }
.blueprint > .corner::before { left: 5px; top: 0; width: 1px; height: 100%; }
.blueprint > .corner::after  { top: 5px; left: 0; width: 100%; height: 1px; }
.blueprint > .corner.tl { top: -6px; left: -6px; }
.blueprint > .corner.tr { top: -6px; right: -6px; }
.blueprint > .corner.bl { bottom: -6px; left: -6px; }
.blueprint > .corner.br { bottom: -6px; right: -6px; }

.blueprint--surface { background: var(--color-neutral-100); }
.card-body { padding: var(--space-4); }

/* ── Two-dimensional scroll container (ADR 0003) ────────────────────────
   The ONLY sanctioned way to scroll horizontally. SC 1.4.10 exempts content
   requiring two-dimensional layout for usage or meaning; the exception is
   claimed by five named components (source viewer, explorer two-pane grid,
   wide data tables, policy category grid, DAST request/response panels).
   The page body must never scroll horizontally at 320px. */

.scroll-x {
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
}

/* ── Buttons ────────────────────────────────────────────────────────────
   The primary button is the one solid object in the system. */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-body);
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover:not(:disabled) { background: var(--hover-nav); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn--primary {
  position: relative;
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-solid);
  font-weight: 500;
}
.btn--primary:hover:not(:disabled) { background: var(--color-accent-600); border-color: var(--color-accent-600); }

.btn--danger  { color: var(--color-critical); border-color: color-mix(in srgb, var(--color-critical) 55%, transparent); }
.btn--warning { color: var(--color-high);     border-color: color-mix(in srgb, var(--color-high) 55%, transparent); }

/* Text buttons — row actions. Stop propagation at the component level. */
.btn--text {
  border: 0; padding: 0; background: none;
  color: var(--color-accent-700);
  text-decoration: underline; text-underline-offset: 3px;
}
.btn--text:hover:not(:disabled) { background: none; color: var(--color-accent-800); }
.btn--text.btn--danger { color: var(--color-critical); }

/* ── Chips ──────────────────────────────────────────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 1px var(--space-2);
  border: 1px solid currentColor;
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-kicker);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.6;
  white-space: nowrap;
}
/* Outlined: 14% tint fill in the chip's own colour. */
.chip--outlined { background: color-mix(in srgb, currentColor 14%, transparent); }
/* Solid: the chip's colour becomes the fill and text goes to the dark ink. */
.chip--solid { background: currentColor; border-color: currentColor; }
.chip--solid > * { color: var(--color-on-solid); }
.chip--dashed { border-style: dashed; background: color-mix(in srgb, currentColor 14%, transparent); }

/* ── Verdict marks (docs/redesign/gate-verdict-marks.md) ────────────────
   Four states. Each pairs a glyph, a written tag and a colour, so colour is
   never the sole signal. Dashed means "nothing happened here" — the same
   idea the scan-receipt cards use for a class that never ran. */

.mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border: 1px solid currentColor;
  border-radius: var(--radius);
  font-size: 11px;
  line-height: 1;
  flex: none;
}
.mark--pass    { color: var(--color-accent); }
.mark--fail    { color: var(--color-critical); background: currentColor; }
.mark--unknown { color: var(--color-medium); border-style: dashed;
                 background: color-mix(in srgb, var(--color-medium) 14%, transparent); }
.mark--error   { color: var(--color-high); background: currentColor; }
.mark--fail > *, .mark--error > * { color: var(--color-on-solid); }

/* ── Tables ─────────────────────────────────────────────────────────── */

.table { width: 100%; border-collapse: collapse; font-size: var(--text-body); }
.table th {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-divider);
  font-size: var(--text-column);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-neutral-500);
  font-weight: 400;
  text-align: left;
  white-space: nowrap;
}
.table td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-divider); }
.table tbody tr:last-child td { border-bottom: 0; }
.table--rows tbody tr { cursor: pointer; }
.table--rows tbody tr:hover { background: var(--hover-row); }
.table .num, .table .align-end { text-align: right; }

/* Compact density — a persona setting, not a screen setting. */
.density-compact .table th,
.density-compact .table td { padding: var(--space-1) var(--space-2); }

/* ── Tabs ───────────────────────────────────────────────────────────────
   The active tab takes the page background, a 1px accent top border and a
   -1px bottom margin so it merges with the content below. */

.tabs {
  display: flex;
  gap: 2px;
  background: var(--color-neutral-100);
  border-bottom: 1px solid var(--color-divider);
  padding: 0 var(--shell-padding);
  /* Scrolls INSIDE ITSELF, so the page body still does not scroll in two
     dimensions. This is the ordinary responsive-nav pattern rather than a
     claim on SC 1.4.10's two-dimensional exception — no content is lost, the
     strip simply pans. */
  overflow-x: auto;
}
.tab {
  padding: var(--space-2) var(--space-4);
  border: 1px solid transparent;
  border-bottom: 0;
  background: none;
  color: var(--color-neutral-600);
  font: inherit;
  font-size: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
}
.tab:hover { color: var(--color-text); background: var(--hover-nav); }
.tab--active {
  background: var(--color-bg);
  color: var(--color-text);
  border-color: var(--color-divider);
  border-top: 1px solid var(--color-accent);
  margin-bottom: -1px;
}
/* Instance-level: the System tab is outside project scope and says so. */
.tab--system.tab--active { border-top-color: var(--color-accent-800); }

/* Spine chips — the explorer's five spines. */
.spine {
  /* Anchors now, not buttons (TFND-27): everything in this product is
     addressable, and a button cannot be middle-clicked or opened in a new tab.
     The reset below is what a link needs to keep looking like a chip. */
  display: inline-block;
  text-decoration: none;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius);
  background: none;
  color: var(--color-neutral-600);
  font-family: var(--font-mono);
  font-size: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
}
.spine--active {
  border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 18%, transparent);
  color: var(--color-text);
}

/* ── Tree rows ──────────────────────────────────────────────────────── */

.tree-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--text-secondary);
  text-align: left;
  cursor: pointer;
}
.tree-row:hover { background: var(--hover-tree); }
.tree-row--selected { background: var(--color-accent-200); border-left-color: var(--color-accent); }
/* Group rows are structure, not targets. */
.tree-row--group { font-weight: 600; cursor: default; }
.tree-row--group:hover { background: none; }
.tree-row__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
}

/* ── Form controls ──────────────────────────────────────────────────── */

.field { display: block; margin-bottom: var(--space-4); }
.field__label { display: block; font-size: var(--text-secondary); margin-bottom: var(--space-1); }
/* Helper text sits BENEATH the input, never as a placeholder — helper text
   has to survive typing. */
.field__help { display: block; font-size: var(--text-kicker); color: var(--color-neutral-500); margin-top: var(--space-1); }

.input, .textarea, .select {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius);
  background: var(--color-neutral-100);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-body);
}
.input:disabled, .textarea:disabled, .select:disabled { opacity: 0.5; cursor: not-allowed; }
.input--mono { font-family: var(--font-mono); }
.textarea { min-height: 72px; resize: vertical; }

.check { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; font-size: var(--text-body); }
.check input { accent-color: var(--color-accent); width: 13px; height: 13px; cursor: pointer; }
.check input:disabled, .check:has(input:disabled) { cursor: not-allowed; opacity: 0.5; }

/* ── Dialog ─────────────────────────────────────────────────────────────
   Scroll goes on the INNER wrapper. The registration marks sit outside the
   framed box, and a scroll container counts them as overflow. */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: rgba(8, 10, 12, 0.62);
}
.dialog {
  width: 100%;
  max-height: calc(100vh - var(--space-8) * 2);
  background: var(--color-neutral-100);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
}
.dialog__scroll { overflow-y: auto; padding: var(--space-4); }
.dialog__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
}
.dialog__foot {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-divider);
}

/* ── Reveal-once secret panel ───────────────────────────────────────── */

.secret {
  border: 1px solid var(--color-accent-600);
  background: var(--color-accent-100);
  padding: var(--space-3);
}
.secret__head {
  font-size: var(--text-kicker);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-accent-800);
  margin-bottom: var(--space-2);
}

/* ── Layout ─────────────────────────────────────────────────────────────
   A flex row above the dense breakpoint, a column below it. The sidebar is a
   fixed 238px column beside the content, or a full-width band above it — a
   fixed column at 320px would guarantee the two-dimensional scrolling ADR
   0003 forbids. */

.layout { display: flex; align-items: stretch; min-height: 100vh; }
.layout__main { flex: 1 1 auto; min-width: 0; }

.sidebar {
  flex: none;
  width: var(--sidebar-width);
  background: var(--color-neutral-100);
  border-right: 1px solid var(--color-divider);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

@media (max-width: 900px) {
  .layout { flex-direction: column; }
  .sidebar { width: auto; border-right: 0; border-bottom: 1px solid var(--color-divider); }
}

.sidebar__brand { line-height: 1.1; }
.brand { font-family: var(--font-heading); font-weight: 600; font-size: 20px; }
.brand__accent { color: var(--color-accent-700); }

.sidebar__scope { padding: var(--space-2); }
.scope-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
}
.scope-row__label {
  grid-column: 1;
  font-size: var(--text-column);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-neutral-500);
}
.scope-row__value {
  grid-column: 1;
  font-size: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scope-row__chevron { grid-column: 2; grid-row: 1 / span 2; color: var(--color-neutral-500); }

.navgroup { display: flex; flex-direction: column; }
.navgroup__label {
  font-size: var(--text-column);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-neutral-500);
  margin-bottom: var(--space-1);
}
/* Instance-level: outside any client or project scope, and the nav says so. */
.navgroup--instance {
  border-top: 1px solid var(--color-divider);
  padding-top: 14px;
  margin-top: var(--space-2);
}
.navgroup--instance .navgroup__label { color: var(--color-accent-700); }

.navitem {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-left: 3px solid transparent;
  color: var(--color-text);
  text-decoration: none;
  font-size: var(--text-secondary);
}
.navitem:hover { background: var(--hover-nav); }
.navitem--active {
  background: var(--color-accent);
  color: var(--color-on-solid);
  border-left-color: var(--color-accent-900);
}
.navitem__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.navitem__count { font-size: var(--text-kicker); flex: none; }

.sidebar__foot {
  margin-top: auto;
  border-top: 1px solid var(--color-divider);
  padding-top: var(--space-2);
  font-size: var(--text-secondary);
}

/* ── Header ─────────────────────────────────────────────────────────── */

.appheader { border-bottom: 1px solid var(--color-divider); }

.urlrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--shell-padding);
  flex-wrap: wrap;
}
.urlrow__right { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.urlchip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid var(--color-divider);
  padding: var(--space-1) var(--space-2);
  min-width: 0;
  flex: 1 1 260px;
}
/* Truncates rather than pushing the row wide: a deep explorer selection can be
   a long path, and the page must still reflow at 320px (ADR 0003). */
.urlchip__path {
  font-size: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.urlchip__copy { flex: none; text-decoration: none; gap: var(--space-1); }

/* ── Account control ────────────────────────────────────────────────── */

.account { position: relative; }
.account__trigger {
  display: flex; align-items: center; gap: var(--space-2);
  background: none; border: 0; padding: 0; cursor: pointer;
  color: inherit; font: inherit;
}
.account__login { font-size: var(--text-secondary); }

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none;
  background: var(--color-accent);
  color: var(--color-on-solid);
  font-size: var(--text-kicker); font-weight: 500;
}
.avatar--lg { width: 32px; height: 32px; font-size: var(--text-secondary); }

/* Sits under the menu so a click anywhere else closes it. */
.account__scrim { position: fixed; inset: 0; z-index: 40; }

.account__menu {
  position: absolute; right: 0; top: calc(100% + var(--space-2));
  width: 246px; z-index: 41;
  box-shadow: var(--shadow-lg);
}
.account__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: 1px solid var(--color-divider);
}
.account__item {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-divider);
  color: inherit; text-decoration: none;
  font-size: var(--text-secondary);
}
.account__item:last-child { border-bottom: 0; }
.account__item:hover { background: var(--hover-nav); }
.account__item-sub { display: block; font-size: var(--text-kicker); color: var(--color-neutral-500); }
.account__item-danger { color: var(--color-critical); }

/* ── Shell ──────────────────────────────────────────────────────────────
   The padding is load-bearing, not decoration: blueprint registration marks
   are positioned 6px OUTSIDE the card border, so a full-bleed card would push
   the body 6px wider than the viewport and scroll it horizontally — the exact
   thing ADR 0003 forbids. --shell-padding (22px) absorbs the overhang.

   Below the dense breakpoint the padding tightens but must never reach zero. */

.shell { padding: var(--shell-padding); }

@media (max-width: 640px) {
  .shell { padding: var(--space-3); }
}

/* ── Project hub ────────────────────────────────────────────────────── */

.hubhead { padding-left: var(--space-3); }

/* Two columns above the dense breakpoint, stacked below it (ADR 0003). The
   minimums are the design's, but they are MINIMUMS for English — verify
   against the pseudo-locale before treating them as settled. */
.hubrow {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}
@media (max-width: 1180px) { .hubrow { grid-template-columns: 1fr; } }

.score { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.score__value { font-family: var(--font-heading); font-weight: 600; font-size: 46px; line-height: 1; }
/* Never wraps: a delta on its own line reads as a separate fact rather than a
   qualifier on the score. */
.score__delta { color: var(--color-accent-700); white-space: nowrap; font-size: var(--text-secondary); }
.score__caption { margin-left: auto; text-align: right; font-size: var(--text-kicker); max-width: 34ch; }

.contrib { margin-top: var(--space-4); }
.contrib__row {
  display: grid;
  grid-template-columns: minmax(120px, 150px) minmax(80px, 1fr) 74px 34px minmax(0, 1.15fr);
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border-left: 2px solid transparent;
}
.contrib__row:hover { background: var(--hover-row); }
/* A category costing points reads hotter. */
.contrib__row--hot { border-left-color: var(--color-critical); }
.contrib__row--hot .contrib__evidence { color: var(--color-text); }
.contrib__row--off { opacity: 0.55; }

.contrib__label { font-size: var(--text-body); }
.contrib__key { font-size: var(--text-column); color: var(--color-neutral-500); }
.contrib__bar { height: 9px; background: var(--color-neutral-200); }
.contrib__fill { height: 100%; }
.contrib__points { font-size: var(--text-secondary); text-align: right; }
.contrib__sat { font-size: var(--text-column); color: var(--color-critical); }
.contrib__evidence { font-size: var(--text-secondary); color: var(--color-neutral-500); min-width: 0; }

@media (max-width: 720px) {
  /* Below this the five-column grid stops being a matrix and becomes a list.
     The relationship between columns is no longer legible at that width, so
     the 1.4.10 exception does not apply — it reflows. */
  .contrib__row { grid-template-columns: 1fr auto; }
  .contrib__bar, .contrib__evidence { grid-column: 1 / -1; }
}

.verdict { margin: var(--space-2) 0; }
.gates { margin-top: var(--space-3); }
.gate {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1.2fr);
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-1) 0;
}
.gate__label { font-size: var(--text-secondary); }
.gate__observed { font-size: var(--text-secondary); min-width: 0; }
@media (max-width: 720px) {
  .gate { grid-template-columns: auto 1fr auto; }
  .gate__observed { grid-column: 1 / -1; }
}

/* ── Scan receipts ──────────────────────────────────────────────────────
   A never-ran class is drawn DIFFERENTLY, not merely greyed. Grey reads as
   "less important"; dashed reads as "nothing happened here" — the same idea
   the gate rail's UNKNOWN mark uses, so the reader learns one convention. */

.receipts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.receipt { border: 1px solid var(--color-divider); padding: var(--space-3); }
.receipt__name { font-size: var(--text-emphasis); font-family: var(--font-heading); font-weight: 600; }
.receipt__mark { font-size: var(--text-emphasis); line-height: 1; }
.receipt__state {
  font-size: var(--text-kicker); text-transform: uppercase; letter-spacing: 0.12em;
}
.receipt__detail { font-size: var(--text-secondary); margin-top: var(--space-1); }

.receipt--clean .receipt__mark { color: var(--color-pass); }
.receipt--findings .receipt__mark { color: var(--color-high); }
.receipt--never {
  border-style: dashed;
  border-color: var(--color-accent-600);
  background: var(--color-accent-100);
}
.receipt--never .receipt__mark { color: var(--color-medium); }

/* ── Build history ──────────────────────────────────────────────────── */

.buildrow-gates { display: inline-flex; gap: var(--space-2); }

/* ── Portfolio ──────────────────────────────────────────────────────── */

/* Blocking reasons are prose and can be long. They wrap rather than truncate:
   the reason IS the value of the column, and an ellipsis would hide the part
   that tells the reader what to do. */
.portfolio__blocking { max-width: 42ch; white-space: normal; }

/* ── Sign-in ────────────────────────────────────────────────────────────
   Centred, narrow, no chrome. Vertically centred with min-height rather than
   a fixed height so it still reflows at 320px (ADR 0003). */

.signin {
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
}
.signin__brand { text-align: center; }
.signin__card { width: 100%; max-width: 420px; }
.signin__card .btn { margin-top: var(--space-3); }

.claim {
  border: 1px dashed var(--color-medium);
  background: color-mix(in srgb, var(--color-medium) 8%, transparent);
  padding: var(--space-3);
  margin: var(--space-3) 0;
}

/* ── Explorer ───────────────────────────────────────────────────────────
   320px tree, detail fills the rest — above the dense breakpoint.

   Below it the two panes become MASTER-DETAIL: the tree, or the detail with a
   way back, never both. The two-pane grid claims SC 1.4.10's
   two-dimensional-layout exception only while it genuinely IS two dimensions;
   at 320px it stops being a matrix and has to reflow (ADR 0003). */

.explorer {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

/* The tree scrolls independently of the page so the detail stays put while
   the reader moves through a long list. */
.explorer__scroll { max-height: 70vh; overflow-y: auto; }

/* Shown only below the breakpoint, where the detail replaces the tree. */
.explorer__back { display: none; }

@media (max-width: 900px) {
  .explorer { grid-template-columns: 1fr; }
  .explorer__scroll { max-height: none; }

  /* With a selection, the detail IS the view: hide the tree and offer a way
     back. Stacking both would make the reader scroll past the whole tree to
     reach what they just clicked. */
  .explorer--detail .explorer__tree { display: none; }
  .explorer--detail .explorer__back {
    display: block;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-divider);
  }
  .explorer:not(.explorer--detail) .explorer__detail { display: none; }
}

/* ── Source viewer ──────────────────────────────────────────────────────
   Fixed 22px rows so the gutter lines up with the code and Virtualize can
   size without measuring.

   THE HEIGHT IS ON THE ROW, NOT THE CODE SPAN. An empty line in a
   baseline-aligned flex row otherwise collapses and the gutter drifts out of
   step with the file — the hand-off calls this out specifically. */

.source {
  font-family: var(--font-mono);
  font-size: 12px;
  /* Virtualize needs a BOUNDED, scrollable container to compute how many rows
     fit. Without a height it renders zero rows — silently, in a real browser,
     with no error anywhere. The viewer would scroll inside the detail pane
     regardless; this is what makes it work at all. */
  max-height: 70vh;
  overflow-y: auto;
}

.source__row {
  display: flex;
  align-items: center;
  height: 22px;
  line-height: 22px;
  border-left: 3px solid transparent;
  white-space: pre;
}

.source__gutter {
  width: 46px;
  flex: none;
  padding-right: var(--space-2);
  text-align: right;
  color: var(--color-neutral-500);
  /* Copying a block of code should give the code, not the code with a line
     number welded to every line. */
  user-select: none;
}

.source__mark { width: 16px; flex: none; display: inline-flex; align-items: center; }
.source__code { min-width: 0; }

.tok-keyword { color: var(--syntax-keyword); }
.tok-type    { color: var(--syntax-type); }
.tok-method  { color: var(--syntax-method); }
.tok-string  { color: var(--syntax-string); }
.tok-number  { color: var(--syntax-number); }
.tok-comment { color: var(--syntax-comment); }
.tok-plain   { color: var(--syntax-plain); }
.tok-punct   { color: var(--syntax-punctuation); }

/* ── DAST ───────────────────────────────────────────────────────────────
   Request and response side by side above the breakpoint, stacked below it.
   The comparison IS the evidence, so the panes claim SC 1.4.10's
   two-dimensional exception — but only while they are genuinely side by side. */

.dast-evidence {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  padding: 0 var(--space-4) var(--space-4);
}
@media (max-width: 900px) { .dast-evidence { grid-template-columns: 1fr; } }

.dast-pane {
  margin: var(--space-1) 0 0;
  padding: var(--space-2);
  background: var(--color-neutral-100);
  border: 1px solid var(--color-divider);
  font-size: var(--text-secondary);
  /* break-all, per the design: a payload has no spaces to wrap on and would
     otherwise push the page sideways. */
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 320px;
  overflow-y: auto;
}

.dup-host {
  border: 1px solid var(--color-high);
  background: color-mix(in srgb, var(--color-high) 8%, transparent);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

/* ── Severity ───────────────────────────────────────────────────────────
   Three redundant signals: ladder, letter, colour. The first two survive a
   monochrome print and a colour-blind reader — the attestation has to be
   readable off a black-and-white printer. */

.sev { display: inline-flex; align-items: center; gap: var(--space-1); white-space: nowrap; }
.sev__ladder { font-size: var(--text-kicker); letter-spacing: -1px; }
.sev__letter { font-family: var(--font-mono); font-size: var(--text-kicker); font-weight: 500; }
.sev__name { font-size: var(--text-secondary); }

/* ── Utility ────────────────────────────────────────────────────────── */

.hr { height: 1px; border: 0; background: var(--color-divider); margin: var(--space-4) 0; }
.row { display: flex; align-items: center; gap: var(--space-3); }
.row--between { justify-content: space-between; }
.stack > * + * { margin-top: var(--space-3); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------------------
   POA&M (TFND-95 … TFND-98)
   --------------------------------------------------------------------------- */

/* Stats strip: 26px numerals over 10px uppercase labels, laid out so a reader
   compares the numbers rather than reading them as a sentence.

   auto-fit rather than a fixed column count: the strip has five entries today
   and the screen still has to work at the 1180px density floor and below. */
.poam-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: var(--space-4);
}
.stat__value {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 26px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat__label {
  margin-top: var(--space-1);
  font-size: var(--text-kicker);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-neutral-500);
}

/* The federal-template fields as a two-column grid. Collapses to one column
   below the density floor: these are prose fields, not a data table, so they
   reflow rather than claiming the 1.4.10 exception. */
.poam-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}
.poam-grid p { margin-top: var(--space-1); }

/* The selected row in a selectable table. Left tick plus a tint, so the
   selection survives a monochrome print. */
.table--rows tbody tr.is-selected {
  background: color-mix(in srgb, var(--color-accent) 10%, transparent);
  box-shadow: inset 3px 0 0 var(--color-accent);
}

/* A retired VEX statement: still listed, visibly no longer the answer of
   record. Dimmed rather than struck through — a strikethrough on a table row
   reads as "wrong", and a retired statement was not wrong, it stopped
   applying. */
.table--rows tbody tr.is-retired { opacity: 0.55; cursor: default; }
.table--rows tbody tr.is-retired:hover { background: transparent; }

/* ---------------------------------------------------------------------------
   Attestation (TFND-100 … TFND-103)

   The deliverable. Someone signs this and attaches it to a contract package,
   so the print rules below are not a nicety — the printed copy IS the artefact.
   --------------------------------------------------------------------------- */

.attest { max-width: 1080px; display: flex; flex-direction: column; gap: var(--space-4); }
.attest__title { font-size: 30px; }

/* Practices on the left, the build stamp on the right. Collapses below the
   density floor: this is a reading column plus a sidebar, not a data table, so
   it reflows rather than claiming the 1.4.10 exception. */
.attest__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--space-4);
  align-items: start;
}
@media (max-width: 1180px) { .attest__body { grid-template-columns: 1fr; } }

.attest__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-3);
}
.tally { display: flex; align-items: center; gap: var(--space-2); }
.tally__glyph { font-size: 22px; line-height: 1; }
.tally__value { font-family: var(--font-heading); font-weight: 600; font-size: 26px; line-height: 1; }
.tally__label {
  font-size: var(--text-kicker);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-neutral-500);
}

/* Practice id, status chip, then the stack of label / intent / evidence. The
   id column is fixed so the ids line up down the page — an assessor reads this
   by practice number. */
.practice {
  display: grid;
  grid-template-columns: 62px 92px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
}
@media (max-width: 720px) { .practice { grid-template-columns: 1fr; } }
.practice__id { font-weight: 600; }
.practice__evidence {
  margin-top: var(--space-1);
  font-size: var(--text-secondary);
  color: var(--color-neutral-600);
  overflow-wrap: anywhere;
}

.stamp { display: grid; grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-3); margin-top: var(--space-2); }
.stamp dt { font-size: var(--text-kicker); text-transform: uppercase; letter-spacing: 0.12em; color: var(--color-neutral-500); }
.stamp dd { font-size: var(--text-secondary); }

.signature { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); margin-top: var(--space-5); }
.signature__rule { display: block; border-bottom: 1px solid var(--color-neutral-400); height: 28px; margin-bottom: var(--space-1); }

/* Export format cards. A radio mark rather than a real <input> because the
   whole card is the target — a 3px radio button is not. */
.format {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-neutral-300);
  cursor: pointer;
  margin-bottom: var(--space-2);
}
.format--on { border-color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 8%, transparent); }
.format__mark { color: var(--color-accent); }

@media print {
  /* The page chrome is navigation. A printed attestation carries none of it,
     and a sidebar reproduced on paper wastes a third of every sheet. */
  .sidebar, .app-header, .attest__actions, .overlay { display: none !important; }
  .app-main, .attest { max-width: none; margin: 0; padding: 0; }

  /* The build stamp belongs ABOVE the practices on paper: a two-column grid
     splits across page breaks and strands the stamp on its own sheet. */
  .attest__body { display: block; }
  .attest__rail { margin-bottom: var(--space-4); }

  /* A practice split across a page break is unreadable, and the signature
     block on its own page is how an attestation gets signed without its
     evidence attached. */
  .practice, .signature, .tally { break-inside: avoid; }
  .card { border-color: #999; }

  a[href]::after { content: ""; }
}

/* ---------------------------------------------------------------------------
   Policy & gates (TFND-104 … TFND-106)
   --------------------------------------------------------------------------- */

.policy-library {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-3);
}
.policy-card--on {
  border-color: var(--color-accent);
  box-shadow: inset 3px 0 0 var(--color-accent);
}

/* Categories left, bands and gates right. Both halves are dense tables of
   numbers, so this claims the 1.4.10 two-dimensional exception per ADR 0003
   rather than reflowing into a single column that would separate a weight from
   the effective maximum it produces. */
.policy-editor {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}
@media (max-width: 1180px) { .policy-editor { grid-template-columns: 1fr; } }

/* Numeric inputs are right-aligned and narrow: a weight is two digits, and a
   full-width box invites someone to type prose into it. */
.input--num {
  width: 72px;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.band-grid { display: grid; gap: var(--space-2); }
.band { display: grid; grid-template-columns: 14px 1fr auto; gap: var(--space-2); align-items: center; }
.band__swatch { width: 14px; height: 14px; display: inline-block; }

/* Settings tab strip sits inside the page rather than under the header, so it
   keeps the tab visuals but not the shell's edge-to-edge padding. */
.settings-tabs { padding: 0; }

/* The curl example. Collapsed by default: it is reference material, and an
   open code block between the token and the table pushes the list of existing
   tokens off the first screen. */
.curl { margin-top: var(--space-3); }
.curl summary { cursor: pointer; font-size: var(--text-secondary); color: var(--color-accent-700); }

/* ---------------------------------------------------------------------------
   Pending states (TFND-116)

   A qualifier beside the status chip, not a replacement for it. Amber, because
   an undecided thing is the same kind of fact as an unanswered gate — and a
   glyph, because the distinction has to survive a monochrome print like every
   other status in this product.
   --------------------------------------------------------------------------- */
.pending {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-high);
  font-size: var(--text-secondary);
  white-space: nowrap;
}
.pending__dot { font-size: var(--text-emphasis); line-height: 1; }

/* Dependency paths (TFND-7 / F6.2).
   Read downwards: leftmost is what the team chose to depend on, rightmost is
   the package they are looking at. The chain wraps rather than scrolling —
   a deep path is exactly when a reader needs to see all of it at once. */
.dep-paths {
  margin: 0;
  padding-left: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.dep-path {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1);
  font-size: var(--text-secondary);
  line-height: 1.6;
}

/* The FIRST node, not the last: the last is what the reader already clicked,
   the first is the direct dependency they can actually change. */
.dep-path__root {
  font-weight: 600;
}

.dep-path__arrow {
  opacity: 0.55;
}
