/*
 * Prototype-only chrome — composes tokens.css into the canonical app shell.
 * Final Blazor app will reproduce this in razor + isolated css; the prototypes
 * use plain HTML so the design can be reviewed without the .NET toolchain.
 */

body {
  font-family: var(--wep-font-sans);
  background: var(--wep-color-bg-0);
  color: var(--wep-color-fg-1);
}

/* ─── Element-selector baselines ───────────────────────────────────────
 * Per Plan 2026-05-15-002 U4 / R2. Native form controls leak OS chrome
 * unless every consumer wraps them in Wep* primitives. These :where()-
 * scoped rules drop specificity to 0,0,0 so existing .wep-* class rules
 * (0,1,0 and higher) win the cascade naturally. The baselines mirror
 * the design-system primitive treatments at lower specificity so an
 * unclassed <input> looks indistinguishable from a <WepInput>-wrapped
 * one.
 *
 * Placed near the top of _shell.css (above .wep-shell + all .wep-*
 * rules) so source-order also reinforces the cascade direction.
 *
 * The .wep-test-case-list li button rule at ~line 1068 has specificity
 * 0,1,2 and continues to win over :where(button) — PolicyCedarAuthoring
 * sidebar list-items remain styled by that descendant rule (R1b
 * covered for free).
 * ────────────────────────────────────────────────────────────────── */

:where(
  input[type="text"],
  input[type="email"],
  input[type="password"],
  input[type="number"],
  input[type="search"],
  input[type="datetime-local"],
  select,
  textarea
) {
  height: 28px;
  padding: 0 var(--wep-space-2);
  font: inherit;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  box-sizing: border-box;
}

:where(textarea) {
  height: auto;
  min-height: 60px;
  padding: var(--wep-space-2);
  /* #1768 — was var(--wep-leading-snug), a token that has never existed: the
     scale is --wep-line-{tight,snug,relaxed}. With no fallback the declaration
     was invalid at computed-value time, so line-height silently INHERITED
     instead of tightening. */
  line-height: var(--wep-line-snug);
  resize: vertical;
}

:where(select) {
  /* Drop the OS dropdown chevron — design-system selects render their
     own via .wep-select; for unclassed selects we just suppress the
     native chrome and leave the click area intact. */
  appearance: none;
  padding-right: var(--wep-space-5);
}

:where(button) {
  cursor: pointer;
  font: inherit;
  font-size: var(--wep-size-text-sm);
  height: 28px;
  padding: 0 var(--wep-space-3);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-1);
}

/* #1754 — anchor colour baseline. An <a> with no class and no ancestor rule
 * falls to the UA default #0000EE, which measures 2.05:1 on the default dark
 * canvas: effectively unreadable, and dark is the product default. A census
 * during #1750 found 26 anchors in that state. Making UNTHEMED the safe
 * default costs one rule and closes the shape permanently — a future anchor
 * added without a class can no longer regress to UA blue.
 *
 * :where() (0,0,0) for the same reason the form baselines use it: this rule
 * must lose to EVERY author rule, including a bare `a` type selector, so it
 * can never fight an intentional treatment. Verified across the tree before
 * landing — every anchor is either (a) carrying a class whose own rule sets
 * colour, (b) reached by one of the five anchor-by-descent rules
 * (.wep-crumbs a, .wep-auth-card a:not(.wep-btn), .wep-home-onboarding-body a,
 * .wep-community-footer-nudge a, .wep-community-footer-cta a), all of which
 * out-specify 0,0,0, or (c) one of the unthemed set this issue themes. No
 * anchor was found that deliberately inherits its container's colour.
 *
 * Colour only: the UA underline already carries the affordance (WCAG 1.4.1),
 * and .wep-link remains the explicit treatment for prose links (it adds the
 * hover colour and the underline offset). */
:where(a) {
  color: var(--wep-color-link);
}

/* Checkbox + radio: mirror the .wep-check / .wep-radio rules at lines
 * 1308-1357 byte-for-byte at lower specificity. Border-trick checkmark
 * (no mask, no image). */
:where(input[type="checkbox"], input[type="radio"]) {
  appearance: none;
  width: 14px;
  height: 14px;
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-1);
  margin: 0;
  vertical-align: -2px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

:where(input[type="radio"]) {
  border-radius: 999px;
}

:where(input[type="checkbox"]:checked, input[type="radio"]:checked) {
  background: var(--wep-color-accent-1);
  border-color: var(--wep-color-accent-1);
}

:where(input[type="checkbox"]:checked)::after {
  content: "";
  width: 4px;
  height: 8px;
  border-right: 2px solid var(--wep-color-accent-fg);
  border-bottom: 2px solid var(--wep-color-accent-fg);
  transform: rotate(45deg) translate(0, -1px);
}

:where(input[type="radio"]:checked)::after {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--wep-color-accent-fg);
}

:where(input[type="checkbox"][disabled], input[type="radio"][disabled]) {
  opacity: .5;
  cursor: not-allowed;
}

/* :focus-visible ring is already applied globally via tokens.css
 * (`box-shadow: var(--wep-focus-ring)` on every :focus-visible). No
 * per-element duplication needed. */

/* ─── App shell ─── */
/* Plan 2026-07-01-002 U1 (R1): third `auto` row = the optional footer
 * status-bar (community-edition footer). It collapses to zero when no
 * .wep-shell-foot child renders (Saas/OnPrem), so the shell always fits
 * exactly 100vh and footer chrome can never spawn a second, page-level
 * scrollbar — .wep-main stays the single scroll region.
 *
 * Plan 2026-07-28-001 U6b: the flexible tracks are minmax(0, 1fr), never
 * bare 1fr (= minmax(auto, 1fr) — the track minimum is the items'
 * min-content, so tall/wide content can GROW the nav/main track past the
 * 100vh shell instead of .wep-main scrolling). Same trap + fix as the
 * Home card grid (U6, R8). */
.wep-shell {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  grid-template-rows: 44px minmax(0, 1fr) auto;
  grid-template-areas:
    "ctx ctx"
    "nav main"
    "foot foot";
  height: 100vh;
}

/* ─── The navy shell (#4654 U3) ───
 * The context bar and the nav rail are navy in BOTH themes, as approved on
 * the U0 boards; only the page under them follows the theme. Every colour
 * in the shell's rules is a --wep-shell-* token (tokens.css), never a
 * --wep-color-* token, which would flip with the theme, and never a literal.
 * ShellStylesheetTests holds both, in this sheet and in the scoped copies
 * (ContextBar.razor.css, NavRail.razor.css), which move with this one (#887).
 *
 * The bar's own colour is the shell fg (white): the brand inherits it. The
 * settings popover that drops out of the bar is a page surface, so it sets
 * its own text colour rather than inheriting white onto white. */
.wep-ctxbar {
  grid-area: ctx;
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
  padding: 0 var(--wep-space-4);
  background: var(--wep-shell-bg);
  border-bottom: 1px solid var(--wep-shell-border);
  color: var(--wep-shell-fg);
  font-size: var(--wep-size-text-sm);
}
/* ─── Brand lockup ───
 * The M tree mark at 22px beside the outlined wordmark at 18px, gap 8px,
 * white on navy (#4654). It replaces the 18px teal -> indigo glyph and the
 * semibold "Attestree" text. The mark and the wordmark are copies of the
 * master assets (docs/design/assets/mark-m.svg, logo.svg), not drawings.
 *
 * width+height are mandatory, not cosmetic: an inline <svg> with neither
 * falls back to the 300x150 replaced-element default and blows the 44px
 * ctxbar row apart. flex:none because .wep-ctxbar is a flex row and the
 * cmd-K stub next door grows (#1859 — a child that can be squeezed says so
 * explicitly).
 *
 * The wordmark's box is the Outfit line box (WepBrandWordmark.razor), 1em
 * tall and 4.065em wide, so its font-size IS the drawn 18px type size, and
 * centring the box beside the mark centres the type as the drawing does. */
.wep-ctxbar .wep-brand {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--wep-space-2);
  color: var(--wep-shell-fg);
}
/* #4657: the lockup is the link home; it keeps the bar's white and wears no underline. */
.wep-ctxbar a.wep-brand {
  text-decoration: none;
  border-radius: var(--wep-radius-xs);
}
.wep-ctxbar .wep-brand .wep-brand-mark {
  flex: none;
  width: 22px;
  height: 22px;
}
.wep-ctxbar .wep-brand .wep-brand-wordmark {
  font-size: 18px;
}
.wep-brand-wordmark {
  flex: none;
  width: 4.065em;
  height: 1em;
}
.wep-ctxbar .wep-tenant {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: 4px var(--wep-space-2) 4px var(--wep-space-3);
  border: 1px solid var(--wep-shell-border);
  background: var(--wep-shell-raised);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-shell-fg-muted);
  cursor: pointer;
}
.wep-ctxbar .wep-tenant .wep-tenant-env {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  padding: 1px 6px;
  background: var(--wep-shell-fg);
  color: var(--wep-shell-bg);
  border-radius: var(--wep-radius-xs);
}
/* #2020: the search field is a <button> — it OPENS the command palette, and a
 * `div tabindex="0"` was a focus stop that did nothing. The swap inherits the UA
 * button stylesheet, so this rule now carries the reset a div never needed:
 * `appearance: none` drops the native control chrome, `text-align: left` undoes
 * the centred button label, `margin: 0` the platform button margin. The rest of
 * the reset is already here or in the `:where(button)` baseline at the top of
 * this sheet — border, background, colour, padding and height are declared
 * below (they out-specify the baseline), and `font: inherit` comes from the
 * baseline, with font-family/size overridden below. `cursor: text` STAYS: it
 * promised a field the operator could type into, and typing now works. */
.wep-ctxbar .wep-cmdk {
  flex: 1;
  max-width: 480px;
  height: 28px;
  appearance: none;
  margin: 0;
  text-align: left;
  background: var(--wep-shell-raised);
  border: 1px solid var(--wep-shell-border);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-shell-label);
  padding: 0 var(--wep-space-3);
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  cursor: text;
}
.wep-ctxbar .wep-cmdk-key {
  margin-left: auto;
  padding: 1px 6px;
  border: 1px solid var(--wep-shell-border);
  background: var(--wep-shell-bg);
  color: var(--wep-shell-fg-muted);
  font-size: var(--wep-size-text-3xs);
}
.wep-ctxbar .wep-spacer { flex: 1; }
.wep-ctxbar .wep-id {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-shell-fg-muted);
}
/* The role-and-door suffix on the operator id (R20) wears .wep-fg-3, the
 * page's tertiary text, which is a grey made for the page's grounds and
 * fails on navy. On the shell the same step down is the label tone. */
.wep-ctxbar .wep-id .wep-fg-3 { color: var(--wep-shell-label); }

/* Keyboard focus on the shell: a 2px white outline with a 2px offset, as the
 * U0 states board draws it (17.9:1 on navy). The global ring is a box-shadow
 * whose inner band is bg-1, which on navy reads as a light halo in one theme
 * and vanishes in the other, so the shell's controls trade it for this
 * outline. An outline, not a ring, is also what forced colours keeps
 * (#2053). The settings popover's own rows sit on a page surface and keep
 * the global ring. */
.wep-ctxbar a.wep-brand:focus-visible,
.wep-ctxbar .wep-cmdk:focus-visible,
.wep-settings-menu > summary:focus-visible,
.wep-nav-link:focus-visible,
.wep-nav-sub:focus-visible {
  outline: 2px solid var(--wep-shell-focus);
  outline-offset: 2px;
  box-shadow: none;
}

.wep-nav {
  grid-area: nav;
  background: var(--wep-shell-bg);
  border-right: 1px solid var(--wep-shell-border);
  color: var(--wep-shell-fg-muted);
  padding: var(--wep-space-3) var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  display: flex;
  flex-direction: column;
}
.wep-nav-section {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-shell-label);
  padding: var(--wep-space-3) var(--wep-space-3) var(--wep-space-1);
}
/* The first group header doesn't need its full top padding when Home sits
   directly above it — Home already provides the separation. */
.wep-nav-home + .wep-nav-section { padding-top: var(--wep-space-2); }
/* Home/dashboard root link: a hairline under it sets it apart from the
   grouped workspace surfaces below. */
.wep-nav-home {
  margin-bottom: var(--wep-space-1);
  padding-bottom: var(--wep-space-2);
  border-bottom: 1px solid var(--wep-shell-border);
}
/* The rail's states on navy (#4654 U0): mist at rest, white on navy 800 under
 * the pointer, and the current page white and medium on navy 600 with a 2px
 * white rule on its left, its key lifted from the key tone to mist. */
.wep-nav-link {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: 6px var(--wep-space-3);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-shell-fg-muted);
  text-decoration: none;
}
.wep-nav-link:hover {
  background: var(--wep-shell-raised);
  color: var(--wep-shell-fg);
}
.wep-nav-link[aria-current="page"] {
  background: var(--wep-shell-current);
  color: var(--wep-shell-fg);
  font-weight: var(--wep-weight-medium);
  border-left: 2px solid var(--wep-shell-fg);
  padding-left: 10px;
}
.wep-nav-key {
  margin-left: auto;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  color: var(--wep-shell-key);
}
.wep-nav-link:hover .wep-nav-key,
.wep-nav-link[aria-current="page"] .wep-nav-key {
  color: var(--wep-shell-fg-muted);
}

/* Second-level rail entry — a settings/sub-section child of a .wep-nav-link.
 * Fifteen artboards carried an identical copy of these four rules inside their
 * own inlined shell slice; plan 2026-09-06-2143 U2 moved the copies here so a
 * rail change reaches them. One step down from the parent link (the label
 * tone, not mist; the next type step down), indented to space-6, and NEVER
 * carrying the parent's left rule: a child that painted the 2px bar would read
 * as a top-level section and defeat the very hierarchy the indent establishes.
 * The active child is distinguished by weight and the current ground instead. */
.wep-nav-sub {
  display: block;
  padding: 4px var(--wep-space-3) 4px var(--wep-space-6);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-shell-label);
  text-decoration: none;
  border-radius: var(--wep-radius-xs);
}
.wep-nav-sub:hover {
  background: var(--wep-shell-raised);
  color: var(--wep-shell-fg);
}
.wep-nav-sub[aria-current="page"] {
  background: var(--wep-shell-current);
  color: var(--wep-shell-fg);
  font-weight: var(--wep-weight-medium);
}
/* Trailing count on a sub-entry (e.g. "Storage · 6"). Mono in the key tone so
 * it reads as a measurement rather than as part of the label. */
.wep-nav-sub .n {
  float: right;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  color: var(--wep-shell-key);
}

.wep-nav-foot {
  margin-top: auto;
  padding: var(--wep-space-3);
  border-top: 1px solid var(--wep-shell-border);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-shell-key);
  font-family: var(--wep-font-mono);
}

/* Control-plane build version. Own line above the SHA/region so the string an
 * operator reads for "which build am I on" isn't buried mid-sentence; one step
 * up from the slot's key tone (mist against key) to carry that emphasis
 * WITHOUT inventing a token. Inherits the slot's mono face + 2xs size — no
 * new type scale. */
.wep-nav-version {
  display: block;
  color: var(--wep-shell-fg-muted);
}

/* The shell's SINGLE scroll region (Plan 2026-07-28-001 U6b hardening):
 *   - min-width/min-height 0 pin the grid-item containment floor explicitly
 *     instead of leaning on the overflow-implies-zero-auto-minimum spec
 *     subtlety (min-height:auto is the classic track-growth break);
 *   - position: relative makes .wep-main the containing block for every
 *     un-anchored position:absolute descendant (.wep-sr-only spans et al.),
 *     so they scroll + clip WITH the region. Without it they anchor to the
 *     initial containing block, ESCAPE this overflow clip, and inflate
 *     documentElement.scrollHeight/scrollWidth past the 100vh shell — the
 *     .wep-table-wrap catalog phantom-scrollbar mechanism, reproduced by
 *     populated-Home DecisionRows on both axes.
 * Mirrored in AppLayout.razor.css (both copies move together — #887).
 *
 * ── THE WORKSPACE GUTTER (#2086) ──────────────────────────────────────
 * The horizontal padding is the workspace's own gutter, and until #2086
 * it did not exist. MainLayout renders @Body straight into this element,
 * so a page's BODY started at x=220 — flush against the nav rail on one
 * side and against the viewport edge on the other — under a header whose
 * text started at x=244, because .wep-page-head was the only thing that
 * padded itself. Measured on the running console by the 2144 walk: 45 of
 * 45 CE-reachable surfaces, at 1440 AND 1920 AND 2560. Every artboard in
 * the accepted set insets the body by var(--wep-space-6) — 48 of 48 body
 * wrappers spelled `var(--wep-space-6)` in their horizontal slot — which
 * is where this value comes from; it is not a new number.
 *
 * IT LIVES ON THE CONTAINER, NOT ON THE CHILDREN. The obvious
 * alternative, `.wep-main > :not(.wep-page-head)`, was measured and
 * rejected: the app's real DOM puts MANY elements at this level (on
 * /fleet the header, the .wep-subtabs strip, the safety strip and the
 * list section are all siblings), and a child rule REPLACES their own
 * padding instead of adding to it — it fattens the inline-flex tab strip
 * to a 24px inner pad without moving it off the rail at all, and pads the
 * inside of a top-level position:fixed drawer. Padding the container
 * composes with every child's own padding and cannot reach a fixed
 * element at all, which is exactly the reach wanted.
 *
 * A DOCKED DRAWER REPLACES the right half of it: the `:has()` rules at
 * the foot of this file set `padding-right` on this same element, so
 * their calc() wins on source order and the gutter never doubles up
 * against a docked panel. */
.wep-main {
  grid-area: main;
  overflow: auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  position: relative;
  padding-left: var(--wep-space-6);
  padding-right: var(--wep-space-6);
}

/* Optional footer slot — pinned shell chrome (the grid's `foot` status-bar
 * row), never page flow. AppLayout renders its Footer fragment inside this
 * wrapper only when a footer is passed (community edition); otherwise the
 * element is absent and the `auto` row collapses to zero. */
.wep-shell-foot {
  grid-area: foot;
  min-height: 0;
}

/* The community-edition footer that fills that slot — "Community Edition ·
 * N of 200 endpoints active · Next step: … · No compliance claims." — on every
 * authenticated CE page. Rendered by CommunityFooter.razor and styled only by
 * its scoped sheet until plan 2026-09-06-2143 U11, so no artboard could draw
 * the CE shell with its footer: the four settings artboards simply omitted
 * it, and the CE shell they showed was the SaaS shell. Values are
 * CommunityFooter.razor.css's own; the scoped sheet still wins in the app on
 * specificity, so nothing that renders today changes (the WepBanner-slots
 * precedent above). The interpuncts are ::before separators so a footer
 * whose milestone slot is absent renders three segments, not four and a
 * stray dot. */
.wep-community-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--wep-space-1) var(--wep-space-3);
  padding: var(--wep-space-2) var(--wep-space-6);
  border-top: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-community-footer > span + span::before {
  content: "·";
  margin-right: var(--wep-space-3);
  color: var(--wep-color-fg-4);
}
.wep-community-footer-label { color: var(--wep-color-fg-2); }
/* The count and the disclosure carry classes the footer emits and nothing
 * styles — they read at the slot's own fg-3, which is the intent. Declared so
 * the vocabulary resolves and a future treatment has a home. */
.wep-community-footer-count,
.wep-community-footer-disclosure { color: inherit; }
.wep-community-footer-nudge,
.wep-community-footer-cta {
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-community-footer-nudge a,
.wep-community-footer-cta a {
  color: var(--wep-color-link);
  text-decoration: none;
}
.wep-community-footer-nudge a:hover,
.wep-community-footer-cta a:hover { text-decoration: underline; }

/* ═══ Desktop viewport ladder (plan 2026-09-06-2142 U1) ══════════════════
 * The console was authored and verified at exactly one viewport — 1440 ×
 * 1100 — and had almost no rule above it. `.wep-shell` is
 * `220px minmax(0, 1fr)` with no cap, so a wider monitor bought whitespace
 * rather than evidence. The ladder names three desktop rungs and says what
 * each one buys. Stated for the next author in design.md §3.6.
 *
 *   1440   the AUTHORED rung. It has no media query and never gains one:
 *          every unqueried rule in this file IS the 1440 layout, and the
 *          screenshot baselines are renders of it.
 *   1920   the WIDE rung. Fixed-count chrome grids stop growing (see
 *          `.wep-fleet-metrics` and `.wep-ring`), secondary prose gains a
 *          measure (see `.wep-meta`), and the evidence drawer docks beside
 *          the workspace instead of covering it (see the docking block,
 *          `.wep-main:has(.wep-drawer)`).
 *   2560   the ULTRAWIDE rung. The docked drawer widens. Surplus width
 *          goes to more evidence on screen, never to more padding
 *          (design.md §2 principle 2); §26 keeps density the operator's
 *          own toggle and the ladder does not spend width on it.
 *
 * A RUNG RULE IS DECLARED BESIDE THE BASE RULE IT OVERRIDES, ALWAYS AFTER
 * IT — #2088. A media query adds NO specificity, so a rung rule and its
 * base tie, and source order alone decides. This block used to hold the
 * chrome caps and the prose measure here, ahead of the sections that
 * declare those classes, and both caps were dead from the day they were
 * written: `.wep-ring`'s base `repeat(4, 1fr)` and `.wep-fleet-metrics`'s
 * `repeat(4, minmax(0, 1fr))` are declared later in this file at equal
 * specificity and won unconditionally. Against a promised ≤420px track the
 * 2144 walk measured 573px on the live /fleet at 2560 and 557px on the
 * fleet-index artboard; re-measured headless for the fix, the artboard's
 * tracks were 561px before and 420px after. Page and drawing were wrong in
 * exactly the same way, so no artboard diff could see it — the numbers
 * differ only in how each was measured, never in the verdict.
 * The caps now sit immediately after their own base rules,
 * in the same run as `.wep-fleet-metrics`'s downward 1080/620 reflow,
 * which is the idiom this file already used everywhere else (the drawer's
 * docking rungs sit in one block; `WepDataTable.razor.css`
 * declares the held-back column base then its reveal rungs). Adjacency is
 * the point: an author editing a base rule sees the rung rule that
 * modifies it. ViewportLadderTests fails any queried declaration this
 * file later shadows.
 *
 * THE 1200px BOUNDARY IS GONE (#4213 U13, 2026-10-14). It was the only
 * general-purpose breakpoint this file had before the ladder, and it had
 * one meaning: the DOCK THRESHOLD of the 432px catalog drawer, full-sheet
 * below it and docked above. The catalog list's quick facts now render in
 * the shared evidence drawer, which docks from the 1920 rung, so nothing
 * docks at 1200 and neither the 1200 nor the 1199 query remains.
 *
 * DO NOT ADD A RUNG BETWEEN 1080 AND 1920 OTHER THAN THE UNQUERIED 1440.
 * Two general-purpose breakpoints a few hundred pixels apart in a
 * 5,000-line stylesheet are indistinguishable in practice and impossible
 * to reason about later — and 1440 needs no query, because it is what
 * every unqueried rule already describes.
 *
 * NARROWER THAN 1200 IS DEFERRED, KNOWINGLY AND AGAINST THE EVIDENCE. A
 * 1080p laptop at 150% Windows scaling renders 1280 CSS px and a
 * half-screen snap beside an RDP or PowerShell window renders roughly
 * 960 — both at or below this file's only downward breakpoints, with
 * nothing verified underneath, and the narrow case is the more common one
 * in the target market. That work rides with a mobile-friendly interface
 * and WCAG 1.4.10 reflow (the fixed 220px rail and `height: 100vh` fail
 * reflow at 320 CSS px). Recorded with its evidence in design.md §3.6.
 *
 * MIRROR NOTE (#887). `.wep-shell` and `.wep-main` are declared in BOTH
 * this file and AppLayout.razor.css, and those DECLARATIONS move together.
 * The ladder's viewport rules deliberately live here only, as the
 * docking block always has: the
 * gutter rule is a `:has()` selector that cannot survive Blazor's
 * scoped-CSS rewrite (the scope attribute lands on the last compound
 * selector, and neither drawer is part of AppLayout's render tree), and
 * this shared sheet already reaches the app's `.wep-main` — the scoped
 * copy declares no `padding-right` or `transition` to outrank it.
 * ═════════════════════════════════════════════════════════════════════ */

/* ─── Page header ───
 *
 * ONE HEAD, EVERY PAGE (#4561 U2, Marc 2026-10-20). The approved #4213 and
 * #4256 drawings laid the head out on two columns, and until this unit only
 * /catalog, /inventory and the package page wore it (the #4323 block and
 * CatalogPackage.razor.css). It is the kit's now: the crumbs above, the title
 * and its meta on the left, the Actions slot at the far RIGHT end of the same
 * row (never on a row of its own under the title), and anything PageHead's
 * body slot holds (the tab strip) on the row below, across both columns. A
 * head that holds a tab strip keeps no padding under it, so the active tab's
 * underline sits on the head's own bottom rule (`:has()` below).
 *
 * The bottom margin is the one gap between the head and the page under it.
 * The boards drew that gap themselves, on an artboard-only `.page-body` (20px
 * on 36 boards, 16px on 8, none on 12), and the console had none, so twelve
 * pages' content touched the rule. The boards' `.page-body` keeps no top
 * padding now, and boards and pages both read the gap from here. It is 16px
 * because a page that wraps its head in a block root (/catalog, /inventory,
 * the package page) collapses it with its content's own top margin, and at
 * 16px those approved drawings do not move. */
.wep-page-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--wep-space-4);
  align-items: center;
  padding: var(--wep-space-4) var(--wep-space-6);
  margin-bottom: var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
}
/* (0,2,1): PageHead's scoped padding is (0,2,0) and loads after this sheet. */
header.wep-page-head:has(> .wep-subtabs) {
  padding-bottom: 0;
}
/* A paragraph that opens the page right under the head brings no top margin
 * of its own (the browser's 1em): the gap under the head is the head's. The
 * boards draw every such lead with its top margin zeroed by an artboard rule,
 * and they hold it in `.page-body`, so this rule reaches the pages only. It
 * weighs nothing (:where), so it replaces only the browser's margin: a lead
 * whose class sets a margin of its own (.wep-fleet-trust) keeps it. */
:where(.wep-page-head + p) {
  margin-top: 0;
}
/* Everything but the title and the Actions slot spans both columns: the
 * crumbs, the tab strip, and whatever else a page passes in the body slot.
 * The progress rule is positioned against the head's own box, not a column.
 * (Chained, not one :not() with a list: the sheet readers split on commas.) */
.wep-page-head > :not(.wep-page-title):not(.wep-page-actions):not(.wep-page-progress) {
  grid-column: 1 / -1;
}

/* THE ONE FULL-BLEED CHILD (#2086). Every artboard in the set draws this
 * band edge to edge — rail to viewport, 220..1440 at the authored rung —
 * with its own 24px inner padding putting the title at x=244; 60 of 60
 * measured. `.wep-main`'s gutter would inset the band's ground and its
 * bottom rule by 24px on each side, so the header escapes it by exactly
 * the gutter it is escaping, spelled with the SAME token so the two can
 * never drift. The escape lands the header's margin box on `.wep-main`'s
 * PADDING box edge, not past it, so it adds no scrollable overflow, and
 * the header's own padding is untouched — that is what holds the title
 * at 244.
 *
 * `.wep-main >` IS LOAD-BEARING, not decoration. The escape is only
 * correct where the gutter it escapes is the band's own edge.
 * catalog-index.html nests page headers inside review boards with
 * `overflow: hidden` (its Chocolatey and Scoop sections; the shape came from
 * the per-manager board it absorbed), and an unscoped escape made each nested band 48px
 * wider than its board — measured, 24px clipped. The ancestor in the
 * selector does not put this rule in a fight with the scoped
 * PageHead.razor.css copy: that rule declares no margin at all, and
 * specificity only decides between rules setting the SAME property.
 *
 * TWO PLACEMENTS, AND ONLY TWO (#2613). A routed page renders its header
 * either straight into the workspace or as the FIRST CHILD of its own
 * root element: nine pages wrap themselves in one (.wep-inventory-page,
 * .wep-catalog-page, .wep-pkg-page, …) that carries the page's
 * data-wep-test hook and, on four, its tabindex=-1 focus target. With the
 * direct-child selector alone those headers kept margin 0 and sat 24px
 * inside the rail and the viewport edge — 11 routes, /catalog, /inventory
 * and package detail among them, against 27 that ran flush. The wrapper
 * neither pads nor clips, so the same escape lands the band on the same
 * two edges. `:first-child` keeps the second selector to the page's own
 * header, and the review-board headers above sit four levels down and
 * stay out of reach. ViewportLadderTests forbids a third selector;
 * PageHeadPlacementCensusTests fails a routed page that puts its header
 * anywhere else. */
.wep-main > .wep-page-head,
.wep-main > * > .wep-page-head:first-child {
  margin-left: calc(-1 * var(--wep-space-6));
  margin-right: calc(-1 * var(--wep-space-6));
}
/* The crumbs are plain words at the label size (#4561 U2; the #4213 drawing's,
 * which the list pages wore since #4323). An identifier in them wears its own
 * mono class, as the package page's id does. */
.wep-crumbs {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
  margin-bottom: var(--wep-space-2);
}
.wep-crumbs a { color: var(--wep-color-fg-3); text-decoration: none; }
.wep-crumbs a:hover { color: var(--wep-color-fg-1); }
.wep-page-title {
  grid-column: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--wep-space-1) var(--wep-space-3);
  font-size: var(--wep-size-text-xl);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  margin: 0;
}
/* The meta beside the title: plain words at prose size, each number in it a
 * step stronger (`59 apps · 289 versions`, the numbers in <b>). A count that
 * links to the list it counts keeps the text's colour and shows its underline
 * on hover and on focus. */
.wep-page-title .wep-page-meta {
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-regular);
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-sans);
}
.wep-page-meta a.wep-link {
  color: inherit;
  text-decoration: none;
}
.wep-page-meta a.wep-link:hover,
.wep-page-meta a.wep-link:focus-visible {
  text-decoration: underline;
}
.wep-page-meta b {
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  font-variant-numeric: tabular-nums;
}
/* The Actions slot ends the title's row; its menu opens from the row's right
 * end, so the menu's list anchors to the button's RIGHT edge and runs left,
 * inside the workspace (below; (0,2,1), because the menu's own scoped anchor
 * is (0,2,0) and loads after this sheet). */
.wep-page-actions {
  grid-column: 2;
  display: flex;
  justify-content: flex-end;
  gap: var(--wep-space-2);
  flex-wrap: wrap;
  align-items: center;
}
header.wep-page-head .wep-page-menu-list {
  left: auto;
  right: 0;
}

/* ─── Buttons ─── */
.wep-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
  height: 28px;
  padding: 0 var(--wep-space-3);
  font: inherit;
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-medium);
  border: 1px solid var(--wep-color-border-2);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-1);
  border-radius: var(--wep-radius-xs);
  cursor: pointer;
}
.wep-btn:hover { background: var(--wep-color-bg-2); }
/* WepButton renders an <a> when it carries an Href, and four hand-written
 * call sites (the diagnostics download, the agents rollout link, the
 * bootstrap JSON download, the recovery-codes download) spell the anchor
 * form out. An anchor inherits the UA underline, so every one of those
 * "buttons" shipped with its label underlined — found drawing the Community
 * canvas (plan 2026-09-06-2143 U11). One declaration; the button rule above
 * already paints everything else. */
a.wep-btn { text-decoration: none; }

/* A button that is CURRENTLY CHOSEN out of a set — the selected pin candidate on
   the CVE-exposure page is the only caller today, and it had no rule, so the
   chosen candidate was pixel-identical to the ones beside it and the operator
   could not see which they had picked. Accent border + tinted ground rather than
   the solid `.wep-btn-primary` fill: primary means "this is the action", not
   "this is the one you selected", and the pin ceremony still has a real primary
   button under the list. Named `is-selected` because that is what the markup
   already asked for (#1865 follow-up). */
.wep-btn.is-selected {
  border-color: var(--wep-color-accent-solid);
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-1);
}
/* #1725 primitives audit — fg-1/fg-inv made Primary read as the same value
 * as ordinary body text (nothing marked it "the thing you click"). accent-1
 * is the token the ramp exists for; shadow-sm gives it a hair of lift. */
/* #1764 — the fill moved to the accent-SOLID role because this control writes
 * a label on it: accent-fg on accent-1 measured 3.163:1 dark / 4.473:1 light
 * on the live console (Nightfall's accent, before the #2063 revert), and the
 * label is 13px/500 = normal text, so 4.5:1
 * governs. Keep this rule and WepButton.razor.css in lockstep (see #887). */
.wep-btn-primary {
  background: var(--wep-color-accent-solid);
  color: var(--wep-color-accent-fg);
  border-color: var(--wep-color-accent-solid);
  box-shadow: var(--wep-shadow-sm);
}
.wep-btn-primary:hover { background: var(--wep-color-accent-solid-hover); border-color: var(--wep-color-accent-solid-hover); }
.wep-btn-danger {
  background: var(--wep-color-danger-bg);
  color: var(--wep-color-danger-fg);
  border-color: var(--wep-color-danger-bg);
}
.wep-btn-danger:hover { background: var(--wep-color-danger-bg-hover); border-color: var(--wep-color-danger-bg-hover); }
/* #1865 part M — `wep-btn-secondary` had exactly ONE rule in the repo, behind
 * `.wep-pkg-page ::deep` in CatalogPackage.razor.css, and that rule's own
 * comment already recorded the finding: "the kit's secondary weight IS
 * .wep-btn-ghost, so mirror that rule rather than invent a third button". Two
 * of its three call sites (InventoryTable's two Manage buttons) render outside
 * that page scope and got nothing. Promoted as a SELECTOR on the ghost rule —
 * one treatment, two names — rather than a copy of the declarations. */
.wep-btn-ghost,
.wep-btn-secondary {
  border-color: transparent;
  background: transparent;
}
.wep-btn-ghost:hover,
.wep-btn-secondary:hover { background: var(--wep-color-bg-2); }
/* The DEFAULT variant, named. `.wep-btn` already paints it and WepButton emits
 * bare `wep-btn` for WepButtonVariant.Default — but two hand-written call sites
 * (BootstrapDownloadButton's anchor, FleetCveExposure's pin candidates) spell
 * the variant out and had no rule for the name. Restating the base is what
 * makes the name resolve, and it lets `wep-btn-default` reset a sibling
 * modifier the way the other variants override each other. Every value is
 * `.wep-btn`'s own, so nothing that renders today changes. */
.wep-btn-default {
  background: var(--wep-color-bg-1);
  border-color: var(--wep-color-border-2);
  color: var(--wep-color-fg-1);
}
/* A SIZE modifier, orthogonal to the variant modifiers above — it sets only the
 * three metrics that shrink and inherits every colour, so `wep-btn-sm` composes
 * with `-primary`, `-danger` or `-ghost` without restating them. The four
 * settings artboards used it for per-row actions inside a dense list, where a
 * 28px control would out-weigh the setting it acts on. 24px is the floor: the
 * next rung down stops being a comfortable pointer target. Moved here from four
 * inlined shell slices by plan 2026-09-06-2143 U2. */
.wep-btn-sm {
  height: 24px;
  font-size: var(--wep-size-text-xs);
  padding: 0 var(--wep-space-2);
}
.wep-btn-key {
  margin-left: var(--wep-space-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  padding: 1px 5px;
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-3);
  background: var(--wep-color-bg-1);
}
/* Disabled controls must read as disabled, not "temporarily locked". The
 * CVE-response surface (flow A) renders its response actions disabled because
 * they are not wired to the platform in this build (issue #1170 / Plan
 * 2026-07-01-002 U2).
 *
 * `[aria-disabled="true"]` added by plan 2026-09-07-1635 U7. WepButton.razor.css
 * has carried it since the primitive was written, and this copy did not — the
 * #887 rule is that the kit's two copies of the button ramp move together, and
 * _shell.css already carries the same parity addition for aria-disabled
 * checkboxes further down. It is what a REFUSED halt verb wears: a real
 * `disabled` would drop the control out of the tab order and take its
 * aria-describedby reason with it, so the halts queue renders aria-disabled and
 * keeps it focusable (plan 2026-09-07-1635 KTD8, R26). `pointer-events: none`
 * is what makes that state genuinely inert to a pointer, which is why the
 * reason is always-visible row text and never a tooltip. Nothing that renders
 * today changes: no artboard used the attribute before this one. */
.wep-btn:disabled,
.wep-btn[disabled],
.wep-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* ─── Filter chips ───
 *
 * ONE CHIP, EVERY PAGE (#4561 U3, Marc 2026-10-20). The approved #4213 drawing's
 * chip, which only /catalog and /inventory wore (#4323): sans at the label size,
 * a pill outline, the count beside the label a step quieter, and the chip in
 * force on the accent's tint. It was mono at 2xs, and the chip in force an
 * inverted black pill. WepFilterChip.razor.css mirrors the shape (#887). */
.wep-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.wep-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 26px;
  padding: 0 10px;
  border: 1px solid var(--wep-color-border-2);
  background: var(--wep-color-bg-1);
  border-radius: var(--wep-radius-pill);
  font-size: var(--wep-size-text-xs);
  font-family: var(--wep-font-sans);
  line-height: 1;
  color: var(--wep-color-fg-2);
  white-space: nowrap;
  cursor: pointer;
}
.wep-chip-on {
  background: var(--wep-color-accent-bg);
  color: var(--wep-color-fg-1);
  border-color: var(--wep-color-accent-1);
}
.wep-chip .wep-chip-count {
  font-weight: var(--wep-weight-medium);
  font-variant-numeric: tabular-nums;
  color: var(--wep-color-fg-3);
}
.wep-chip .wep-chip-x {
  margin-left: 2px;
  color: var(--wep-color-fg-3);
}
.wep-chip-on .wep-chip-count,
.wep-chip-on .wep-chip-x {
  color: var(--wep-color-fg-2);
}
/* #3966 U5 (KTD4) — a chip that is a link, and the remove link inside an
 * active chip. No bare `a { color }` exists in this sheet, so the chip's own
 * colour stands; the underline goes, because the pill shape is the affordance,
 * as it is on the button chip. Anchors only: the button and span chips other
 * artboards draw are untouched. The muted count and the inverse chip's × are
 * WepFilterChip's scoped rules, and the one board that draws them
 * (fleet-index-attention.html) mirrors them locally. */
a.wep-chip,
a.wep-chip-x { text-decoration: none; }

/* ─── Status pills ───
 *
 * ONE PILL, EVERY PAGE (#4561 U3, Marc 2026-10-20). The approved #4213
 * drawing's pill, which only the two list pages wore (#4323): lowercase, at the
 * label size and medium weight, with no leading dot and no wide tracking. It is
 * the same pill: its state, its colour, its border family (design.md §10) and
 * its words in the DOM and in its accessible name are unchanged; only
 * `text-transform` changes how the words show. Drawers and the package page's
 * head pill wear it too ("attested · safe to deploy"). WepStatusPill.razor.css
 * mirrors it (#887). */
.wep-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px;
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
  font-weight: var(--wep-weight-medium);
  letter-spacing: normal;
  text-transform: lowercase;
  line-height: 1.1;
  border-radius: var(--wep-radius-xs);
  border: 1px solid transparent;
  white-space: nowrap;
}
.wep-pill > .wep-pill-sub {
  font-weight: var(--wep-weight-regular);
}
/* An abbreviation keeps its capitals in a lowercase pill (KEV, MSI): the pill
 * marks it up as <abbr>, and only the words around it lower their case. */
.wep-pill abbr {
  text-transform: none;
  text-decoration: none;
}

/* Plan 2026-05-14-002 (detonation mechanism) U15 — overflow rule:
 * pin pill max-width so long labels truncate with ellipsis. Full
 * label exposed via title + aria-label on the WepStatusPill component
 * (U4 added the AriaLabel parameter).
 *
 * Plan 2026-05-15-003 Phase 2 U1: raised 14ch → 20ch so the longest
 * catalog lifecycle status (QUEUED-FOR-DETONATE = 19 chars) fits
 * without mid-character clip. Audit decision pills (CLEAN/ALLOW) are
 * 5 chars and unaffected. If a future status exceeds 20ch, raise the
 * cap here rather than introducing a per-call-site modifier.
 *
 * #1621: 30ch, measured live, and the reason the old numbers lied:
 * `ch` is the width of the digit 0 (6.78px at Geist 11px), while an
 * UPPERCASE wide-tracked label averages ~7.4px per character — and the
 * status dot, gap, and padding spend from the same border-box budget.
 * REENROLLMENTREQUIRED (20 chars, the longest live status) renders
 * untruncated from 29ch up; 30ch = measured minimum + 1ch font-metric
 * headroom. This copy had also drifted to 20ch while the shipped
 * component said 24ch — the parity test in WepStatusPillTests now pins
 * both copies to the same value.
 *
 * #1320: NO text-overflow on .wep-pill itself — it never applies to an
 * inline-flex container (its text is an anonymous flex item), so it
 * silently degraded to a hard mid-word clip. The container keeps the
 * width budget + shrink permission; the ellipsis lives on the inner
 * .wep-pill-label span the WepStatusPill component renders. Mirrored in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson).
 */
.wep-pill {
  max-width: 30ch;
  min-width: 0;
  overflow: hidden;
  flex-shrink: 1;
}
.wep-pill-label {
  display: block;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* #2259 — the state name is the identity; the sub-label yields. The label
 * and the sub-label share the 30ch budget above. With no rule here the
 * sub-label kept its automatic minimum size and could not shrink, so the
 * label was the only item that gave up width: `AHEAD OF ATTESTA…  DISMISSED`.
 *
 * The sub-label is sized from a ZERO basis and grows into whatever the
 * label leaves, so the label keeps its whole content width until it alone,
 * plus the sub-label's 3ch floor, is over budget. A heavier shrink weight
 * (`flex: 0 1000 auto`) is not the same thing, and it was measured: flex
 * shrink still hands the label its weighted share of the overflow, 1/32px
 * in Chrome 153, and a label 1/32px narrower than its text ellipsizes. The
 * full reading stays on title + aria-label.
 *
 * Scoped to a sub-label INSIDE a pill: `.wep-pill-sub` also rides alone as
 * a chip (the catalog's `Auto-update: …` reading, the row origin sub-label),
 * where a nowrap rule would change how it wraps. Mirrored in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill > .wep-pill-sub {
  flex: 1 1 0;
  min-width: 3ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* #2259 — the trust-tier origin chips never truncate: they are what an
 * auditor reads at a glance, and their words are short. `flex: none`, not
 * `flex-shrink: 0`: under the zero basis above a non-shrinking chip would
 * still start at the 3ch floor and be cut to what the label left. */
.wep-pill > .wep-pill-sub.wep-origin-manualattest,
.wep-pill > .wep-pill-sub.wep-origin-waived,
.wep-pill > .wep-pill-sub.wep-origin-waived-expired {
  flex: none;
}

.wep-pill-clean      { color: var(--wep-state-clean-fg);      background: var(--wep-state-clean-bg); }
.wep-pill-detonating { color: var(--wep-state-detonating-fg); background: var(--wep-state-detonating-bg); }
.wep-pill-sbomready  { color: var(--wep-state-sbomready-fg);  background: var(--wep-state-sbomready-bg); }
.wep-pill-cveblocked { color: var(--wep-state-cveblocked-fg); background: var(--wep-state-cveblocked-bg); }
.wep-pill-quarantined{ color: var(--wep-state-quarantined-fg);background: var(--wep-state-quarantined-bg); }
.wep-pill-promoted   { color: var(--wep-state-promoted-fg);   background: var(--wep-state-promoted-bg); }
.wep-pill-rolledback { color: var(--wep-state-rolledback-fg); background: var(--wep-state-rolledback-bg); }

/* Global copies of the states that previously lived ONLY in the scoped
 * WepStatusPill.razor.css (attested/observedunattested/offline/degraded/
 * drift/foreign). Without these, those states fell back to the inherited
 * (white) colour wherever the scoped rule lost the cascade — e.g. the
 * catalog list's ATTESTED status read as plain white text. Mirrors the
 * scoped values 1:1 so the colour is identical whichever rule wins. */
.wep-pill-attested          { color: var(--wep-state-clean-fg);       background: var(--wep-state-clean-bg); }
.wep-pill-observedunattested{ color: var(--wep-color-fg-3);           background: var(--wep-color-bg-2); }
.wep-pill-offline           { color: var(--wep-color-fg-3);           background: var(--wep-color-bg-2); }
.wep-pill-degraded          { color: var(--wep-color-warn-fg);        background: var(--wep-color-warn-bg); }
.wep-pill-drift             { color: var(--wep-state-detonating-fg);  background: var(--wep-state-detonating-bg); }
.wep-pill-foreign           { color: var(--wep-state-quarantined-fg); background: var(--wep-state-quarantined-bg); }

/* Plan 2026-05-14-002 (detonation mechanism) U15 — five new pill
 * states. Token reuse rationale:
 *   NetworkPostureOffline: clean ramp — offline is the safe default.
 *   NetworkPostureMitm:    detonating ramp — "warning: traffic intercepted".
 *   NetworkPostureUnknown: sbomready ramp (muted blue) — neither good nor bad.
 *   UnsignedAttestation:   rolledback ramp (warm orange) — "evidence is weaker".
 *   Cancelled:             quarantined ramp (muted purple) — "out of band". */
.wep-pill-networkpostureoffline { color: var(--wep-state-clean-fg);      background: var(--wep-state-clean-bg); }
.wep-pill-networkposturemitm    { color: var(--wep-state-detonating-fg); background: var(--wep-state-detonating-bg); }
.wep-pill-networkpostureunknown { color: var(--wep-state-sbomready-fg);  background: var(--wep-state-sbomready-bg); }
.wep-pill-unsignedattestation   { color: var(--wep-state-rolledback-fg); background: var(--wep-state-rolledback-bg); }
.wep-pill-cancelled             { color: var(--wep-state-quarantined-fg);background: var(--wep-state-quarantined-bg); }

/* Plan 2026-06-10-001 U6 — exceeds-detonation-budget. Warning-amber ramp
 * (detonating tokens, the drift precedent): a coverage limit the operator
 * can resolve (raise budget / upgrade broker / manual-attest valve), NOT
 * quarantine-purple — no sandbox verdict exists for these rows. */
.wep-pill-exceedsdetonationbudget { color: var(--wep-state-detonating-fg); background: var(--wep-state-detonating-bg); }

/* Plan 2026-06-10-002 U5 — requires-interaction. Warning-amber ramp
 * (detonating tokens, same reuse as exceeds-detonation-budget): an actionable
 * behavioral verdict, NOT quarantine-purple — the pipeline worked; the
 * installer demands a human. No manual-attest valve for these rows. */
.wep-pill-requiresinteraction { color: var(--wep-state-detonating-fg); background: var(--wep-state-detonating-bg); }

/* Plan 2026-06-26-002 U8 — exceeds-observability-budget. Warning-amber ramp
 * (detonating tokens, same reuse as exceeds-detonation-budget / requires-
 * interaction): a Succeeded-but-over-bound run is over-observable, not suspicious
 * — raise the global bound + re-detonate, or sign a per-version waiver, NOT
 * quarantine-purple. Distinct pill class (offers no manual-attest valve). Mirrored
 * in the scoped copy in WepStatusPill.razor.css (both copies move together — the
 * #887 lesson). */
.wep-pill-exceedsobservabilitybudget { color: var(--wep-state-detonating-fg); background: var(--wep-state-detonating-bg); }

/* Plan 2026-06-10-002 U12 — derived-artifact provenance badge (DERIVED-MSI).
 * Promoted-teal ramp ("currently serving"): the feed serves the extracted
 * inner MSI, not the vendor bytes. Its own pill — NOT an attestation-origin
 * sub-label — the provenance axis must read distinct from trust tiers.
 * Mirrors the scoped copy in WepStatusPill.razor.css (#887 lesson). */
.wep-pill-derivedartifact { color: var(--wep-state-promoted-fg); background: var(--wep-state-promoted-bg); }

/* Plan 2026-06-13-001 U5 (KTD8) — winget recognition pills. Each carries its
 * OWN ramp (--wep-state-recognized{confident,possible}-*), NOT an alias of
 * Foreign/Drift/Promoted: the recognition surface must read distinct from the
 * states it lives beside. recognizedconfident = informational-teal (a confident
 * read-only catalog correlation, not the promoted-teal shipping state);
 * recognizedpossible = muted advisory (correlated-but-unconfirmed, distinct
 * from the muted-grey un-correlated Foreign row). Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-recognizedconfident { color: var(--wep-state-recognizedconfident-fg); background: var(--wep-state-recognizedconfident-bg); }
.wep-pill-recognizedpossible  { color: var(--wep-state-recognizedpossible-fg);  background: var(--wep-state-recognizedpossible-bg); }

/* Plan 2026-06-16-001 U3 (KTD5) — auto-rollback HOLD pill. Its OWN amber-gold
 * ramp (--wep-state-held-*), NOT an alias of rolledback/quarantined: a held
 * endpoint is FROZEN at the breached version (protective), not demoted. Mirrors
 * the scoped copy in WepStatusPill.razor.css (both copies move together — the
 * #887 lesson). */
.wep-pill-held { color: var(--wep-state-held-fg); background: var(--wep-state-held-bg); }

/* Plan 2026-06-29-002 U8 (R4/R5) — per-user last-known / unverified pill. Its OWN
 * muted "aged/stone" ramp (--wep-state-lastknownunverified-*): a per-user package
 * remembered for an unobservable SID (logout/headless), present but not re-observed.
 * NOT an alias of held (amber-gold), observedunattested (grey), or recognizedpossible
 * (slate) — the no-pill-color-aliasing convention. Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-lastknownunverified { color: var(--wep-state-lastknownunverified-fg); background: var(--wep-state-lastknownunverified-bg); }

/* Plan 2026-09-08-1838 U3 (KTD4) — the management axis's `not driven` rung: a
 * catalog-covered row whose bytes the platform has no evidence of placing. Its OWN
 * cool graphite ramp (--wep-state-notdriven-*), deliberately OFF the clean ramp — the
 * defect being fixed is a green `match` that claimed management it never checked.
 * NOT an alias of observedunattested (grey — `provenance pending` holds that ramp in
 * the SAME column and means the opposite thing about management), foreign (purple —
 * un-correlated; this row IS covered), recognizedpossible (slate — an identity hedge),
 * or drift (amber — asserts a managed package moved, which this row cannot claim) —
 * the no-pill-color-aliasing convention. Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-notdriven { color: var(--wep-state-notdriven-fg); background: var(--wep-state-notdriven-bg); }

/* #2540 — a decommissioned endpoint: tokens revoked, device key distrusted, fleet rows
 * kept. Its OWN muted stone ramp (--wep-state-decommissioned-*): NOT an alias of
 * offline (grey — a retired machine must not read as one that may come back),
 * brokerdecommissioned (quarantined purple, an attention treatment), or
 * lastknownunverified (warmer taupe; an aging signal). Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-decommissioned { color: var(--wep-state-decommissioned-fg); background: var(--wep-state-decommissioned-bg); }

/* #2597 — an endpoint whose agent answers the wake probe but has stopped checking in
 * (its check-in loop is stuck): label `not checking in (probing)`. Its OWN teal-cyan ramp
 * (--wep-state-notcheckingin-*): NOT an alias of offline (grey — this box IS reachable),
 * settling (slate-blue — "wait, nothing is wrong"), degraded (warn-amber — the `update
 * stuck` pill in the same cell), or decommissioned (stone). Actionable, so it joins the
 * ATTENTION forced-colours family. Mirrors the scoped copy in WepStatusPill.razor.css
 * (both copies move together — the #887 lesson). */
.wep-pill-notcheckingin { color: var(--wep-state-notcheckingin-fg); background: var(--wep-state-notcheckingin-bg); }

/* #2829 — the ahead-of-attestation exception, drawn beside an endpoint package row's own
 * pill. The SAME warn ramp as degraded, which it used before, so nothing moves in colour:
 * this is a class for forced colours, not a new hue. There `degraded` is dashed 2px, the
 * pair `drift` renders in the same cell, and narrowing `degraded` would move dozens of
 * other surfaces, so this state takes dashed 1px of its own. Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-aheadofattestation { color: var(--wep-color-warn-fg); background: var(--wep-color-warn-bg); }

/* #3577 R19, #3645 (U6) — a confident Critical CVE that no policy holds ("Critical CVE"): the CVE
 * ramp and the severity chip's rule border, so it never reads as `cveblocked` (the transparent
 * base border) at a glance. Under forced colours it is ATTENTION, not BLOCKED: nothing refuses this
 * version until an operator policy does (#3390). Mirrors the scoped copy in WepStatusPill.razor.css
 * (both copies move together — the #887 lesson). */
.wep-pill-criticalcve { color: var(--wep-state-cveblocked-fg); background: var(--wep-state-cveblocked-bg); border-color: var(--wep-state-cveblocked-rule); }

/* #3925 U13 (R16, R18; KTD15) — the installer label's states: what a package on a box can say about the
 * installer bytes it recorded running (`verified`, `unverified · accepted`, `unverified · not accepted`),
 * the catalog verdict table's `unverified` sibling, and the R18 finding (`other installer`: the box ran
 * another installer than it was named). Each borrows the ramp of the meaning it shares and is a class of
 * its own for the label vocabulary and for forced colours (the #2829 precedent): `installerverified` the
 * clean ramp `attested` wears, because a verified installer is the same claim at installer grain;
 * `installerunverified` the muted grey of a claim without proof (`observedunattested`'s pair), the normal
 * state of a sibling on the catalog page; `installeraccepted` (#3925 PR H) the same muted grey — an
 * operator consented, a settled state that wants nobody; `installernotaccepted` (#3925 PR H) the warn ramp
 * — the box runs unverified bytes nobody consented to, and that wants an operator; `installermismatch`
 * the warn ramp of a finding that wants an operator. KTD15: none is named for `unverified-sublabel`,
 * `LastKnownUnverified` or `ServedUnverified`. Mirrors the scoped copy in WepStatusPill.razor.css (both
 * copies move together — the #887 lesson). */
.wep-pill-installerverified    { color: var(--wep-state-clean-fg);  background: var(--wep-state-clean-bg); }
.wep-pill-installerunverified  { color: var(--wep-color-fg-3);      background: var(--wep-color-bg-2); }
.wep-pill-installeraccepted    { color: var(--wep-color-fg-3);      background: var(--wep-color-bg-2); }
.wep-pill-installernotaccepted { color: var(--wep-color-warn-fg);   background: var(--wep-color-warn-bg); }
.wep-pill-installermismatch    { color: var(--wep-color-warn-fg);   background: var(--wep-color-warn-bg); }

/* Plan 2026-07-08-001 (#1251) — requires-user-context pill. Its OWN warm
 * copper/sepia amber ramp (--wep-state-requiresusercontext-*): a per-user
 * installer exited 0 as SYSTEM into HKU\S-1-5-18 — installed, but visible to no
 * real user; un-converged, NOT failed. NOT an alias of held (amber-gold),
 * cveblocked (red), or detonating (amber) — the no-pill-color-aliasing
 * convention. Mirrors the scoped copy in WepStatusPill.razor.css (both copies
 * move together — the #887 lesson). */
.wep-pill-requiresusercontext { color: var(--wep-state-requiresusercontext-fg); background: var(--wep-state-requiresusercontext-bg); }

/* Plan 2026-07-16-001 (#1421) U1 — withdrawn-upstream tombstone pill. Its OWN muted
 * slate-violet ramp (--wep-state-withdrawnupstream-*): the vendor deleted this
 * version's winget-pkgs manifest upstream after ingest — a never-served ghost. NOT
 * an alias of quarantined/cancelled (violet) or observedunattested (grey) — the
 * no-pill-color-aliasing convention. Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-withdrawnupstream { color: var(--wep-state-withdrawnupstream-fg); background: var(--wep-state-withdrawnupstream-bg); }

/* Plan 2026-07-18-001 U1 (#1454) — app-in-use deferred pill. Its OWN muted
 * olive/moss ramp (--wep-state-appinusedeferred-*): the target application is
 * running in a user session, so the silent SYSTEM upgrade is confirmed blocked
 * and backed off until the app closes — un-converged, NOT failed, never
 * "cancelled by user". NOT an alias of requiresusercontext (copper), held
 * (amber-gold), degraded (amber), or detonating (amber) — the
 * no-pill-color-aliasing convention. Mirrors the scoped copy in
 * WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-appinusedeferred { color: var(--wep-state-appinusedeferred-fg); background: var(--wep-state-appinusedeferred-bg); }

/* Plan 2026-07-23-004 U4 (#1569, R6) — self-rejecting rollout member pill. Its OWN muted
 * mustard-amber ramp (--wep-state-selfrejecting-*): a ring member excluded from the agent-rollout
 * health denominators for rejecting two or more distinct unsurpassed agent versions — a CAUTION,
 * NOT a failure. NOT an alias of cveblocked/rolledback (red/orange), offline (grey — an excluded
 * member must never read as offline), quarantined (violet — a catalog-admin hold, KTD6),
 * held/detonating (gold), or appinusedeferred (olive) — the no-pill-color-aliasing convention.
 * Mirrors the scoped copy in WepStatusPill.razor.css (both copies move together — the #887 lesson). */
.wep-pill-selfrejecting { color: var(--wep-state-selfrejecting-fg); background: var(--wep-state-selfrejecting-bg); }
/* #1616 — joined-at-target "settling" member. Cool by design: the defect was this row reading as
   a broken upgrade, so no warm ramp. Mirrored in WepStatusPill.razor.css (#887 lesson). */
.wep-pill-settling { color: var(--wep-state-settling-fg); background: var(--wep-state-settling-bg); }

/* Plan 2026-08-09-001 U5 (#1733) — undeclared/looping SYSTEM-hollow debt pill. Its OWN
 * muted plum ramp (--wep-state-uninstalllooping-*): a machine uninstall stuck in backoff
 * because only the SYSTEM-profile hollow copy remains — actionable remediation debt, not
 * a failure verdict. NOT an alias of cveblocked (red), quarantined/withdrawnupstream
 * (violet — catalog axes), requiresusercontext (copper — the declared+held sibling shape
 * on the same Holds surface), or held (amber-gold — auto-rollback; the no-aliasing
 * convention). Mirrors the scoped copy in WepStatusPill.razor.css (both copies move
 * together — the #887 lesson). */
.wep-pill-uninstalllooping { color: var(--wep-state-uninstalllooping-fg); background: var(--wep-state-uninstalllooping-bg); }

/* #1061 U3 — dev-grade / not-sandbox-verified serve badge. Dashed outline is the
 * primary discriminator (the warm family is saturated; "unfinished" is the honest
 * read). Mirrored from WepStatusPill.razor.css (both copies move together — the
 * #887 lesson). */
.wep-pill-servedunverified { color: var(--wep-state-servedunverified-fg); background: var(--wep-state-servedunverified-bg); border: 1px dashed var(--wep-state-servedunverified-rule); }

/* Plan 2026-06-29-002 U8 (R5) — staleness flag on a per-user last-seen cell. A
 * small warn-amber chip that appears once a last-known snapshot's last-seen
 * exceeds 7 days, so a stale row is never silently presented as current truth.
 * Uses the cross-cutting warn tokens (an aging signal, not a distinct product
 * state), so no new --wep-state-* ramp. */
.wep-stale-flag {
    display: inline-block;
    margin-left: var(--wep-space-1);
    padding: 0 4px;
    border-radius: var(--wep-radius-xs);
    color: var(--wep-color-warn-fg);
    background: var(--wep-color-warn-bg);
    font-size: var(--wep-size-text-xs);
    font-weight: var(--wep-weight-medium);
    text-transform: lowercase; /* #4561 U3: set as the pill beside it is */
}

/* #3577 U6 (R2, R20) — a POSSIBLE (or can't-tell) CVE match's severity: a muted dashed outline,
 * against the kit severity pill a CONFIDENT match wears, so the two tiers read apart before any word
 * is read. No attention colour: a possible match is not a finding until a pin or the alias list
 * verifies its mapping, and it is never counted. The dashed border survives forced colours. Shared by
 * the evidence panel (drawer and version page) and /fleet/cve. */
.wep-cve-possible {
    display: inline-block;
    padding: 0 4px;
    border: 1px dashed var(--wep-color-border-2);
    border-radius: var(--wep-radius-xs);
    color: var(--wep-color-fg-3);
    font-size: var(--wep-size-text-xs);
    font-weight: var(--wep-weight-medium);
    text-transform: lowercase; /* #4561 U3: set as the confident match's pill is */
}

/* Plan 2026-06-25-001 (safe-to-deploy verdict) U2 — WepVerdict glyph cells. The
 * four-cell strip encodes each leg's LegState by FILL/SHAPE, never color alone
 * (R11): solid pass, diagonal hatch = not-evaluated (the gap), hollow + × = fail.
 * Four solid pass cells (gap:0) MERGE into one block, which happens only when
 * every leg passes (R9). Token-sourced from --wep-verdict-cell-* (tokens.css).
 * Mirrors the scoped copy in WepVerdict.razor.css (both copies move together —
 * the #887 lesson). No motion declared (KTD3). */
.wep-verdict-cells {
  display: inline-flex;
  gap: 0;
  border-radius: var(--wep-radius-xs);
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--wep-color-border-2);
}
.wep-verdict-cells-solid { box-shadow: inset 0 0 0 1px var(--wep-verdict-cell-pass-rule); }
.wep-verdict-cell {
  width: 9px;
  height: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.wep-verdict-cell-pass    { background: var(--wep-verdict-cell-pass-bg); }
.wep-verdict-cell-noteval { background-image: var(--wep-verdict-cell-noteval-hatch); }
.wep-verdict-cell-fail {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--wep-verdict-cell-fail-rule);
  color: var(--wep-verdict-cell-fail-fg);
}
.wep-verdict-cell-x {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  line-height: 1;
  color: var(--wep-verdict-cell-fail-fg);
}

/* Plan 2026-09-11-0056 U8 (R18) — the unsigned-installer ACCEPTANCE beside the
 * attested-unsigned label. The label never changes; this is the annotation, and
 * every surface wears these two classes so the treatment is one rule:
 *   .wep-acceptance-mark — the compact form (a catalog row, a collapsed group,
 *                          the Home queue); its tooltip + aria-label carry the
 *                          full sentence.
 *   .wep-acceptance-note — the detailed form ("accepted by X until Y") in the
 *                          drawer checklist, the evidence fields, the package
 *                          page and the promote dialog.
 * An ACTIVE acceptance is informational and wears no attention colour — a normal
 * state is not a warning. Expired and revoked carry their own state treatment
 * because the release is no longer covered, and they differ by more than hue: the
 * expired mark is filled, the revoked mark is dashed. Defined globally (not in
 * WepVerdict.razor.css) because a scoped rule could not reach the other surfaces. */
.wep-acceptance-mark {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  padding: 0 var(--wep-space-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  line-height: 1.5;
  color: var(--wep-color-fg-3);
  white-space: nowrap;
  cursor: help;
}
.wep-acceptance-note {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
}
.wep-acceptance-mark[data-state="expired"] {
  background: var(--wep-color-warn-bg);
  border-color: var(--wep-color-warn-fg);
  color: var(--wep-color-warn-fg);
}
.wep-acceptance-note[data-state="expired"] { color: var(--wep-color-warn-fg); }
.wep-acceptance-mark[data-state="revoked"] {
  border-style: dashed;
  border-color: var(--wep-color-danger-text);
  color: var(--wep-color-danger-text);
}
.wep-acceptance-note[data-state="revoked"] { color: var(--wep-color-danger-text); }
/* #2291: an acceptance on a package that now holds a signer reference covers nothing (R24) —
 * the package card says so in words, in the revoked note's colour: neither covers a release. */
.wep-acceptance-note[data-state="ineligible"] { color: var(--wep-color-danger-text); }
/* The acceptance card's ceremony blocks (record, renew, add a source, revoke): each a
 * stacked group with room above it, so one ceremony's button never reads as the lead of
 * the next. Global for the same reason as the two classes above: the artboard draws it. */
.wep-acceptance-ceremony {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-4);
}
.wep-acceptance-ceremony textarea.wep-input { width: min(100%, 32rem); }
/* #3925 U7 — the unverified-installer acceptance card and the deployment-wide control.
 * A standing acceptance that covers none of an architecture's installers (a sandbox here
 * can test them, or they download from a source it did not approve) is NOT IN FORCE there:
 * its note wears the expired treatment, because, like an expired one, it covers nothing.
 * Each architecture of the card is its own block with room above it; its field list sits
 * in that block's flow (never in the column-flex ceremony block, whose shrink-wrap made
 * the auto-fit grid take its column count from the text length). The value lists (approved
 * or bound sources, coverage lines) drop the browser's 40px list indent so a source
 * identity has its column's width, and a day is one token: it never breaks at its hyphens. */
.wep-acceptance-note[data-state="not-in-force"] { color: var(--wep-color-warn-fg); }
.wep-acceptance-architecture { margin-top: var(--wep-space-5); }
.wep-acceptance-list {
  margin: 0;
  padding-left: var(--wep-space-4);
}
.wep-acceptance-day { white-space: nowrap; }
/* #3925 PR G (capture defect 16) — the per-box serving cutover's census: two or three tables of the same five columns
 * (endpoint, package, version, scope, installer), one under another. Auto layout sized each table to its own content,
 * so their columns did not line up; a fixed layout takes every census table's widths from its header, so they share
 * one grid, and a long package id wraps inside its cell instead of widening the column. Global, like the acceptance
 * rules above, because the artboard draws it. */
.wep-perbox-census { table-layout: fixed; width: 100%; }
.wep-perbox-census th:nth-child(1) { width: 22%; }
.wep-perbox-census th:nth-child(2) { width: 30%; }
.wep-perbox-census th:nth-child(3) { width: 14%; }
.wep-perbox-census th:nth-child(4) { width: 10%; }
.wep-perbox-census th:nth-child(5) { width: 24%; }
.wep-table.wep-perbox-census tbody td { white-space: normal; overflow-wrap: anywhere; }

/* Plan 2026-06-13-001 U5 — non-interactive recognition update badge. Reuses
 * the .wep-update-badge visual SHAPE (Plan 2026-06-08-002) but never as a
 * button: R12 forbids any action affordance on a recognized row. This modifier
 * strips the click affordance (no pointer cursor, no hover state) so the
 * informational span doesn't read as clickable. */
.wep-update-badge.wep-update-badge-static { cursor: default; }
.wep-update-badge.wep-update-badge-static:hover {
  background: var(--wep-color-accent-bg);
  color: var(--wep-color-accent-text);
}

/* Plan 2026-06-10-001 U9 — manual-attest origin badge. The valve origin
 * ("operator-vouched, NO sandbox evidence") must read visually DISTINCT from
 * the muted manual-sbom sub-label ("operator-uploaded SBOM") so an auditor
 * can tell the trust tiers apart at a glance. Warm rolledback ramp +
 * outlined chip. Mirrors the scoped copy in WepStatusPill.razor.css
 * (both copies move together — the #887 lesson). */
.wep-pill-sub.wep-origin-manualattest {
  color: var(--wep-state-rolledback-fg);
  background: var(--wep-state-rolledback-bg);
  border: 1px solid currentColor;
  border-radius: var(--wep-radius-xs);
  padding: 0 4px;
}

/* Plan 2026-06-18-001 U6 — baseline-gate-waiver origin sub-labels. A waived
 * attestation ("attested (waived)") reached attested through an operator
 * ceiling waiver — the verbatim probe failure stays in evidence, so it must
 * read DISTINCT from a clean real-sandbox attestation (amber detonating ramp).
 * The expired variant ("waiver expired", lazy-window, still Attested) escalates
 * to the cveblocked ramp so the lapsed time-box reads as action-required.
 * Mirrors the scoped copy in WepStatusPill.razor.css (both copies move
 * together — the #887 lesson). */
.wep-pill-sub.wep-origin-waived {
  color: var(--wep-state-detonating-fg);
  background: var(--wep-state-detonating-bg);
  border: 1px solid currentColor;
  border-radius: var(--wep-radius-xs);
  padding: 0 4px;
}

.wep-pill-sub.wep-origin-waived-expired {
  color: var(--wep-state-cveblocked-fg);
  background: var(--wep-state-cveblocked-bg);
  border: 1px solid currentColor;
  border-radius: var(--wep-radius-xs);
  padding: 0 4px;
}

/* Plan 2026-05-21-003 U19 — broker lifecycle pill ramps. Each broker
 * state gets its own ramp via --wep-state-broker-*-{fg|bg|rule} so
 * BrokerReconnecting doesn't visually collide with Detonating, etc.
 * Ramp choices:
 *   BrokerConnecting:           sbomready (muted blue, neutral transitioning).
 *   BrokerHealthy:              clean (green, operational).
 *   BrokerReconnecting:         detonating (amber, attention-needed).
 *   BrokerReenrollmentRequired: cveblocked (red, operator action required).
 *   BrokerRevoked:              rolledback (muted red, terminal).
 *   BrokerDecommissioned:       quarantined (muted purple, archived). */
.wep-pill-brokerconnecting             { color: var(--wep-state-sbomready-fg);   background: var(--wep-state-sbomready-bg); }
.wep-pill-brokerhealthy                { color: var(--wep-state-clean-fg);       background: var(--wep-state-clean-bg); }
.wep-pill-brokerreconnecting           { color: var(--wep-state-detonating-fg);  background: var(--wep-state-detonating-bg); }
.wep-pill-brokerreenrollmentrequired   { color: var(--wep-state-cveblocked-fg);  background: var(--wep-state-cveblocked-bg); }
.wep-pill-brokerrevoked                { color: var(--wep-state-rolledback-fg);  background: var(--wep-state-rolledback-bg); }
.wep-pill-brokerdecommissioned         { color: var(--wep-state-quarantined-fg); background: var(--wep-state-quarantined-bg); }

/* ─── Tables ─── */
.wep-table-wrap {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  overflow: auto;
  /* LOAD-BEARING, paired with the overflow above — deleting it is not a
     tidy-up. `.wep-main` is a COLUMN flex container (display:flex;
     flex-direction:column; min-height:0; overflow:auto — see "The shell's
     SINGLE scroll region" above) and a table rendered straight into the
     workspace is a direct flex item of it. A flex item's automatic minimum
     size (min-height:auto) applies only while its overflow is `visible` —
     CSS Box Sizing 3 §4.1 — so the `overflow: auto` above resolves this
     wrap's minimum height to 0 and makes it the only shrinkable item on the
     page: the flex algorithm hands it every pixel the page is over budget.
     There is no per-axis escape either — `overflow-x: auto` with
     `overflow-y: visible` computes the visible half to `auto`
     (CSS Overflow 3 §3.1).
     Measured live on v0.177.0 at 1440: /fleet/groups/{id} rendered BOTH its
     tables at clientHeight 0 (of 257px and 1035px of rows); /audit/search
     739px of 3351px; /catalog/activity 738px of 1691px. Comfortable density
     is strictly worse. This is the #1859 .wep-endpoint-facts trap with
     `auto` in place of `hidden` — the spec condition is "overflow is not
     visible", never "overflow is hidden", which is why both guards written
     after #1859 (they match only the `hidden` spelling) missed it. */
  flex-shrink: 0;
  /* Containing block for absolute descendants (the .wep-sr-only
     ineligible-reason spans on non-selectable rows). Without this they
     anchor to the initial containing block, ESCAPE this overflow clip,
     and stretch the document below the 100vh shell — the catalog page
     grew a phantom window scrollbar. */
  position: relative;
}
/* ScrollContainer=false — the table needs no horizontal room at the
   authored 1440 rung, so it gets no scroll container of its own and
   `.wep-main` becomes the scrollport instead, which is what makes the
   sticky `thead` above work again (a scroll container of its own would
   leave `position: sticky` resolving against a scrollport that never
   moves). Mirrors WepDataTable.razor.css's `.wep-table-wrap-noscroll` —
   both copies move together (#887). */
.wep-table-wrap.wep-table-wrap-noscroll {
  overflow: visible;
}
/* Give the sticky header back the corner clip the overflow above used to do.
   `overflow: auto` clipped the table to the wrap's `border-radius`; `visible`
   does not, so `thead th`'s bg-2 — a DIFFERENT ground from the wrap's bg-1 —
   painted square into the two rounded top corners. Measured, not theorised:
   the 1440 light shot of catalog-activity moved 20 channel levels against a
   gate that allows 6, and all eight of that artboard's shots failed while the
   other 624 held. `- 1px` because the wrap's 1px border sits outside the
   radius, so the INNER curve the old clip followed is one pixel tighter.
   Framed only: `.wep-table-wrap-flush` has no border and no radius, so there
   is no corner to restore. Mirrored in
   src/Wep.ControlPlane.Web/Components/Primitives/WepDataTable.razor.css —
   both copies move together (#887). */
.wep-table-wrap.wep-table-wrap-noscroll:not(.wep-table-wrap-flush) .wep-table thead th:first-child {
  border-top-left-radius: calc(var(--wep-radius-sm) - 1px);
}
.wep-table-wrap.wep-table-wrap-noscroll:not(.wep-table-wrap-flush) .wep-table thead th:last-child {
  border-top-right-radius: calc(var(--wep-radius-sm) - 1px);
}
/* The two BOTTOM corners are deliberately NOT rounded here, and the matching
   `tbody tr:last-child td` rules were tried and removed rather than left in.
   `.wep-table` is `border-collapse: collapse`, and a collapsed border ignores
   `border-radius` on the cell — Chrome still clips a cell's BACKGROUND to the
   radius, which is why the `thead th` rules above work (they carry bg-2), but
   the last row's 1px `border-bottom` is a border, not a background, so nothing
   rounds it. Measured with a same-session A/B of catalog-activity against the
   pre-opt-out render: exactly 6 pixels differ in dark and 8 in light, three at
   each bottom corner plus one at each top corner, out of a 1440-wide shot.
   Rounding them would mean `border-collapse: separate`, which changes how every
   table in the kit draws every border. Not worth six pixels — the eight
   catalog-activity baselines carry the residual instead. */
.wep-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--wep-size-text-xs);
}
.wep-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--wep-z-sticky);
  background: var(--wep-color-bg-2);
  text-align: left;
  font-weight: var(--wep-weight-semi);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  padding: var(--wep-space-2) var(--wep-space-3);
  /* #2495: the rule is drawn by the ::after below, not by this border. The
     border stays, transparent, so the collapsed grid keeps its 1px and no row
     below the header moves. */
  border-bottom: 1px solid transparent;
}
/* #2495: THE HEADER RULE BELONGS TO THE STICKY CELL. As a collapsed
   `border-bottom` it was painted by the TABLE, beneath the cells that stick,
   and Chrome lost it three ways — measured in headed Chrome 154 on the
   Coverage table of /policy/update-grant, 2026-09-30:
   - Shifted in 0.05px steps, the table lost the rule under EVERY header cell
     at 12 of 80 subpixel offsets (the header's bottom edge in the last ~0.13px
     of a device pixel). With the cells not sticky: 0 of 80.
   - Unsubscribing a row in place at a 1280px viewport lost it under MANAGER
     and ACTIONS only — the #2067 U4 walk's picture — for Git.Git as much as
     for Microsoft.PowerToys, and a reload brought it back. At 1440 neither
     removal lost it: what matters is where the in-place update leaves the
     table, not which row went.
   - Once the header actually sticks, the rule scrolled away with the table.
   `background-clip: padding-box` on the cell brings none of it back.
   Drawn here instead, in the sticky cell's own layer, it sticks with the
   header and painted in all three: 0 of 80 offsets, every step of both
   removals, and stuck. `bottom: -1px` against the padding edge puts it where
   the collapsed border was: a collapsed cell holds HALF its 1px border, so the
   padding edge sits 0.5px above the grid line and this box spans the line
   ±0.5px. At 1x the render is byte-identical wherever the old rule painted —
   68 of 80 sweep offsets, and 655 of the 686 design captures (the other 31
   differ by 1-12 antialiased pixels on pill dots and corners, none on a
   rule). What remains is Chrome's: right after an in-place update, cells can
   snap this box to adjacent device rows — a 1px step at a column edge where
   there used to be a gap — until the next full layout. `border-collapse:
   separate`, or an inset box-shadow in place of the border, also restore the
   rule, but move every row under every kit header by half a pixel. print.css
   cancels this box: printed, the header is static. Mirrored in
   src/Wep.ControlPlane.Web/Components/Primitives/WepDataTable.razor.css —
   both copies move together (#887). */
.wep-table thead th::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--wep-color-border-2);
  pointer-events: none;
}
.wep-table tbody td {
  padding: var(--wep-space-2) var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
  vertical-align: middle;
  white-space: nowrap;
}
.wep-table tbody tr:hover { background: var(--wep-color-bg-2); }
.wep-table tbody tr.wep-row-selected { background: var(--wep-color-accent-bg); }
/* Plan 2026-05-14-001 U16: dim rows armed > 30 days to nudge operator
   housekeeping. Same opacity ramp as the existing 0.5 patterns elsewhere
   in this file (per the U16 round-2 design spec). */
.wep-table tbody tr.wep-row-dim { opacity: 0.55; }
.wep-table .wep-num,
.wep-table th.wep-num { text-align: right; font-family: var(--wep-font-mono); }
.wep-table .wep-mono { font-family: var(--wep-font-mono); }
/* #4561 U4e — a cell holding a long identifier with no break opportunity (the
 * signing keys' 64-digit SPKI fingerprint, a session token's id) breaks inside
 * itself, so the row's stamps keep their one line and the table its width.
 * Paint only: the copied value is unchanged. */
.wep-table tbody td.wep-cell-hash { white-space: normal; overflow-wrap: anywhere; }

/* #1865 part M — dense cells. Promoted from CatalogPackage.razor.css, its only
 * definition, which sat behind `.wep-pkg-page ::deep`: three of the four call
 * sites (the bulk-adopt batch + outcome tables, the bulk-promote partial table,
 * the recent-transitions table) live in components hosted from OTHER catalog
 * surfaces and fell back to UA cell padding. Declarations verbatim from that
 * copy.
 *
 * #1865 follow-up, now taken: the class was used ALONE on <table> at all four
 * call sites, and this modifier only sets PADDING — so those four tables had no
 * base table styling at all. No sticky uppercase header, no header rule, no row
 * borders, no tabular font size: four bare HTML tables with tight cells, inside
 * the bulk-adopt / bulk-promote modals and the recent-transitions panel. The
 * markup now carries `.wep-table .wep-table-dense` together, and the combined
 * selector above keeps the dense padding winning over the base cell padding. */
.wep-table-dense th,
.wep-table-dense td,
/* …and again at a higher specificity, because the four call sites now carry
   `.wep-table` too (see below) and `.wep-table thead th` / `.wep-table tbody td`
   would otherwise outrank the bare modifier and restore the roomy padding. */
.wep-table.wep-table-dense th,
.wep-table.wep-table-dense td {
  padding: var(--wep-space-1) var(--wep-space-2);
}

/* The package-id line under a friendly name in a catalog row: mono, one line,
 * ellipsised. Promoted from three BYTE-IDENTICAL scoped copies (CatalogWinget /
 * CatalogScoop / CatalogChocolatey .razor.css) — the same six declarations
 * written out three times because batch 4 was forbidden the kit.
 *
 * #1859, carried over from the CatalogWinget copy: the `overflow: hidden` here
 * is on a table cell's inline block, not on a child of the column-flex
 * `.wep-main` — no automatic-minimum-size interaction. `text-overflow` needs
 * the clip and the element is not a flex item. */
.wep-pkg-id-secondary {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Sortable column headers (catalog index). The <th> keeps its sticky/upper-
   case styling; the inner button inherits it and only adds the pointer
   affordance + a sort-direction arrow. */
.wep-th-sort {
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-1);
}
.wep-th-sort:hover,
.wep-th-sort.is-sorted { color: var(--wep-color-fg-1); }
/* The arrow takes the header's own size. It was 0.9em, which drew it below the
   12px floor (ADR 2026-10-12-001). */
.wep-th-sort-ind { font-size: inherit; }
.wep-table th.wep-num .wep-th-sort { justify-content: flex-end; }
.wep-table-controls {
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
  padding: var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
}
.wep-savedview {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: 2px var(--wep-space-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  font-size: var(--wep-size-text-2xs);
  font-family: var(--wep-font-mono);
  color: var(--wep-color-fg-2);
  background: var(--wep-color-bg-2);
}

/* ─── Drawer ─── */
.wep-drawer {
  position: fixed;
  top: 44px;
  right: 0;
  bottom: 0;
  width: 520px;
  background: var(--wep-color-bg-1);
  border-left: 1px solid var(--wep-color-border-2);
  box-shadow: var(--wep-shadow-drawer);
  z-index: var(--wep-z-drawer);
  display: flex;
  flex-direction: column;
}
/* #3742 U15: the head is a one-column grid so the close button can take a
 * second column beside the title and sub without wrapping them in a new
 * element. With no close button (no OnDismiss) there is no second column and
 * no gap, so the head lays out exactly as the block it was. */
.wep-drawer-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--wep-space-3);
  padding: var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
}
.wep-drawer-title {
  grid-column: 1;
  font-size: var(--wep-size-text-lg);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  margin: 0 0 var(--wep-space-1);
}
.wep-drawer-sub {
  grid-column: 1;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
/* A 28px square (the retired 432px catalog drawer's close button, carried
 * over), top-aligned beside the title and sub. Focus takes the global :focus-visible ring (tokens.css). */
.wep-drawer-close {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  font-size: 18px;
  line-height: 1;
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-2);
  cursor: pointer;
  transition: background var(--wep-motion-fast) var(--wep-ease-standard),
              color var(--wep-motion-fast) var(--wep-ease-standard);
}
.wep-drawer-close:hover {
  background: var(--wep-color-bg-3);
  color: var(--wep-color-fg-1);
}
.wep-drawer-body {
  flex: 1;
  overflow: auto;
  padding: var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
}
.wep-drawer-foot {
  padding: var(--wep-space-3) var(--wep-space-4);
  border-top: 1px solid var(--wep-color-border-1);
  display: flex;
  gap: var(--wep-space-2);
  background: var(--wep-color-bg-1);
}
/* A spacer at the start of a drawer's foot puts its buttons at the right, Cancel then the verb, as the app-in-use
   drawing draws them (#4388 PR D, Marc: "as drawn"); the modal foot's .wep-modal-foot-spacer is the same idiom. */
.wep-drawer-foot .wep-drawer-foot-spacer { margin-left: auto; }

/* ─── Definition list (drawer evidence) ─── */
.wep-dl {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: var(--wep-space-2) var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
}
.wep-dl dt {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  font-weight: var(--wep-weight-medium);
}
.wep-dl dd { margin: 0; font-family: var(--wep-font-mono); color: var(--wep-color-fg-1); word-break: break-all; }

/* #1865 part M — the SETTINGS key/value list. Five <dl class="wep-kv"> blocks
 * (AutoAdoptPolicyPanel, AuditRetentionWindowPanel ×2, AuditPrunePreviewPanel,
 * CommunityBrokers' decommission preview) have asked for this since they were
 * written and no stylesheet ever defined it, so they rendered as UA definition
 * lists — key on one line, value indented 40px beneath it.
 *
 * It is deliberately NOT an alias of `.wep-dl` above, and part I was right to
 * decline `.wep-kv` while it was dead. `.wep-dl` is the DRAWER EVIDENCE list:
 * uppercase 2xs keys and a mono, break-all value, because its values are
 * digests and identifiers. Every `wep-kv` value here is a SENTENCE ("Enabled —
 * newly Confident-recognized installs adopt automatically on ingest"), and
 * `.wep-dl`'s treatment would set that prose in mono and break it mid-word.
 * Same structure, different content, so: its own rule, keys sentence-case in
 * fg-3, values in prose at fg-1. */
.wep-kv {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--wep-space-1) var(--wep-space-3);
  margin: 0 0 var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
}
.wep-kv dt {
  color: var(--wep-color-fg-3);
  font-weight: var(--wep-weight-medium);
}
.wep-kv dd {
  margin: 0;
  color: var(--wep-color-fg-1);
  overflow-wrap: anywhere;
}
/* #3479 — a value that opts into the muted utility gets it. `.wep-kv dd` above
 * is (0,1,1) and the kit's `.wep-fg-3` is (0,1,0), so the rule that sets every
 * value to fg-1 also outranked the one value marked muted: the halt row's
 * public-index advisory (#3012) rendered at full strength on the page and on
 * its board alike. This restates the utility at the list's own weight plus a
 * class, (0,2,1). Every other value keeps fg-1; only a `dd` that carries
 * `wep-fg-3` changes. UpdateHaltQueueTests resolves the cascade over this
 * sheet on the rendered row and holds the advisory to fg-3. */
.wep-kv dd.wep-fg-3 { color: var(--wep-color-fg-3); }

/* ─── Section card (rare; only for evidence groupings) ─── */
.wep-section {
  border-top: 1px solid var(--wep-color-border-1);
  padding: var(--wep-space-4) 0;
}
/* #4561 U2 — the section that opens the page draws no top rule and no top
 * padding of its own: the head's bottom rule is its edge and the head's
 * margin its gap. Otherwise a page that opens on a section showed two rules a
 * gap apart. The page puts it right after the head; a board, as the first
 * child of the `.page-body` that follows the head. */
:where(.wep-page-head) + .wep-section,
:where(.wep-page-head) + * > .wep-section:first-child {
  border-top: 0;
  padding-top: 0;
}
/* A SECTION'S TITLE IS A SENTENCE (#4561 U3, Marc 2026-10-20): 16px semi-bold in
 * the body ink, the approved #4256 drawing's card title (tokens.css names
 * text-lg "section headings"). It was a 2xs uppercase eyebrow in fg-3. The words
 * in the markup were always sentence case; only the transform goes.
 *
 * The heading's level is the title's level. A title one level down (an h3 or
 * h4 under the section's h2: the endpoint page's "Covered by this catalog"
 * under "Installed packages", the halt queue's "Holding a slot" under
 * "In-flight slots") reads as `.wep-section-subtitle` below, so two levels
 * still read apart at 16px. The outline the markup already gives a screen
 * reader is the one the eye gets. */
.wep-section-title {
  font-size: var(--wep-size-text-lg);
  font-weight: var(--wep-weight-semi);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-1);
  margin: 0 0 var(--wep-space-2);
}
:is(h3, h4, h5, h6).wep-section-title {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
}
/* #1865 part M — a heading BELOW a section title, for a section that has more
 * than one part (FleetCveEvaluationLimits' "What this record claims is
 * affected" and "Pin a mapping from here"). Never defined, so both rendered as
 * UA <h3>: ~16px bold with 1em margins, louder than the `.wep-section-title`
 * they sit under. One step down in size and colour from the title, and the
 * kit's own rhythm above/below. */
.wep-section-subtitle {
  margin: var(--wep-space-4) 0 var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-2);
}

/* The page-lead paragraph under a page head or section title: what this surface
   is, in one measure-limited line. Sibling of .wep-empty-lead (same size, colour
   and 60ch measure) but left-aligned in the flow rather than centred in an empty
   state.

   #1860 part K — added, not restyled: three pages (PolicyCedarAuthoring,
   PolicyDenyList, PolicyGrants) have asked for `wep-section-intro` since they
   were written and NO stylesheet has ever defined it, so the lead rendered at
   body size across the full column width. A brand-new selector cannot regress
   anything that renders today, which is why this one rule is in the kit rather
   than duplicated into three scoped sheets.

   #1865 part M — `wep-section-lead` joins the selector, it does not get a rule
   of its own. Four call sites (CatalogRingSoakConfigPage, FleetGroups,
   FleetGroupDetail ×2) are all the same thing this rule already describes: a
   measure-limited lead <p> under a section title. Two names for one idiom is
   the vocabulary debt this batch exists to pay down, so they share the ONE
   place a future change has to be made. Declarations untouched. */
.wep-section-intro,
.wep-section-lead {
  max-width: 60ch;
  margin: 0 0 var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  line-height: 1.5;
  color: var(--wep-color-fg-3);
}

/* ─── Secondary text (#1865 part M) ───
 * `wep-meta` is the single biggest piece of dead vocabulary in the console: 59
 * uses across 15 files (DivergenceFindingCard ×16, PublisherTrustCard ×10,
 * BaselineGateWaiverCard ×7, CatalogEvidenceDrawer ×7, RollbackProposalQueue
 * ×5, PromoteReadBackChecklist ×3, CatalogPackage ×2, CatalogDriftHealCard ×2,
 * and seven more once each) and NO stylesheet has ever defined it. Every one of
 * those 59 elements rendered at body colour and body size — the explanatory
 * paragraph, the field label above an input and the primary evidence line all
 * at the same weight, which is most of why these cards read as undesigned.
 *
 * All 59 were read before this rule was written. They are one idiom: small,
 * muted, secondary text. The spread of ELEMENTS is what shapes the rule —
 * <p> (explanations, status lines), <span> (inline detail), <label> and
 * <legend> (form descriptors), and <li> (evidence lists in DivergenceFindingCard):
 *
 *   - No `display`. A third of the call sites are inline <span>s inside a
 *     sentence; forcing block would break them, and the block sites already
 *     have the display they want.
 *   - Vertical margins are ignored on non-replaced inline boxes, so the
 *     bottom margin reaches the <p>/<li>/<legend> sites and is inert on the
 *     spans and labels — one rule, no per-element exceptions.
 *   - No `max-width`. A measure belongs to a lead (.wep-section-intro above);
 *     this class is also worn by inline fragments and dense list rows.
 *
 * The recipe is not invented: FleetCveExposure.razor.css already wrote it out
 * longhand for `.wep-cve-coverage-meta`, with a header recording WHY ("`.wep-meta`
 * is a repo-wide class convention with NO rule in the design source, so the rule
 * below carries the coverage line's full recipe itself rather than inheriting
 * one"). That sheet's author measured what `.wep-meta` should have been; this is
 * that, in the kit. */
.wep-meta {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-3);
}
/* LADDER, 1920 rung (§3.6). Secondary prose gains a measure. The base rule
 * above deliberately carries no `max-width`: at the authored rung the content
 * column IS the measure. Above the authored rung it is not, and a 2,300px line
 * of muted explanatory text is exactly what "stretch" looks like. `max-width`
 * is inert on the inline <span>/<label> call sites — non-replaced inline boxes
 * ignore it — so one rule reaches the <p>/<li>/<legend> sites without an
 * exception list. Declared HERE, after the base, because a media query adds no
 * specificity (#2088): from the ladder block near the top of this file it tied
 * with the base and survived only by the accident that the base declares no
 * `max-width` of its own — one added there would have killed it silently. */
@media (min-width: 1920px) { .wep-meta { max-width: 90ch; } }

/* ─── Ring timeline ─── */
.wep-ring {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--wep-space-3);
  margin: var(--wep-space-3) 0;
}
/* LADDER, 1920 rung (§3.6). A fixed-count chrome strip stops growing: four
 * ring cells at ANY width, so at 2560 the base `1fr` gives a ~560px cell
 * carrying a ring name, a percentage and one meta line. Cap the TRACK, never
 * the count. Declared after the base it overrides — see #2088 in the ladder
 * header; written above it, this rule never applied at any width. */
@media (min-width: 1920px) { .wep-ring { grid-template-columns: repeat(4, minmax(0, 420px)); } }
.wep-ring-cell {
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-3);
  background: var(--wep-color-bg-1);
  position: relative;
}
.wep-ring-cell.is-active {
  border-color: var(--wep-state-promoted-rule);
  background: var(--wep-state-promoted-bg);
}
.wep-ring-cell.is-rolledback {
  border-color: var(--wep-state-rolledback-rule);
  background: var(--wep-state-rolledback-bg);
}
.wep-ring-cell.is-pending {
  border-style: dashed;
  color: var(--wep-color-fg-3);
}
.wep-ring-name {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-3);
}
.wep-ring-pct {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-lg);
  font-weight: var(--wep-weight-semi);
  margin-top: var(--wep-space-1);
}
.wep-ring-meta {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  margin-top: var(--wep-space-1);
}

/* ─── Code block ─── */
.wep-code {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  padding: var(--wep-space-3);
  white-space: pre;
  overflow: auto;
  color: var(--wep-color-fg-1);
}

/* ─── Banner ─── */
.wep-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--wep-space-3);
  padding: var(--wep-space-3) var(--wep-space-4);
  border-left: 3px solid var(--wep-state-cveblocked-rule);
  background: var(--wep-state-cveblocked-bg);
  color: var(--wep-state-cveblocked-fg);
  font-size: var(--wep-size-text-sm);
}
.wep-banner-warn {
  border-left-color: var(--wep-state-detonating-rule);
  background: var(--wep-state-detonating-bg);
  color: var(--wep-state-detonating-fg);
}
/* The banner's three internal slots and its degraded variant. WepBanner emits
 * all four, but until now only its scoped sheet defined them, so an artboard
 * could draw a banner's frame and not its contents — which is why fifteen
 * artboards hand-rolled a paragraph inside .wep-banner instead of composing the
 * shipped shape. Values are WepBanner.razor.css's own; the scoped sheet still
 * wins in the app on specificity, so nothing that renders today changes.
 *
 * `degraded` shares `warn`'s pair deliberately: both mean "this reading is not
 * to be trusted at face value", and giving degradation its own colour would add
 * an eighth semantic to a seven-state ramp for a distinction the operator does
 * not act on differently. */
.wep-banner-title {
  font-weight: var(--wep-weight-semi);
  flex-shrink: 0;
}
.wep-banner-body { flex: 1; }
.wep-banner-action {
  margin-left: auto;
  flex-shrink: 0;
}
.wep-banner-degraded {
  border-left-color: var(--wep-state-detonating-rule);
  background: var(--wep-state-detonating-bg);
  color: var(--wep-state-detonating-fg);
}

/* The third banner rung: neutral advisory. Fifteen artboards carried this rule
 * in their own inlined shell slice; 2143 U2 moved it here. It sits on the
 * accent pair rather than a state-ramp pair BY DESIGN — the state ramp's seven
 * colours all mean "this thing is in that state", and an advisory is not a
 * state of the object it sits above. Reusing, say, sbomready-blue here would
 * teach an operator that blue means "informational" on one surface and
 * "SBOM ready" on the next. */
.wep-banner-info {
  border-left-color: var(--wep-color-accent-1);
  background: var(--wep-color-accent-bg);
  color: var(--wep-color-accent-text);
}

/* #1754 — prose links INSIDE a banner keep the banner's own voice. A banner
 * already carries a semantic foreground/background pair from the state ramp
 * (.wep-banner = cveblocked, .wep-banner-warn = detonating), and each pair is
 * contrast-checked as a pair. Painting a link inside it with the console link
 * colour would (a) fight the banner's semantics — a compromised-instance
 * warning whose one link reads "ordinary navigation" — and (b) put an
 * unchecked colour on an unusual backdrop, which is precisely how #1750's
 * defect arose. currentColor inherits the checked foreground, so AA holds for
 * free and can never drift when a state token is re-tuned; the underline
 * carries the affordance alone (WCAG 1.4.1), which is why it is explicit here
 * rather than left to the UA default.
 *
 * Covers CompromisedInstanceBanner (every authenticated page, via MainLayout)
 * and every <WepBanner> body link — the settings-advanced trio among them. */
.wep-banner a {
  color: currentColor;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ─── Modal + typed-confirm (§18) ─── */
.wep-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(15 23 36 / 0.45);
  z-index: var(--wep-z-modal);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 10vh;
}
.wep-modal {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-pop);
  width: 480px;
  max-width: calc(100vw - var(--wep-space-4));
  max-height: calc(100vh - 20vh);
  display: flex;
  flex-direction: column;
}
/* The 360px `.wep-modal-sm` rung was retired 2026-09-07 (plan 2026-09-06-2143
   U5) along with WepModalSize.Small. A census of every modal ceremony in the
   web project — 30 across 22 files — found zero at that width, and nothing
   emitted the class: the rule styled a selector no markup ever carried. */
.wep-modal-md { width: 480px; }
.wep-modal-lg { width: 540px; }
/* #1939: an extra-large rung for dialogs that carry a working list (the Ingest
   package search shows name + id + version per candidate and a long URL
   placeholder); lg was still a postage stamp on a wide operator screen.
   .wep-modal's max-width keeps it inside narrow viewports. */
.wep-modal-xl { width: 720px; }
/* #1939: the Ingest dialog's submission field fills the (xl) dialog instead of
   keeping .wep-input's 320 px default — the long winget-pkgs URL placeholder and
   pasted URLs were cut off in a 720 px dialog. Scoped to the ingest dialog; this
   also gives .wep-catalog-ingest-modal (a markup-only token until now) its rule. */
.wep-catalog-ingest-modal .wep-input { width: 100%; }
.wep-modal-head {
  padding: var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
  flex: 0 0 auto;
}
.wep-modal-title {
  font-size: var(--wep-size-text-lg);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  margin: 0 0 4px;
}
.wep-modal-sub {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-modal-body {
  padding: var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
  line-height: var(--wep-line-relaxed);
  overflow-y: auto;
  flex: 1 1 auto;
}
/* #1865 part M — a modal's lead paragraph: the sentence that states what the
 * operator is about to cause. Fourteen call sites across eleven files; batch 4
 * wrote THREE copies of the rule (CatalogScoop, CatalogChocolatey and, behind
 * `.wep-pkg-page ::deep`, CatalogPackage) and the other eleven — every shared
 * modal component: CatalogTypedConfirmModal, CatalogRollbackModal,
 * CatalogClearSoakModal, CatalogBulkPromoteRingModal, CatalogBulkAdoptModal,
 * CatalogIngestModal, RollbackProposalDeclineModal, FleetCveDeployModal,
 * FleetEndpointDetail ×3 — render outside every scope that defined it.
 *
 * The three copies DISAGREED (finding, #1865): Scoop/Choco are byte-identical
 * at `margin-bottom: space-4` + `color: fg-2`; the CatalogPackage copy says
 * `space-3` + `fg-3`. fg-2 is the one that ships. `.wep-modal-body` above is
 * itself fg-2, so an fg-3 lead renders the consequence sentence DIMMER than the
 * detail beneath it — hierarchy inverted on a destructive-confirm surface, and
 * fg-3 is the metadata role (design.md §3.2), which a lead is not. */
.wep-modal-body-lead {
  margin: 0 0 var(--wep-space-4);
  max-width: 60ch;
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-2);
}
.wep-modal-foot {
  padding: var(--wep-space-3) var(--wep-space-4);
  border-top: 1px solid var(--wep-color-border-1);
  display: flex;
  gap: var(--wep-space-2);
  align-items: center;
  flex: 0 0 auto;
}
.wep-modal-foot .wep-modal-foot-spacer { margin-left: auto; }

/* Native <dialog> modal (static-SSR surfaces with no Blazor circuit — e.g.
   /community/agents enroll flow). The browser default <dialog> has its own
   bg/border/padding + centers itself + provides ::backdrop; we strip the
   chrome and let the inner .wep-modal card carry the visual style. */
.wep-modal-dialog {
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  max-width: calc(100vw - var(--wep-space-4));
  max-height: calc(100vh - 10vh);
  overflow: visible;
}
.wep-modal-dialog::backdrop {
  background: rgb(15 23 36 / 0.45);
}
/* The card inside the dialog grows to the dialog width and owns scrolling via
   its own .wep-modal-body (flex column, like the Blazor WepModal). */
.wep-modal-dialog .wep-modal {
  max-width: 100%;
  max-height: calc(100vh - 10vh);
}
/* Header gets a close affordance pinned to the top-right. */
.wep-modal-head { position: relative; }
.wep-modal-close {
  position: absolute;
  top: var(--wep-space-3);
  right: var(--wep-space-3);
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: var(--wep-size-text-lg);
  line-height: 1;
  border: 1px solid transparent;
  background: transparent;
  color: var(--wep-color-fg-3);
  border-radius: var(--wep-radius-xs);
  cursor: pointer;
}
.wep-modal-close:hover {
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-1);
}
/* "Enroll an agent" toolbar above the agents list. */
.wep-agents-toolbar {
  display: flex;
  justify-content: flex-end;
  margin: var(--wep-space-3) 0;
}

/* #1865 part M — the typed-confirm BLOCK. Its four children below have been
 * styled since the kit was written; the wrapper the markup puts them in never
 * was (WepTypedConfirm's root <div>, AuditSearch's export ceremony <section>,
 * CommunityUsers' role-change and delete ceremonies).
 *
 * One declaration, deliberately. The children each carry their own
 * `margin-top`, so a flex column with a `gap` here would ADD to that rhythm
 * rather than replace it (margins do not collapse inside a flex container) and
 * every ceremony would grow a second set of spaces. What the block genuinely
 * lacks is a measure: the phrase, the input and the feedback line are one
 * column of short strings, and on a full-width surface like /audit the input
 * stretched the whole page. 60ch is the console's prose measure, same as
 * `.wep-section-intro` and `.wep-empty-lead`. */
.wep-typed-confirm {
  max-width: 60ch;
}
.wep-typed-confirm-label {
  display: block;
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  margin-top: var(--wep-space-3);
}
.wep-typed-confirm-phrase {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-sm);
  margin-top: 4px;
  padding: 4px var(--wep-space-2);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  user-select: all;
  /* #3027: a phrase is ONE token with no spaces
     (`tenant:CATALOG:VERB:package.id:version`) and its container is narrow (the
     block's 60ch measure above, a 480px modal), so a long one had no break
     opportunity and painted past this border. `anywhere` breaks it inside the
     box and zeroes its min-content contribution. Paint only: the copied text
     and the phrase to type are unchanged. */
  overflow-wrap: anywhere;
}
.wep-typed-confirm-input {
  width: 100%;
  height: 32px;
  padding: 0 var(--wep-space-3);
  margin-top: var(--wep-space-3);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-sm);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-1);
}
.wep-typed-confirm-input:focus {
  outline: 2px solid transparent;
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
}
.wep-typed-confirm-input.is-matched {
  border-color: var(--wep-state-clean-rule);
  box-shadow: 0 0 0 1px var(--wep-state-clean-rule) inset;
}
.wep-typed-confirm-input.is-mismatch {
  border-color: var(--wep-state-cveblocked-rule);
  box-shadow: 0 0 0 1px var(--wep-state-cveblocked-rule) inset;
}
.wep-typed-confirm-input.is-shake { animation: wep-typed-confirm-shake 120ms var(--wep-ease-standard); }
@keyframes wep-typed-confirm-shake {
  0%   { transform: translateX(0); }
  25%  { transform: translateX(-4px); }
  50%  { transform: translateX(4px); }
  75%  { transform: translateX(-2px); }
  100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .wep-typed-confirm-input.is-shake { animation: none; }
}
.wep-typed-confirm-feedback {
  margin-top: var(--wep-space-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
}
.wep-typed-confirm-feedback.is-matched { color: var(--wep-state-clean-fg); }
.wep-typed-confirm-feedback.is-mismatch { color: var(--wep-state-cveblocked-fg); }

/* ─── DSL/YAML editor (§5.3) ─── */
.wep-dsl-editor {
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-1);
  overflow: hidden;
}
.wep-dsl-editor-head {
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
  padding: var(--wep-space-2) var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
}
.wep-dsl-editor-head-spacer { margin-left: auto; }
.wep-dsl-editor-head-sep { color: var(--wep-color-fg-1); }
.wep-dsl-editor-body {
  display: grid;
  grid-template-columns: 36px 1fr;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  line-height: 1.6;
}
.wep-dsl-editor-gutter {
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-4);
  text-align: right;
  padding: var(--wep-space-3) var(--wep-space-2);
  border-right: 1px solid var(--wep-color-border-1);
  user-select: none;
  margin: 0;
  white-space: pre;
}
.wep-dsl-editor-code {
  padding: var(--wep-space-3);
  white-space: pre;
  overflow: auto;
  color: var(--wep-color-fg-1);
  margin: 0;
}
/* #3741 — ONE LINE BOX PER LINE, SO THE CODE STAYS BESIDE ITS GUTTER. Each line
 * is a block span, and a block already ends its line: WepDslEditor writes
 * nothing between two spans (it used to write a "\n", which inside the <pre>
 * became a blank line box of its own and double-spaced the code pane while the
 * gutter stayed single-spaced). A blank line, such as the separator the Cedar
 * projection writes between two policy documents, is an EMPTY block, and an
 * empty block has no height: min-height keeps it one line tall, so the gutter
 * never counts a line the code pane does not draw. 1lh is the line the body's
 * line-height gives the text, whatever it is. policy-cedar-authoring.html drew
 * both halves of this as an artboard-only correction before it shipped. */
.wep-dsl-line {
  display: block;
  min-height: 1lh;
}
.wep-dsl-line.is-add {
  background: rgb(44 140 90 / 0.12);
  box-shadow: inset 2px 0 0 0 var(--wep-state-clean-rule);
}
.wep-dsl-line.is-mod {
  background: rgb(31 95 255 / 0.10);
  box-shadow: inset 2px 0 0 0 var(--wep-color-accent-1);
}
.wep-dsl-line.is-error {
  background: rgb(194 48 74 / 0.10);
  box-shadow: inset 2px 0 0 0 var(--wep-state-cveblocked-rule);
}

/* ─── Diff viewer (§5.7) ─── */
.wep-diff {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--wep-size-text-sm);
}
.wep-diff thead th {
  text-align: left;
  font-weight: var(--wep-weight-semi);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  padding: var(--wep-space-2) var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-2);
}
.wep-diff tbody td {
  padding: var(--wep-space-2) var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
  vertical-align: top;
}
.wep-diff-row.is-add    td:first-child { box-shadow: inset 2px 0 0 0 var(--wep-state-clean-rule); }
.wep-diff-row.is-mod    td:first-child { box-shadow: inset 2px 0 0 0 var(--wep-color-accent-1); }
.wep-diff-row.is-remove td:first-child { box-shadow: inset 2px 0 0 0 var(--wep-state-cveblocked-rule); }
.wep-diff-row.is-unchanged td { color: var(--wep-color-fg-3); }
.wep-diff-was, .wep-diff-now {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
}
.wep-diff-was { color: var(--wep-color-fg-3); text-decoration: line-through; }
.wep-diff-now { color: var(--wep-color-fg-1); }
.wep-diff-arrow {
  font-family: var(--wep-font-mono);
  color: var(--wep-color-fg-4);
  margin: 0 var(--wep-space-2);
}
.wep-diff-state {
  display: inline-block;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--wep-tracking-wide);
  padding: 2px 6px;
  border-radius: var(--wep-radius-xs);
}
.wep-diff-state.is-add    { color: var(--wep-state-clean-fg);      background: var(--wep-state-clean-bg); }
.wep-diff-state.is-mod    { color: var(--wep-color-accent-text); background: rgb(31 95 255 / 0.10); }
.wep-diff-state.is-remove { color: var(--wep-state-cveblocked-fg); background: var(--wep-state-cveblocked-bg); }
.wep-diff-state.is-unchanged { color: var(--wep-color-fg-4);       background: var(--wep-color-bg-2); }

/* ─── Drawer backdrop (§5.5 modal behaviour outside endpoint-detail) ─── */
.wep-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(15 23 36 / 0.20);
  z-index: calc(var(--wep-z-drawer) - 1);
}

/* ─── Verifiable lock affordance inside evidence drawer (§5.5) ─── */
.wep-dl-lock {
  display: inline-block;
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  margin-left: var(--wep-space-2);
  color: var(--wep-color-fg-3);
}
.wep-dl-lock:hover { color: var(--wep-state-clean-fg); }

/* ─── Saved-view chip (§5.13) ─── */
.wep-saved-view {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: 2px var(--wep-space-2);
  border: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-2);
  border-radius: var(--wep-radius-xs);
  text-decoration: none;
}
.wep-saved-view:hover {
  border-color: var(--wep-color-border-2);
  color: var(--wep-color-fg-1);
}
.wep-saved-view-prefix {
  color: var(--wep-color-fg-3);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
}

/* ─── Sparkline (§21) ─── */
.wep-sparkline {
  display: inline-block;
  vertical-align: middle;
  color: var(--wep-color-fg-2);
}
.wep-sparkline-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.wep-sparkline-threshold {
  stroke: var(--wep-color-fg-3);
  stroke-width: 1;
  stroke-dasharray: 3 2;
  fill: none;
}
.wep-sparkline.is-warning   { color: var(--wep-state-detonating-fg); }
.wep-sparkline.is-blocking  { color: var(--wep-state-cveblocked-fg); }

/* ─── Cedar policy simulator (§5.6) ─── */
.wep-cedar-sim {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  overflow: hidden;
}
.wep-cedar-sim-head {
  padding: var(--wep-space-3) var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
}
.wep-cedar-sim-title {
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
}
.wep-cedar-sim-verdict {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  padding: 4px var(--wep-space-2);
  border-radius: var(--wep-radius-xs);
}
.wep-cedar-sim-verdict.is-allow {
  color: var(--wep-state-clean-fg);
  background: var(--wep-state-clean-bg);
}
.wep-cedar-sim-verdict.is-deny {
  color: var(--wep-state-cveblocked-fg);
  background: var(--wep-state-cveblocked-bg);
}
.wep-cedar-sim-trace {
  padding: var(--wep-space-3);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  line-height: 1.7;
  margin: 0;
}
.wep-cedar-sim-trace li {
  list-style: none;
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: var(--wep-space-2);
  align-items: baseline;
  padding: 0;
}
.wep-cedar-sim-trace .glyph {
  display: inline-block;
  width: 16px;
  text-align: center;
  font-weight: var(--wep-weight-semi);
}
.wep-cedar-sim-trace li.is-pass .glyph { color: var(--wep-state-clean-fg); }
.wep-cedar-sim-trace li.is-skip .glyph { color: var(--wep-color-fg-4); }
.wep-cedar-sim-trace li.is-fail .glyph { color: var(--wep-state-cveblocked-fg); }
.wep-cedar-sim-trace .policy-id {
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-medium);
  margin-right: var(--wep-space-2);
}
.wep-cedar-sim-trace .effect {
  text-transform: uppercase;
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  color: var(--wep-color-fg-3);
  margin-right: var(--wep-space-2);
}
.wep-cedar-sim-trace .msg {
  color: var(--wep-color-fg-2);
}
.wep-cedar-sim-empty {
  padding: var(--wep-space-3) var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
}
.wep-cedar-sim-mismatch {
  padding: var(--wep-space-2) var(--wep-space-3);
  border-top: 1px solid var(--wep-color-border-1);
  background: var(--wep-state-cveblocked-bg);
  color: var(--wep-state-cveblocked-fg);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
}

/* ─── Catalog editor page (Unit C.1) ─── */
.wep-editor-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--wep-space-4);
  margin-top: var(--wep-space-3);
}
.wep-editor-column { min-width: 0; }
.wep-editor-textarea {
  display: block;
  width: 100%;
  margin-top: var(--wep-space-2);
  padding: var(--wep-space-3);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-1);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  line-height: 1.6;
  resize: vertical;
}
.wep-editor-textarea:focus {
  outline: 2px solid transparent;
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
}
.wep-editor-textarea-label {
  display: block;
  margin-top: var(--wep-space-3);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
.wep-editor-aside {
  position: sticky;
  top: var(--wep-space-4);
  align-self: start;
}

/* ─── Reconciliation telemetry cards (Unit C.1 / design.md §6.2) ─── */
/* ─── Sub-tabs (Catalog, Fleet, Inventory, the package page) ───
 * #4561 U2 (Marc, 2026-10-20): underline tabs inside the page head, on every
 * page that has a tab strip. PageHead renders the strip in its body slot, so
 * it takes the head's row under the title across both columns, and the head
 * keeps no padding under it (the `:has()` rule above). Each tab holds the
 * underline's 2px, so none shifts when it becomes active, and overlaps the
 * head's bottom rule by one pixel, as the #4213 and #4256 drawings draw it.
 * This was the list pages' variant (#4323) and the package page's (#4256);
 * the segmented control #881 made the kit's (2026-06-09) is retired. A count
 * beside a label is a step quieter than the label, on the active tab too. */
.wep-subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin: var(--wep-space-4) 0 0;
}
.wep-subtab {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: -1px;
  padding: var(--wep-space-2) var(--wep-space-3);
  border-bottom: 2px solid transparent;
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-regular);
  color: var(--wep-color-fg-3);
  text-decoration: none;
  transition: color var(--wep-motion-fast) var(--wep-ease-standard),
              border-color var(--wep-motion-fast) var(--wep-ease-standard);
}
.wep-subtab:hover { color: var(--wep-color-fg-1); }
.wep-subtab.is-active {
  border-bottom-color: var(--wep-color-accent-solid);
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-medium);
}
.wep-subtab-count {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
  font-weight: var(--wep-weight-medium);
  font-variant-numeric: tabular-nums;
  color: var(--wep-color-fg-4);
}
.wep-subtab.is-active .wep-subtab-count { color: var(--wep-color-fg-3); }

.wep-recon-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--wep-space-3);
  margin: var(--wep-space-3) 0;
}
.wep-recon-card {
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-3) var(--wep-space-4);
  background: var(--wep-color-bg-1);
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
}
.wep-recon-card.is-warning {
  border-left: 3px solid var(--wep-state-detonating-rule);
  background: var(--wep-state-detonating-bg);
}
.wep-recon-card.is-blocking {
  border-left: 3px solid var(--wep-state-cveblocked-rule);
  background: var(--wep-state-cveblocked-bg);
}
.wep-recon-card-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
.wep-recon-card-value {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xl);
  font-weight: var(--wep-weight-semi);
  line-height: 1;
  color: var(--wep-color-fg-1);
}
.wep-recon-card-unit {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
  margin-left: 4px;
}
.wep-recon-card-trend {
  margin-top: var(--wep-space-1);
}

/* ─── Per-ring install-fail breakdown (Plan 2026-06-15-001 U4 / R9).
       The same per-ring beacons the auto-rollback firing loop evaluates;
       a ring below MinSampleCount is marked thin (amber detonating ramp)
       so the operator sees the fail-closed reason. ─── */
.wep-recon-rings {
  margin: var(--wep-space-4) 0 var(--wep-space-2);
}
.wep-recon-rings-title {
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-2);
  margin: 0 0 var(--wep-space-1);
}
.wep-recon-rings-sub {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
  margin: 0 0 var(--wep-space-3);
  max-width: 60ch;
}
.wep-recon-ring-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--wep-space-3);
}
.wep-recon-ring-card {
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-3) var(--wep-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
}
.wep-recon-ring-card.is-thin {
  border-left: 3px solid var(--wep-state-detonating-rule);
  background: var(--wep-state-detonating-bg);
}
.wep-recon-ring-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wep-space-2);
}
.wep-recon-ring-name {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
.wep-recon-ring-thin-badge {
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-state-detonating-fg);
  border: 1px solid var(--wep-state-detonating-rule);
  border-radius: var(--wep-radius-sm);
  padding: 0 6px;
  white-space: nowrap;
}
.wep-recon-ring-rate {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xl);
  font-weight: var(--wep-weight-semi);
  line-height: 1;
  color: var(--wep-color-fg-1);
}
.wep-recon-ring-samples {
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
}

/* ─── Slow-burn readout (Plan 2026-08-20-001 U4 / #1456).
       The failure shape the per-ring rate is blind to: a version failing
       steadily with no fleet-wide success. The honesty clause above ties the
       two together; firing rows carry the cve-blocked (version-eject) ramp,
       near-firing the amber detonating ramp — the same visual grammar. ─── */
.wep-recon-rings-honesty {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
  margin: calc(-1 * var(--wep-space-2)) 0 var(--wep-space-3);
  max-width: 72ch;
  border-left: 2px solid var(--wep-color-border-2);
  padding-left: var(--wep-space-2);
}
.wep-recon-slowburn {
  margin: var(--wep-space-4) 0 var(--wep-space-2);
}
.wep-recon-slowburn-rows {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
}
.wep-recon-slowburn-row {
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-2) var(--wep-space-4);
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
  flex-wrap: wrap;
}
.wep-recon-slowburn-row.is-firing {
  border-left: 3px solid var(--wep-state-cveblocked-rule);
  background: var(--wep-state-cveblocked-bg);
}
.wep-recon-slowburn-row.is-near {
  border-left: 3px solid var(--wep-state-detonating-rule);
  background: var(--wep-state-detonating-bg);
}
.wep-recon-slowburn-coord {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  display: flex;
  align-items: baseline;
  gap: var(--wep-space-2);
}
.wep-recon-slowburn-ring {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
.wep-recon-slowburn-badge {
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  border-radius: var(--wep-radius-sm);
  padding: 0 6px;
  white-space: nowrap;
}
.wep-recon-slowburn-badge.is-firing {
  color: var(--wep-state-cveblocked-fg);
  border: 1px solid var(--wep-state-cveblocked-rule);
}
.wep-recon-slowburn-badge.is-near {
  color: var(--wep-state-detonating-fg);
  border: 1px solid var(--wep-state-detonating-rule);
}
.wep-recon-slowburn-metrics {
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  margin-left: auto;
}
.wep-recon-slowburn-link {
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-link);
  text-decoration: none;
  white-space: nowrap;
}
.wep-recon-slowburn-link:hover {
  color: var(--wep-color-link-hover);
  text-decoration: underline;
}

/* ─── Fleet Endpoints — metric strip + agent grid (Plan 2026-06-08-003).
       Prototype: docs/design/archive/nightfall/prototypes/Fleet Endpoints.html.
       All colours map to --wep-* tokens so it reskins in light + dark. ─── */
.wep-fleet-metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--wep-space-4);
  margin: var(--wep-space-5) 0;
}
@media (max-width: 1080px) { .wep-fleet-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .wep-fleet-metrics { grid-template-columns: 1fr; } }
/* LADDER, 1920 rung (§3.6). A fixed-count chrome strip stops growing: four
 * metric cards at ANY width above the 1080 reflow, so at 2560 the base `1fr`
 * gives a 561px card carrying one label and one number — measured headless on
 * fleet-index.html and fleet-agents.html, and 573px on the live /fleet by the
 * 2144 walk. Cap the TRACK, never the count: the 4/2/1
 * counts the rules above already promise are unchanged, and the evidence tables
 * below the strip stay full-bleed. The density win belongs to the evidence, not
 * to the chrome. Declared HERE, in the same run as the downward reflow it
 * belongs with, rather than in the ladder header block near the top of this
 * file, where it lost the tie on source order and was dead at every width
 * (#2088). */
@media (min-width: 1920px) { .wep-fleet-metrics { grid-template-columns: repeat(4, minmax(0, 420px)); } }

.wep-fleet-mcard {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-sm);
  padding: var(--wep-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-4);
  min-height: 148px;
}
.wep-fleet-mcard-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  font-weight: var(--wep-weight-semi);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wep-space-2);
}
.wep-fleet-mcard-tag {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: 0;
  text-transform: none;
  color: var(--wep-color-fg-4);
  font-weight: var(--wep-weight-regular);
}

/* card 1 — version distribution */
.wep-fleet-distbar {
  height: 6px;
  border-radius: var(--wep-radius-pill);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  overflow: hidden;
  display: flex;
}
.wep-fleet-distbar > i { display: block; height: 100%; }
.wep-fleet-distbar > i + i { box-shadow: inset 1px 0 0 var(--wep-color-bg-1); }
.wep-fleet-vrows {
  list-style: none;
  margin: auto 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
}
.wep-fleet-vrow { display: flex; align-items: center; gap: var(--wep-space-2); }
.wep-fleet-vswatch { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.wep-fleet-vver { font-size: var(--wep-size-text-sm); color: var(--wep-color-fg-2); }
.wep-fleet-vcount {
  margin-left: auto;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-medium);
}

/* card 2 — check-in gap */
.wep-fleet-gaprows { display: flex; flex-direction: column; gap: 10px; margin-top: auto; }
.wep-fleet-gaprow {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  align-items: center;
  gap: var(--wep-space-3);
}
.wep-fleet-glabel { font-size: var(--wep-size-text-xs); color: var(--wep-color-fg-2); }
.wep-fleet-gtrack {
  height: 5px;
  border-radius: var(--wep-radius-pill);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  overflow: hidden;
}
.wep-fleet-gtrack > i { display: block; height: 100%; border-radius: var(--wep-radius-pill); }
.wep-fleet-gcount {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-medium);
  min-width: 16px;
  text-align: right;
}
.wep-fleet-gaprow.is-zero .wep-fleet-gcount { color: var(--wep-color-fg-4); font-weight: var(--wep-weight-regular); }

/* sparkline card. #3961 removed the /fleet OCC failure card these were written
 * for (nothing measured it); kit-charts.html still draws the card shape, so the
 * rules its markup uses stay. `.wep-fleet-thresh`, which only that card and its
 * drawing used, left with it. */
.wep-fleet-sparkwrap { margin-top: auto; display: flex; flex-direction: column; gap: 10px; }
.wep-fleet-sparkhead { display: flex; align-items: baseline; gap: var(--wep-space-2); }
.wep-fleet-sval {
  font-size: var(--wep-size-text-xl);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  color: var(--wep-color-fg-1);
}
.wep-fleet-sunit { font-size: var(--wep-size-text-sm); color: var(--wep-color-fg-3); }
.wep-fleet-sparkwrap .wep-sparkline { display: block; width: 100%; height: 32px; }
.wep-fleet-sparkfoot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-4);
  font-family: var(--wep-font-mono);
}

/* big-number cards (+ alarm when > 0) */
.wep-fleet-bignum .wep-fleet-bigval { margin-top: auto; display: flex; align-items: baseline; gap: var(--wep-space-3); }
.wep-fleet-bnum {
  font-size: var(--wep-size-text-2xl);
  line-height: 1;
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  color: var(--wep-state-clean-fg);
}
.wep-fleet-bcap { font-size: var(--wep-size-text-sm); color: var(--wep-color-fg-3); }
.wep-fleet-mcard.is-alarm { border-color: var(--wep-state-cveblocked-rule); background: var(--wep-state-cveblocked-bg); }
.wep-fleet-mcard.is-alarm .wep-fleet-bnum { color: var(--wep-state-cveblocked-fg); }
.wep-fleet-mcard.is-alarm .wep-fleet-mcard-label { color: var(--wep-state-cveblocked-fg); }
/* #1431 — amber warning variant (projection stalled: cursor frozen with spans pending,
   gap 0 vacuous). Mirrors is-alarm with the detonating (warning) tokens; is-alarm wins
   when both are relevant because the markup only ever emits one of the two. */
.wep-fleet-mcard.is-warn { border-color: var(--wep-state-detonating-rule); background: var(--wep-state-detonating-bg); }
.wep-fleet-mcard.is-warn .wep-fleet-bnum { color: var(--wep-state-detonating-fg); }
.wep-fleet-mcard.is-warn .wep-fleet-mcard-label { color: var(--wep-state-detonating-fg); }

/* ─── Fleet overview — the three bands above the endpoint table (#3966 U5).
 * They replaced the metric strip on /fleet: the trust sentence, the exception
 * views and the ring rows. Every number in them is a link to the endpoints it
 * counts (ADR 2026-10-08-001), so none of these rules styles a figure as a
 * gauge: a count is link-coloured text or a chip, at body size.
 *
 * Calm wears no attention colour. The sentence's amber is for what the page
 * could not read or cannot vouch for (the list, the decommission marks, the
 * audit-span projection), and the words say which, so the colour is never the
 * only channel. The selector is two classes deep on purpose: `.wep-link` is
 * declared later in this file and would otherwise win the tie on the one
 * clause that is both a link and a warning. ─── */
.wep-fleet-trust {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--wep-space-1) var(--wep-space-5);
  margin: var(--wep-space-5) 0 var(--wep-space-3);
  font-size: var(--wep-size-text-md);
  color: var(--wep-color-fg-1);
}
.wep-fleet-trust.is-warn,
.wep-fleet-trust .wep-fleet-trust-warn { color: var(--wep-state-detonating-fg); }
/* The read time: the footer's muted fg-4, one size down. It is a fact about
 * the page, not about the fleet, and it never changes while the tab is open. */
.wep-fleet-trust-read {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-4);
}

/* The exception chips are the kit's `.wep-chip-row`; this is the band's own
 * spacing, and the lines that explain a view the load could not evaluate, an
 * address value the page does not know, or what the open view lists. */
.wep-fleet-views { margin: 0 0 var(--wep-space-4); }
.wep-fleet-view-note {
  margin: var(--wep-space-2) 0 0;
  max-width: 100ch;
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-3);
}
.wep-fleet-view-note.is-warn { color: var(--wep-state-detonating-fg); }
.wep-fleet-section > .wep-fleet-view-note { margin: 0 0 var(--wep-space-3); }

/* One row per ring, always four, so the band keeps its height whatever the
 * fleet's size (R16). A row is the ring's name and its figures; plan unit U7
 * adds each ring's halted package updates beside the endpoint link. */
.wep-fleet-rings {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
}
.wep-fleet-ring {
  display: flex;
  align-items: baseline;
  gap: var(--wep-space-4);
  padding: var(--wep-space-2) var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
}
.wep-fleet-ring + .wep-fleet-ring { border-top: 1px solid var(--wep-color-border-1); }
/* The ring the address filters to: the selected-row tint, the accent's role. */
.wep-fleet-ring.is-current { background: var(--wep-color-accent-bg); }
.wep-fleet-ring-name { min-width: 8ch; color: var(--wep-color-fg-2); }
.wep-fleet-ring-figure.is-none { color: var(--wep-color-fg-4); }

/* toolbar */
.wep-fleet-section { margin-top: var(--wep-space-8); }
.wep-fleet-toolbar {
  display: flex;
  align-items: center;
  gap: var(--wep-space-3);
  margin-bottom: var(--wep-space-4);
  flex-wrap: wrap;
}
/* #4602 — the field is laid out as the catalog list's search box is (.wep-list-search): the label is the box, with
 * the border, the hover and the focus ring (on :focus-within); the glyph is a flex item; the input inside is
 * borderless. #4561 U2 had moved the glyph from translateY(-50%) to a whole-pixel offset, and an absolutely placed
 * glyph still rasterised two ways from one hosted gate run to the next on one Chrome build; the catalog's, laid out
 * in flex, never did. The geometry is the field's as before, in pixels at every density: 36px tall, the glyph 12px
 * in from the box's outer edge (11px inside its 1px border) and the text 36px in. #4655: the glyph is the shared
 * search icon at 14px, the one the catalog's box wears, which has never rasterised two ways; the hand-drawn
 * 16-unit circle it replaces flipped one pixel column of its arc from run to run even on whole-pixel rows. The
 * gap grows by the 2px the glyph lost, so the text still starts 36px in, and the glyph is fg-3 as the catalog's
 * is (Marc, 2026-10-22). */
.wep-fleet-search {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 240px;
  max-width: 420px;
  height: 36px;
  padding: 0 var(--wep-space-3) 0 11px;
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
}
.wep-fleet-search:hover { border-color: var(--wep-color-border-3); }
.wep-fleet-search:focus-within {
  border-color: var(--wep-color-border-focus);
  outline: 2px solid transparent;
  box-shadow: var(--wep-focus-ring);
}
.wep-fleet-search-glyph {
  flex: none;
  display: inline-flex;
  color: var(--wep-color-fg-3);
  pointer-events: none;
}
.wep-fleet-search input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  padding: 0;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  background: transparent;
  border: 0;
}
/* The ring is the box's (above): the field draws none of its own, and keeps a transparent outline, which forced
 * colours paints. */
.wep-fleet-search input:focus,
.wep-fleet-search input:focus-visible {
  outline: 2px solid transparent;
  box-shadow: none;
}
.wep-fleet-search input::placeholder { color: var(--wep-color-fg-4); }
.wep-fleet-select { position: relative; }
.wep-fleet-select select {
  appearance: none;
  height: 36px;
  padding: 0 32px 0 var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  cursor: pointer;
}
.wep-fleet-select svg {
  position: absolute;
  right: 10px;
  top: 11px;
  width: 14px;
  height: 14px;
  color: var(--wep-color-fg-3);
  pointer-events: none;
}
.wep-fleet-count { margin-left: auto; font-size: var(--wep-size-text-xs); color: var(--wep-color-fg-3); }
.wep-fleet-count b { color: var(--wep-color-fg-2); font-weight: var(--wep-weight-medium); }

/* table card */
.wep-fleet-tcard {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-sm);
  overflow: hidden;
}
.wep-fleet-tscroll { overflow-x: auto; }
.wep-fleet-table { width: 100%; border-collapse: collapse; font-size: var(--wep-size-text-sm); min-width: 880px; }
.wep-fleet-table thead th {
  background: var(--wep-color-bg-2);
  border-bottom: 1px solid var(--wep-color-border-2);
  text-align: left;
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  padding: 11px var(--wep-space-4);
  white-space: nowrap;
}
.wep-fleet-table thead th.wep-fleet-r { text-align: right; }
.wep-fleet-table tbody td {
  padding: 0 var(--wep-space-4);
  height: 52px;
  border-bottom: 1px solid var(--wep-color-border-1);
  color: var(--wep-color-fg-2);
  vertical-align: middle;
  white-space: nowrap;
}

/* THE FLEET SIZE SURVIVES THE TABLE BECOMING A WepDataTable (#2086 follow-up).
 *
 * FleetEndpointList renders through the primitive now, so its <table> carries
 * `wep-fleet-table` (this file's, specificity 0,1,0) AND `wep-table` — and the
 * <table> element is authored by the PRIMITIVE, so the primitive's scoped copy
 * of the kit table rules matches it too, as `.wep-table[b-…]` at 0,2,0. That
 * outranks the bare class above AND is served later (App.razor links _shell.css
 * before the scoped bundle), so `.wep-table { font-size: var(--wep-size-text-xs) }`
 * would take the table element and every cell inheriting from it would drop a
 * step: measured on the shipped cascade when the table still had the two count
 * columns #3961 removed, those counts and the principal went 13px -> 12px, and
 * the table's minimum 1022px -> 1011px. Text a step smaller than the drawing,
 * with nothing on the page to say so.
 *
 * One type selector buys the tie-break, and it names the exact case it is for.
 * NOT declared on the <td> instead: `.wep-fleet-table tbody td` is 0,2,1 and
 * would out-specify `.wep-fleet-ver` / `.wep-fleet-checkin` / `.wep-fleet-principal`
 * (0,1,0) — which the ARTBOARDS put directly on the cell where the shipped
 * markup puts them on a span inside it. That version moved all sixteen fleet
 * baselines; this one moves none. */
table.wep-table.wep-fleet-table { font-size: var(--wep-size-text-sm); }
.wep-fleet-table tbody tr:last-child td { border-bottom: none; }
.wep-fleet-table tbody tr { cursor: pointer; transition: background var(--wep-motion-fast) var(--wep-ease-standard); }
.wep-fleet-table tbody tr:hover td { background: var(--wep-color-bg-3); }
.wep-fleet-table tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--wep-color-accent-1); }
.wep-fleet-table td.wep-fleet-r { text-align: right; font-family: var(--wep-font-mono); }

/* THE STATUS CELL IS A BADGE STACK, AND IT WRAPS (#2086).
 *
 * `.wep-fleet-table tbody td` is nowrap, which is right for a hostname, an
 * agent version, a timestamp and a UPN — every one of them an atomic token
 * that must never break mid-string. The status cell is the exception: it
 * carries the health pill plus up to four independent badges
 * (FleetEndpointList emits held / update stuck / below floor / platform
 * behind, each conditional), so nowrap made ONE cell's width a function of
 * how many things were wrong with an endpoint, with no ceiling. Two badges
 * were enough to push the ACTIONS column and the `view detail →` link off
 * the viewport — measured on fleet-agents.html, whose three-badge row needs
 * 1285px of table in a 1170px workspace: 115px hidden, in the accepted
 * artboard itself, before the live console adds a fourth badge.
 *
 * Wrapping bounds it: the cell's minimum becomes the widest SINGLE badge
 * instead of the sum, so an endpoint with five problems is taller, never
 * wider. Same table after: 1134px, everything on screen.
 *
 * Those are the numbers of the eight-column table #2086 fixed. Re-measured
 * 2026-10-08 on the six-column table #3961 left, on the two-badge row of
 * fleet-index-attention.html (`update blocked` + `winget needs reboot`), at
 * 1440 with PRINCIPAL counted in: 944px of table with this cell forced back to
 * nowrap, 819px wrapped, in a 1157px workspace (the 1440 viewport less the nav,
 * the gutters and headless Chrome's scrollbar). Two badges
 * no longer overflow the workspace on their own; the stack still has no ceiling
 * without the wrap, and #3966 gave a row one more badge it can wear.
 *
 * Held back a column as well? It was not available here when this was written,
 * and that was the finding: the reveal mechanism (`wep-col-reveal-1920`) is
 * anchored on WepDataTable's own <table> by `::deep` — deliberately, per KTD3 —
 * and this was a raw <table class="wep-fleet-table">. THAT MIGRATION HAS SINCE
 * LANDED. FleetEndpointList renders through the primitive and holds PRINCIPAL
 * back to 1920 while it is masked (fleet-index.html restates the effect for the
 * artboard). This wrap rule is still the cause fix and still carries the whole
 * of the badge stack's bound — a held-back column bounds the table, not the
 * cell, and five badges in a nowrap cell would overflow again with or without
 * it.
 *
 * ONE DECLARATION IS ENOUGH, and no width floor is needed: `.wep-pill` is
 * itself `white-space: nowrap`, so normal wrapping here breaks only at the
 * spaces BETWEEN badges. No label can split mid-phrase, and the cell's
 * minimum is the widest single pill. */
.wep-fleet-table tbody td.wep-fleet-status {
  white-space: normal;
}

.wep-fleet-epcell { display: flex; align-items: center; gap: var(--wep-space-3); }
.wep-fleet-epico {
  width: 32px;
  height: 32px;
  border-radius: var(--wep-radius-xs);
  flex: none;
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-2);
  display: grid;
  place-items: center;
  color: var(--wep-color-fg-3);
}
.wep-fleet-epico svg { width: 16px; height: 16px; }
.wep-fleet-epmeta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.wep-fleet-ephost { font-size: var(--wep-size-text-sm); color: var(--wep-color-fg-1); font-weight: var(--wep-weight-medium); }
.wep-fleet-epsub { font-size: var(--wep-size-text-2xs); color: var(--wep-color-fg-4); }
.wep-fleet-ver { font-size: var(--wep-size-text-xs); color: var(--wep-color-fg-2); }
.wep-fleet-checkin { font-size: var(--wep-size-text-xs); color: var(--wep-color-fg-3); }
.wep-fleet-checkin.is-now { color: var(--wep-state-clean-fg); }
.wep-fleet-principal { color: var(--wep-color-fg-3); }
.wep-fleet-principal.is-unknown { color: var(--wep-color-fg-4); font-style: italic; }
.wep-fleet-viewlink {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
  text-decoration: none;
  font-weight: var(--wep-weight-medium);
}
.wep-fleet-viewlink svg { width: 13px; height: 13px; transition: transform var(--wep-motion-fast) var(--wep-ease-standard); }
.wep-fleet-table tbody tr:hover .wep-fleet-viewlink { color: var(--wep-color-link); }
.wep-fleet-table tbody tr:hover .wep-fleet-viewlink svg { transform: translateX(2px); }

.wep-fleet-tfoot {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: 11px var(--wep-space-4);
  border-top: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-2);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-mono);
}
.wep-fleet-tfdot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--wep-state-clean-rule);
  flex: none;
  box-shadow: 0 0 0 3px var(--wep-state-clean-bg);
}

/* ─── Endpoint detail timeline (Unit B.2 / design.md §15.4) ─── */
.wep-endpoint-timeline,
.wep-endpoint-spans,
.wep-agent-versions,
.wep-agent-checkin {
  list-style: none;
  margin: var(--wep-space-2) 0;
  padding: 0;
}
.wep-endpoint-timeline li {
  display: grid;
  /* when | status pill | body — content-sized rails so the timestamp never
     wraps and the pills align, with the body taking the rest (a long headline
     wraps in place instead of colliding into the next cell). */
  grid-template-columns: max-content max-content 1fr;
  gap: var(--wep-space-2) var(--wep-space-3);
  align-items: start;
  padding: var(--wep-space-2) 0;
  border-bottom: 1px solid var(--wep-color-border-1);
  font-size: var(--wep-size-text-sm);
}
.wep-timeline-when { white-space: nowrap; color: var(--wep-color-fg-3); }
/* min-width:0 lets a long headline wrap instead of forcing the grid wider. */
.wep-timeline-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.wep-timeline-body strong { overflow-wrap: anywhere; }
.wep-timeline-trace { font-size: var(--wep-size-text-2xs); opacity: 0.65; }
.wep-endpoint-spans li {
  padding: var(--wep-space-2) 0;
  border-bottom: 1px solid var(--wep-color-border-1);
}
.wep-agent-versions li,
.wep-agent-checkin li {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  padding: 2px 0;
}
.wep-fg-3 { color: var(--wep-color-fg-3); }
/* #4561 U4 — a timestamp (WepTime, design.md §10): "2026-05-03T06:53Z · 4 m"
 * never breaks across a line, and its digits keep one width so stamps in a
 * column line up. The face is its context's: mono in a table cell that wears
 * .wep-mono, the prose face in a sentence. print.css drops its relative half. */
.wep-time {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* #1865 part M — the primary-text sibling of the utility above. Three call
 * sites (FleetGroupDetail's friendly package name, FleetEndpointDetail ×2) pair
 * `wep-fg-1` on the name with `wep-mono wep-fg-3` on the package id beneath it,
 * to say "this is the answer, that is the identifier". Only half the pair had a
 * rule, so both lines rendered at the same colour and the distinction the
 * markup was drawing did not exist. */
.wep-fg-1 { color: var(--wep-color-fg-1); }

/* ─── Message colour roles (#1865 part M) ───
 * `wep-ok` / `wep-error` are written as a PAIR — fifteen of eighteen call sites
 * are literally `@(succeeded ? "wep-ok" : "wep-error")` on one operator-feedback
 * <p>. They were defined only inside `.wep-pkg-page ::deep` in
 * CatalogPackage.razor.css, so on every other host of those shared cards (the
 * evidence drawer is rendered from CatalogWinget, CatalogScoop and
 * CatalogChocolatey too, and CatalogEditor's promote flash had no rule
 * anywhere) both halves rendered at body colour. Promoted together on purpose:
 * shipping only the half this batch was scoped to would paint success green and
 * leave failure indistinguishable from ordinary copy on the same line.
 *
 * A COLOUR role, nothing else. CatalogScoop/CatalogChocolatey also gave their
 * `.wep-ok` a `font-family: mono` + `2xs` + top margin — the subscribe FLASH
 * LINE's typography, not the semantics of "this succeeded". Promoting that half
 * would have set every success message in mono while its `wep-error` sibling
 * stayed prose. That typography stays with the two flash lines it belongs to,
 * under `.wep-subscribe-flash` in their own sheets. */
.wep-ok    { color: var(--wep-state-clean-fg); }
.wep-error { color: var(--wep-color-danger-text); }

/* ─── Inline content link (#1740) ───
 * There is no bare `a { color }` anywhere in this sheet — every themed anchor
 * carries a class that colours it (.wep-crumbs a, .wep-subtab,
 * .wep-fleet-viewlink, .wep-section-link, .wep-auth-card a…). An anchor that
 * carries only a typographic class (.wep-mono) therefore falls to the UA
 * default #0000EE, which measures 2.05:1 on the dark canvas (#0a0e17) — the
 * exact defect the `.wep-auth-card a:not(.wep-btn)` rule below was written to
 * cure, one surface at a time. This promotes that cure to the reusable token the
 * console lacked, so an inline link in body copy or a dense row is a
 * one-class change and never a new one-off rule.
 *
 * Treatment (#1750): the dedicated link token, NOT accent-1. accent-1 was
 * design.md §Color's declared link role, but as light-theme text it does not
 * clear the 4.5:1 AA floor on every backdrop a link lands on: #1750 measured
 * Nightfall's accent at 4.47:1 on bg-1 / 4.21:1 on bg-0 / 3.89:1 on the
 * accent-bg row tint, and v0's ledger blue (shipped since the revert) still
 * measures 4.495:1 on bg-2 / 4.07:1 on bg-3 / 4.40:1 on accent-bg. It stays
 * the accent for FOCUS RING and SELECTED-ROW TINT, where the 3:1 non-text
 * floor governs (not for button fill: a label is text, #1764); only the text
 * role moved to --wep-color-link, which clears 4.5:1 in both themes on every
 * backdrop (worst case on bg-3: light 4.62, dark 4.67; hover strictly better
 * in both). UA underline kept so the affordance never rides on colour alone
 * (WCAG 1.4.1). */
.wep-link {
  color: var(--wep-color-link);
  text-underline-offset: 2px;
}
.wep-link:hover { color: var(--wep-color-link-hover); }

/* ─── Cedar policy authoring three-pane (Unit D.2 / design.md §15.2) ─── */
.wep-cedar-grid {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr) 360px;
  gap: var(--wep-space-3);
  margin-top: var(--wep-space-3);
}
.wep-cedar-sidebar { min-width: 0; }
.wep-cedar-editor { min-width: 0; }
.wep-cedar-aside {
  position: sticky;
  top: var(--wep-space-4);
  align-self: start;
}
.wep-test-case-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.wep-test-case-list li button {
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-left: 2px solid transparent;
  padding: var(--wep-space-1) var(--wep-space-2);
  cursor: pointer;
  font: inherit;
  color: var(--wep-color-fg-1);
}
.wep-test-case-list li.is-active button {
  border-left-color: var(--wep-color-accent-1);
  background: var(--wep-color-bg-2);
}
.wep-test-case-list li.wep-empty {
  padding: var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
}

/* ─── 3-way merge grid (Unit D.3 / design.md §32.2) ─── */
.wep-merge-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--wep-space-4);
  margin-top: var(--wep-space-3);
}

/* ─── Command palette (Unit E.1 / design.md §5.14, §19) ─── */
.wep-cmdk {
  display: none;
}
/* #3745 — THE RESULT LIST SCROLLS. The palette is fixed 8vh from the top and
 * clips at its edges, and until #3745 nothing bounded its height and no rule
 * named .wep-cmdk-list at all: the list grew to its full height (the empty
 * query lists about 37 rows) inside a fixed box, so on a 900px window every row
 * from about the 25th down sat below the screen. A fixed box does not move when
 * the page scrolls, so those rows (Settings, My account, Diagnostics, every Run
 * command) could not be reached at all.
 *
 * The palette now stops 8vh above the bottom edge, as it starts 8vh below the
 * top, and lays its parts out as a column: the input, the "?" scope's status
 * rows and their label keep their height (flex: none — a status row that wraps
 * must not be squeezed back to its 28px floor, #2905), and the listbox takes
 * what is left and scrolls. overscroll-behavior keeps a wheel at the end of the
 * list from scrolling the page behind the modal. The arrow keys keep the
 * selected row in view through js/paletteActiveRow.js, which WepCommandPalette
 * calls after a render that moved the selection. */
.wep-cmdk.is-open {
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 8vh;
  left: 50%;
  transform: translateX(-50%);
  width: 580px;
  max-width: calc(100vw - var(--wep-space-4));
  max-height: calc(100vh - 16vh);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-pop);
  z-index: var(--wep-z-modal);
  overflow: hidden;
}
.wep-cmdk.is-open > * {
  flex: none;
}
.wep-cmdk.is-open > .wep-cmdk-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.wep-cmdk-input {
  display: grid;
  grid-template-columns: 32px 1fr;
  align-items: center;
  gap: var(--wep-space-2);
  padding: var(--wep-space-3) var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
  font-family: var(--wep-font-mono);
}
.wep-cmdk-input input {
  border: none; outline: none; background: transparent;
  font: inherit; color: var(--wep-color-fg-1);
}
/* #3087 — while plain text's entity search runs, the rule under the input takes the accent: the
 * slow path of design.md section 19 (the last hits stay listed below it). A colour, never a spinner
 * or a moving bar (section 10); it costs no height, so no row moves. */
.wep-cmdk-input.is-searching { border-bottom-color: var(--wep-color-accent-solid); }
.wep-cmdk-scope {
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  text-transform: uppercase;
  letter-spacing: var(--wep-tracking-wide);
}
.wep-cmdk-section {
  padding: 4px var(--wep-space-3);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
/* #2905 — every row is a <button>, and :where(button) fixes a button at 28px.
 * With no height of its own a row that wrapped (the "?" scope's prompt,
 * "… e.g. principal:m.borer action:catalog.subscriptionRemoved", wraps at
 * 580px) drew its second line over the next row, or past the palette's
 * clipped edge. Wrap, not truncate (operator decision on #2905): the prompt's
 * example query and a hit's meta are what the row exists to show. The row
 * grows with its text and keeps the button's 28px as a floor, as .wep-multi
 * and :where(textarea) do.
 * CommandPaletteRowHeightDesignTests pins both declarations. */
.wep-cmdk-row {
  display: grid;
  grid-template-columns: 22px 1fr;
  align-items: center;
  gap: var(--wep-space-2);
  height: auto;
  min-height: 28px;
  padding: 6px var(--wep-space-4);
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  font: inherit;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-1);
  cursor: pointer;
}
.wep-cmdk-row:hover { background: var(--wep-color-bg-2); }
.wep-cmdk-row .kind { color: var(--wep-color-fg-3); text-align: center; }
.wep-cmdk-row .meta { color: var(--wep-color-fg-3); font-size: var(--wep-size-text-2xs); margin-left: var(--wep-space-2); }
/* The row treatments, mirrored from WepCommandPalette.razor.css (both copies
 * move together — the #887 lesson). The scoped sheet reaches only the
 * component, so without these every prototype drew a status row and a
 * Cedar-denied row at full emphasis, indistinguishable from a hit (#2906).
 * CommandPaletteRowTreatmentDesignTests pins the two copies to each other.
 *
 * .is-denied: a Cedar-denied Run row. Its glyph and title dim to 55 % — a
 * static state, never animated — and its deny reason in .meta stays at full
 * strength. The dim is never on the row itself: opacity composites an element
 * and its descendants as one group, so a child cannot be more opaque than its
 * parent, and the "opacity: 1" on .meta that used to follow a row-level dim
 * left the reason at 55 % (#2964). CommandPaletteDeniedReasonOpacityDesignTests
 * pins it. A click on it does nothing, so it drops the pointer affordance the
 * row rules above give every row (#3041, operator decision): the default
 * cursor, and under the pointer the transparent ground it has at rest, not the
 * bg-2 fill of a row a click runs. .is-status: the "?" scope's informational
 * rows (prompt, searching, zero match, unavailable, ignored facet, platform
 * events) — role="status", non-actionable, reduced emphasis, no pointer
 * affordance: the default cursor and, as on the denied row, the transparent
 * ground under the pointer. Until #3237 (operator decision) it dropped only
 * the cursor and took the bg-2 fill. */
.wep-cmdk-row.is-denied .kind,
.wep-cmdk-row.is-denied strong {
  opacity: 0.55;
}
.wep-cmdk-row.is-denied {
  cursor: default;
}
.wep-cmdk-row.is-denied:hover {
  background: transparent;
}
@media (prefers-reduced-motion: reduce) {
  .wep-cmdk-row.is-denied,
  .wep-cmdk-row.is-denied .kind,
  .wep-cmdk-row.is-denied strong {
    transition: none;
  }
}
.wep-cmdk-row.is-status {
  opacity: 0.7;
  cursor: default;
  font-style: italic;
}
.wep-cmdk-row.is-status:hover {
  background: transparent;
}
/* .is-active: the keyboard-selected row, the one Enter runs and the input's
 * aria-activedescendant names. Focus stays in the input, so until #2965 no
 * rule anywhere marked it and ↑/↓ moved the selection invisibly. It takes the
 * hover fill (the keys and the pointer mark a row the same way) and a 2px
 * accent rail on its left edge, which tells it from a row the pointer is only
 * over. Static, never animated. Under forced colours the fill and the rail are
 * both stripped, so the #2053 block at the end of this file gives it a
 * Highlight outline; the scoped sheet carries the same rule. */
.wep-cmdk-row.is-active {
  background: var(--wep-color-bg-2);
  box-shadow: inset 2px 0 0 var(--wep-color-accent-1);
}
/* Tab from the input focuses the first row, which is the selected one whenever
 * it can be selected. The rail and the global focus ring (tokens.css
 * :focus-visible) are both a box-shadow, and the rail's selector outranks the
 * ring's, so on its own the rail hid the ring and focus moved unseen. The
 * focused selected row paints both. The transparent outline is the #2053 High
 * Contrast fallback every ring carries (ForcedColorsFocusIndicatorTests). */
.wep-cmdk-row.is-active:focus-visible {
  outline: 2px solid transparent;
  box-shadow: inset 2px 0 0 var(--wep-color-accent-1), var(--wep-focus-ring);
}
.wep-cmdk-empty {
  padding: var(--wep-space-3) var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
}

/* ─── Inbox (Unit E.2 / design.md §22) ─── */
.wep-inbox {
  list-style: none;
  margin: var(--wep-space-3) 0;
  padding: 0;
}
.wep-inbox li {
  display: grid;
  grid-template-columns: 100px 1fr 200px;
  gap: var(--wep-space-3);
  align-items: center;
  padding: var(--wep-space-2) 0;
  border-bottom: 1px solid var(--wep-color-border-1);
  font-size: var(--wep-size-text-sm);
}

/* ─── Form controls — design.md §17 ─── */
.wep-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.wep-field-label {
  display: block;
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  font-weight: var(--wep-weight-medium);
}
/* #1865 N.3 — required-ness is a STEADY STATE, not an alarm. This marker
 * used to be painted in --wep-state-cveblocked-fg, the CVE-blocked colour,
 * so an untouched sign-in form showed red "REQUIRED" beside every label
 * before the operator had typed a character or made a mistake (#1854 part H:
 * never render a steady state as an alarm). It is now the muted hint it
 * always was, set in sentence case against the uppercase label so it reads
 * as an annotation rather than a second shouted word.
 *
 * The alarm colour stays exactly where an alarm belongs: .wep-field-error
 * and .wep-input[aria-invalid="true"] below are untouched, so a real
 * validation failure is still red — and now it is the ONLY red on the form.
 *
 * Accessible semantics are unchanged: the literal text node ("required")
 * still renders inside the <label>, and the control still carries the native
 * `required` attribute (WepInput/WepSelect/WepMultiSelect/WepDateRange emit
 * both). This rule only re-colours what is already announced.
 *
 * KEEP IN LOCKSTEP with WepInput.razor.css — the scoped copy out-specifies
 * this one (the #887 / LabelledFillContrastTests lesson), so a fix applied
 * to only one of the two renders as no fix at all. */
.wep-field-label .wep-field-required {
  color: var(--wep-color-fg-4);
  font-weight: var(--wep-weight-regular);
  letter-spacing: var(--wep-tracking-normal);
  text-transform: none;
  margin-left: 4px;
}
.wep-field-help {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-mono);
}
.wep-field-error {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-state-cveblocked-fg);
}
/* #1865 part M — a prose hint beside a form control. `.wep-field-help` above is
 * deliberately mono (design.md §17: "The `field-help` line below the input
 * always uses mono so it visually belongs to the same field"), which is right
 * for a value and wrong for a sentence. Both call sites (CatalogIngestModal's
 * version-picker fallback and its typed-confirm instruction) are sentences, so
 * this is the same size and colour without the mono. */
.wep-form-hint {
  margin-top: var(--wep-space-1);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-3);
}

/* #1906 — the Ingest-modal package-search candidate list. A bounded,
 * scrollable list of selectable candidates; each row shows the display name,
 * the mono canonical id, and (right-aligned) the latest known version. Picking
 * a row only prefills the submission — the search never queues. */
.wep-pkg-search {
  margin-top: var(--wep-space-2);
}

.wep-pkg-search-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
}

/* #1939: a candidate is ONE line, always. Long names/ids used to wrap inside the
   baseline-aligned flex row and the second line painted over the next row
   (Adobe Acrobat Reader 32/64-bit were illegible). Every text item now truncates
   with an ellipsis: the name keeps priority (flex-shrink 1), the id gives way
   first (flex-shrink 3), the version never shrinks. The full name + id ride the
   row's title attribute. min-width: 0 on the button and the spans is what lets a
   flex item shrink below its content width at all. */
.wep-pkg-search-candidate {
  display: flex;
  align-items: baseline;
  gap: var(--wep-space-2);
  width: 100%;
  min-width: 0;
  padding: var(--wep-space-1) var(--wep-space-2);
  background: none;
  border: none;
  border-bottom: 1px solid var(--wep-color-border-1);
  text-align: left;
  cursor: pointer;
  color: var(--wep-color-fg-1);
  font: inherit;
}

.wep-pkg-search-list li:last-child .wep-pkg-search-candidate {
  border-bottom: none;
}

/* #2053: no `outline: none` here. The background is gone under forced colours,
   so the global :focus-visible outline (tokens.css; the forced-colors block
   below) is the only focus cue left in High Contrast. */
.wep-pkg-search-candidate:hover,
.wep-pkg-search-candidate:focus-visible {
  background: var(--wep-color-bg-2);
}

.wep-pkg-search-name {
  font-size: var(--wep-size-text-sm);
  /* #1939 follow-up: shrink but never GROW — a growing name pushed the id to the
     right edge beside the version (v0.164.0 live probe); the id belongs next to
     the name, the version alone rides margin-left:auto to the right. */
  flex: 0 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wep-pkg-search-id {
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  flex: 0 3 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wep-pkg-search-latest {
  margin-left: auto;
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
}

.wep-input {
  height: 28px;
  padding: 0 var(--wep-space-3);
  font: inherit;
  font-size: var(--wep-size-text-sm);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-1);
  width: 320px;
  box-sizing: border-box;
}
.wep-input.wep-input-mono {
  font-family: var(--wep-font-mono);
}
.wep-input:hover { border-color: var(--wep-color-border-2); }
.wep-input:focus {
  outline: 2px solid transparent;
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
}
.wep-input[disabled] {
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-4);
  cursor: not-allowed;
}
.wep-input[aria-invalid="true"] {
  border-color: var(--wep-state-cveblocked-rule);
  box-shadow: 0 0 0 1px var(--wep-state-cveblocked-rule) inset;
}

.wep-textarea {
  min-height: 80px;
  padding: var(--wep-space-2) var(--wep-space-3);
  font: inherit;
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  resize: vertical;
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-1);
  width: 480px;
  box-sizing: border-box;
}
.wep-textarea:hover { border-color: var(--wep-color-border-2); }
.wep-textarea:focus {
  outline: 2px solid transparent;
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
}
.wep-textarea[disabled] {
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-4);
  cursor: not-allowed;
}
.wep-textarea[aria-invalid="true"] {
  border-color: var(--wep-state-cveblocked-rule);
  box-shadow: 0 0 0 1px var(--wep-state-cveblocked-rule) inset;
}

.wep-input-group {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-1);
  height: 28px;
  width: 320px;
  box-sizing: border-box;
}
.wep-input-group:focus-within {
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
  outline: 2px solid transparent;
}
.wep-input-group .wep-input {
  border: none;
  height: auto;
  flex: 1;
  width: auto;
  box-shadow: none;
  background: transparent;
}
.wep-input-group .wep-input:focus { box-shadow: none; }
.wep-input-group .wep-input-affix {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--wep-space-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  background: var(--wep-color-bg-2);
}
.wep-input-group .wep-input-affix-prefix { border-right: 1px solid var(--wep-color-border-1); }
.wep-input-group .wep-input-affix-suffix { border-left: 1px solid var(--wep-color-border-1); }

/* ─── Checkbox / radio — design.md §17 ─── */
.wep-check,
.wep-radio {
  appearance: none;
  width: 14px;
  height: 14px;
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-1);
  margin: 0;
  vertical-align: -2px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.wep-radio { border-radius: 999px; }
.wep-check:checked,
.wep-radio:checked {
  background: var(--wep-color-accent-1);
  border-color: var(--wep-color-accent-1);
}
.wep-check:checked::after {
  content: "";
  width: 4px;
  height: 8px;
  border-right: 2px solid var(--wep-color-accent-fg);
  border-bottom: 2px solid var(--wep-color-accent-fg);
  transform: rotate(45deg) translate(0, -1px);
}
.wep-radio:checked::after {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--wep-color-accent-fg);
}
.wep-check:focus-visible,
.wep-radio:focus-visible {
  box-shadow: var(--wep-focus-ring);
  outline: 2px solid transparent;
}
.wep-check[disabled],
.wep-radio[disabled] {
  opacity: .5;
  cursor: not-allowed;
}
.wep-check[aria-invalid="true"],
.wep-radio[aria-invalid="true"] {
  border-color: var(--wep-state-cveblocked-rule);
}

.wep-check-row {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  cursor: pointer;
}
.wep-check-row[data-disabled="true"] {
  cursor: not-allowed;
  color: var(--wep-color-fg-4);
}
.wep-check-row-error {
  color: var(--wep-state-cveblocked-fg);
}

/* ─── Toggle switch — design.md §17 ─── */
.wep-toggle {
  appearance: none;
  width: 28px;
  height: 16px;
  border: 1px solid var(--wep-color-border-2);
  border-radius: 999px;
  background: var(--wep-color-bg-2);
  position: relative;
  cursor: pointer;
  transition: background 120ms var(--wep-ease-standard);
  vertical-align: -2px;
  flex-shrink: 0;
}
.wep-toggle::after {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: var(--wep-color-bg-1);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.2);
  transition: left 120ms var(--wep-ease-standard);
}
.wep-toggle:checked {
  background: var(--wep-color-accent-1);
  border-color: var(--wep-color-accent-1);
}
.wep-toggle:checked::after { left: 13px; }
.wep-toggle:focus-visible {
  box-shadow: var(--wep-focus-ring);
  outline: 2px solid transparent;
}
.wep-toggle[disabled] {
  opacity: .5;
  cursor: not-allowed;
}
@media (prefers-reduced-motion: reduce) {
  .wep-toggle, .wep-toggle::after { transition: none; }
}

/* ─── Select — design.md §17 ─── */
.wep-select {
  height: 28px;
  padding: 0 28px 0 var(--wep-space-3);
  font: inherit;
  font-size: var(--wep-size-text-sm);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-1);
  width: 320px;
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--wep-color-fg-2) 50%),
    linear-gradient(135deg, var(--wep-color-fg-2) 50%, transparent 50%);
  background-position: calc(100% - 14px) 12px, calc(100% - 9px) 12px;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
  box-sizing: border-box;
}
.wep-select:hover { border-color: var(--wep-color-border-2); }
.wep-select:focus {
  outline: 2px solid transparent;
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
}
.wep-select[disabled] {
  background-color: var(--wep-color-bg-2);
  color: var(--wep-color-fg-4);
  cursor: not-allowed;
}
.wep-select[aria-invalid="true"] {
  border-color: var(--wep-state-cveblocked-rule);
  box-shadow: 0 0 0 1px var(--wep-state-cveblocked-rule) inset;
}

/* ─── Date range — design.md §17 ─── */
.wep-daterange-row {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-3);
  flex-wrap: wrap;
}
.wep-daterange-row .wep-input {
  width: 200px;
}
.wep-daterange-arrow {
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-mono);
  user-select: none;
}

/* ─── File picker — design.md §17 ─── */
.wep-filedrop {
  display: block;
  width: 480px;
  border: 1px dashed var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-4);
  text-align: center;
  background: var(--wep-color-bg-1);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
  cursor: pointer;
  position: relative;
  box-sizing: border-box;
}
.wep-filedrop:focus-within {
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
  outline: 2px solid transparent;
}
.wep-filedrop strong {
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-semi);
}
.wep-filedrop .wep-filedrop-meta {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  margin-top: var(--wep-space-1);
}
.wep-filedrop[data-state="accepted"] {
  border-style: solid;
  background: var(--wep-state-clean-bg);
  border-color: var(--wep-state-clean-rule);
  color: var(--wep-state-clean-fg);
}
.wep-filedrop[data-state="rejected"] {
  border-style: solid;
  background: var(--wep-state-cveblocked-bg);
  border-color: var(--wep-state-cveblocked-rule);
  color: var(--wep-state-cveblocked-fg);
}
.wep-filedrop[data-state="rejected"] .wep-filedrop-meta {
  color: var(--wep-state-cveblocked-fg);
}
.wep-filedrop input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

/* ─── Multi-select chip input — design.md §17 ─── */
.wep-multi {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wep-space-1);
  min-height: 28px;
  padding: 2px var(--wep-space-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-1);
  width: 480px;
  box-sizing: border-box;
}
.wep-multi:focus-within {
  border-color: var(--wep-color-border-focus);
  box-shadow: var(--wep-focus-ring);
  outline: 2px solid transparent;
}
.wep-multi[aria-invalid="true"] {
  border-color: var(--wep-state-cveblocked-rule);
  box-shadow: 0 0 0 1px var(--wep-state-cveblocked-rule) inset;
}
.wep-multi[data-disabled="true"] {
  background: var(--wep-color-bg-2);
  cursor: not-allowed;
}
.wep-multi .wep-chip {
  height: 20px;
}
.wep-multi-input {
  border: none;
  outline: none;
  background: transparent;
  flex: 1;
  min-width: 80px;
  padding: 0 4px;
  font: inherit;
  font-size: var(--wep-size-text-xs);
  font-family: var(--wep-font-mono);
  color: var(--wep-color-fg-1);
}

/* ─── Catalog package cell: avatar initials + name/id (prototype parity) ─── */
.wep-pkg-cell { display: flex; align-items: center; gap: var(--wep-space-3); }
.wep-pkg-text { min-width: 0; }

/* ─── A WepDataTable group header row (Plan 2026-07-02-005 U5): the row that
   brackets a group's rows. The catalog's own group-head classes
   (.wep-catalog-group-*) left with the per-version list (#4213 U12): the app
   list is one row per app, and an app row is a table row of cells. ─── */
.wep-table .wep-table-group-header > td {
  background: var(--wep-color-bg-2);
  border-top: 1px solid var(--wep-color-border-1);
  padding-top: var(--wep-space-2);
  padding-bottom: var(--wep-space-2);
}
/* ─── Catalog update monitor (Plan 2026-06-08-002): the "Update → X" badge beside
   a version. The page-head filter is a WepFilterChip link since #4213 U12, so
   the toggle's own class is gone. ─── */
.wep-update-badge {
  display: inline-flex;
  align-items: center;
  margin-left: var(--wep-space-2);
  padding: 1px 6px;
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-accent-bg);
  color: var(--wep-color-accent-text);
  border: 1px solid var(--wep-color-accent-1);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  cursor: pointer;
  white-space: nowrap;
}
/* #1764 — same labelled-fill class as .wep-btn-primary: this hover paints text
 * on an accent fill, so it takes the solid role, not accent-1. */
.wep-update-badge:hover { background: var(--wep-color-accent-solid); color: var(--wep-color-accent-fg); }

/* ─── Upstream-drift badge (Plan 2026-06-25-001 U4, #1100 R2): the vendor URL's
   live bytes no longer hash to the attested SHA-256 for this version (installer
   hash mismatch — the endpoint can never converge until it's re-ingested).
   Shares the .wep-update-badge SHAPE but a DANGER ramp (--wep-state-cveblocked-*)
   to match the InstallerHashMismatch Error tone; non-interactive (the one-click
   re-ingest deep-link is deferred), so no pointer/hover affordance. ─── */
.wep-drift-badge {
  display: inline-flex;
  align-items: center;
  margin-left: var(--wep-space-2);
  padding: 1px 6px;
  border-radius: var(--wep-radius-xs);
  background: var(--wep-state-cveblocked-bg);
  color: var(--wep-state-cveblocked-fg);
  border: 1px solid var(--wep-state-cveblocked-rule);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  white-space: nowrap;
  cursor: default;
}
/* ─── Version "is it safe?" diff (Plan 2026-06-08-002 U4). ─── */
.wep-version-diff-summary { display: flex; gap: var(--wep-space-4); margin-bottom: var(--wep-space-3); font-weight: var(--wep-weight-semi); }
.wep-diff-cleared { color: var(--wep-state-clean-fg); }
/* #1759 completeness — "N added" is TEXT, so it takes the danger text role.
   --wep-color-danger is the non-text danger colour and fails AA as a foreground. */
.wep-diff-added { color: var(--wep-color-danger-text); }
.wep-version-diff-changes { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--wep-space-3); font-size: var(--wep-size-text-xs); color: var(--wep-color-fg-2); }

.wep-pkg-initials {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  color: var(--wep-color-fg-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: 0;
}

/* ─── Catalog ring mini-cells: letterless colored squares (prototype parity).
   Global (CatalogIndex.razor.css scopes its own copy to /catalog, which only
   redirects — /catalog/winget needs these to apply). ─── */
.wep-ring-mini {
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-right: 2px;
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-3);
  border: 1px solid var(--wep-color-border-1);
  color: transparent;   /* hide the C/P/B/A letter; the title attr keeps hover info */
  font-size: 0;
  vertical-align: -1px;
}
.wep-ring-mini-on      { background: var(--wep-state-promoted-rule);   border-color: var(--wep-state-promoted-rule); }
.wep-ring-mini-armed   { background: var(--wep-state-detonating-rule); border-color: var(--wep-state-detonating-rule); }
.wep-ring-mini-blocked { background: var(--wep-state-cveblocked-rule); border-color: var(--wep-state-cveblocked-rule); }

/* ─── WepIcon host (global) ────────────────────────────────────────────
 * WepIcon sizes its <span class="wep-icon-host"> to the requested px
 * (14/16/20) via inline style, but the vendored lucide <svg> carries its
 * own intrinsic width="24" height="24" and was overflowing the host —
 * every icon rendered at 24px and sat ~4px below its adjacent label
 * (no base rule constrained the svg). Make the host a centering box and
 * force the svg to fill it, so the icon is exactly the requested size and
 * vertically centered everywhere (nav rail, table check/lock, etc.). */
.wep-icon-host {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  flex: none;
}
.wep-icon-host > svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ─── Nav-rail icon sizing (grouped nav + lucide icons, prototype parity) ─── */
/* The icon takes its link's colour: mist at rest, white under the pointer and
   on the current page (#4654). */
.wep-nav-link .wep-icon-host { flex: none; color: inherit; }
.wep-nav-label { flex: 1; }

/* ─── Catalog list pills (#4213 U12). The Status-column rule that stood here was keyed on the
   catalog page's FOURTH cell (min-width 25ch, nowrap), which is the Newest column of the app list, or
   Versions on a manager without the selection column. It went with the version table it described: the
   app list's cells wrap between their pieces (CatalogAppList.razor.css), so a verbose pill makes a row
   taller, not the table wider. ─── */
/* A pill in the catalog's tables is never truncated: a verdict cut to an ellipsis is a verdict the
   operator cannot read, and the cells it sits in wrap, so it costs a line and no width. The
   triple-class selector outspecifies the scoped .wep-pill[b-*] cap (30ch). */
.wep-catalog-page .wep-table .wep-pill {
  max-width: none;
}
/* The quick-facts drawer's status row keeps its pills whole the same way: the verbose "ATTESTED
   REAL-SANDBOX" label was clipping against the cap. */
.wep-catalog-facts .wep-catalog-facts-status .wep-pill {
  max-width: none;
}

/* ─── Detail-page footprint stat-cards (prototype parity) ─── */
.wep-stat-row { display: flex; gap: var(--wep-space-3); flex-wrap: wrap; }
.wep-stat {
  flex: 1 1 140px;
  min-width: 140px;
  padding: var(--wep-space-4);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
}
.wep-stat-num {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xl);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  line-height: 1.1;
}
.wep-stat-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  margin-top: var(--wep-space-1);
}

/* ─── Endpoint identity fact strip (detail-page metadata bar) ─── */
/* Used by FleetEndpointDetail.razor. Each .wep-fact stacks an uppercase
   muted label above its value; facts flow horizontally and wrap inside a
   subtle bordered bar. Without these rules the label/value spans collide
   ("Hostnamedesktop-9urpa3h"). */
.wep-fact-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wep-space-4) var(--wep-space-6);
  padding: var(--wep-space-4) var(--wep-space-5);
  margin: var(--wep-space-3) 0 var(--wep-space-5);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  /* #1725 primitives audit — the border alone barely registers against
     bg-0 in dark mode; shadow-sm (already defined, previously unused
     here) lifts the identity strip off the canvas. */
  box-shadow: var(--wep-shadow-sm);
}
.wep-fact {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-1);
  min-width: 0;
}
.wep-fact-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  font-weight: var(--wep-weight-medium);
}
.wep-fact-value {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  line-height: 1.3;
}

/* ─── Endpoint detail panels (#1725 primitives audit) ─── */
/* .wep-ring-reassign and .wep-primary-user were structural class hooks on
   FleetEndpointDetail.razor's <section> elements with NO rule anywhere in
   this file — everything inside rendered on bare canvas. .wep-groups-panel
   is a new wrapper (Group memberships had no container at all). All three
   share one panel treatment: a surface lifted off bg-0 with an accent-1
   rule marking it as a distinct operator-action block, not prose.
   flex+gap replaces the per-paragraph inline margins the .razor file used
   to space children — do not add margin back onto panel children. */
.wep-ring-reassign,
.wep-primary-user,
.wep-groups-panel {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-3);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-left: 3px solid var(--wep-color-accent-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-4) var(--wep-space-5);
  box-shadow: var(--wep-shadow-sm);
}
.wep-ring-reassign > .wep-section-title,
.wep-primary-user > .wep-section-title,
.wep-groups-panel > .wep-section-title {
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  text-transform: none;
  letter-spacing: normal;
  margin: 0;
}
/* The nested identity strip inside .wep-primary-user (Mode/Derived/Pinned)
   reuses .wep-fact-strip's grid but sits one layer down — sunken bg-2
   instead of its own bordered/shadowed surface, so it doesn't compete
   with the panel that contains it. */
.wep-primary-user > .wep-fact-strip {
  background: var(--wep-color-bg-2);
  border-color: var(--wep-color-border-1);
  box-shadow: none;
  margin: 0;
}

/* Nested inside .wep-ring-reassign. Same panel shape, danger's own rule
   color instead of accent-1 — severity by form, the same border-left +
   state-bg idiom .wep-banner already uses (§428, .wep-banner-warn) rather
   than a new background mechanism. */
.wep-device-security-danger {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-3);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-left: 3px solid var(--wep-color-danger);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-4) var(--wep-space-5);
}

/* The SYSTEM-hollow held row (Brave.Brave etc. on FleetEndpointDetail).
   requiresusercontext-rule ties it to the same amber the "requires user
   context" fact-strip pill and count note use elsewhere on this page. */
.wep-system-hollow-row {
  background: var(--wep-color-bg-2);
  border-left: 2px solid var(--wep-state-requiresusercontext-rule);
  border-radius: var(--wep-radius-xs);
  padding: var(--wep-space-2) var(--wep-space-3);
  margin-top: var(--wep-space-2);
}

/* ─── Theme toggle (dev only) ─── */
.wep-theme-toggle,
.wep-density-toggle {
  position: fixed;
  bottom: var(--wep-space-3);
  z-index: var(--wep-z-toast);
  display: inline-block;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  line-height: 1.4;
  padding: 6px var(--wep-space-3);
  border: 1px solid var(--wep-color-border-2);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-1);
  text-decoration: none;
  border-radius: var(--wep-radius-xs);
  cursor: pointer;
}
.wep-theme-toggle   { right: var(--wep-space-3); }
.wep-density-toggle { right: calc(var(--wep-space-3) + 140px); }
@media (prefers-reduced-motion: reduce) {
  .wep-theme-toggle,
  .wep-density-toggle {
    transition: none;
  }
}

/* Context-bar settings menu — holds the theme + density toggles (which used to
   float over every page). Native <details> disclosure; right-aligned popover. */
.wep-settings-menu { position: relative; flex: none; margin-left: var(--wep-space-3); }
/* The gear sits on the navy bar: a white glyph on no ground of its own, as the
   U0 board draws it, raised to navy 800 under the pointer (#4654). The border
   stays, transparent, so the 28px box does not move. */
.wep-settings-menu > summary {
  list-style: none;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  cursor: pointer;
  font-size: 14px;
  border: 1px solid transparent;
  border-radius: var(--wep-radius-xs);
  background: transparent;
  color: var(--wep-shell-fg);
}
.wep-settings-menu > summary::-webkit-details-marker { display: none; }
.wep-settings-menu > summary::marker { content: ""; }
.wep-settings-menu > summary:hover { background: var(--wep-shell-raised); color: var(--wep-shell-fg); }
/* A page surface hanging off the navy bar: it sets its own text colour, so
   nothing inside it inherits the bar's white onto bg-1. */
.wep-settings-popover {
  position: absolute; right: 0; top: calc(100% + 6px);
  z-index: var(--wep-z-toast);
  display: flex; flex-direction: column; gap: var(--wep-space-2);
  min-width: 180px; padding: var(--wep-space-3);
  color: var(--wep-color-fg-1);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.wep-settings-row { display: flex; }
/* The toggle links default to position:fixed (their old floating home); inside
   the settings popover render them as normal full-width menu rows. */
.wep-settings-popover .wep-theme-toggle,
.wep-settings-popover .wep-density-toggle {
  position: static; inset: auto; z-index: auto;
  display: block; width: 100%; text-align: left;
}
/* Settings-menu navigation — the Admin/Settings nav is stripped in CE, so
   /community/settings/{storage,network} surface here; on SaaS/OnPrem the
   "Tenant settings" section links the bare /admin/* tenant pages the /admin
   index refuses a tenant operator (#2856). A hairline-separated, labelled
   section below the theme/density preference rows so navigation reads
   distinct from the display preferences. */
.wep-settings-section {
  display: flex; flex-direction: column; gap: var(--wep-space-2);
  margin-top: var(--wep-space-1); padding-top: var(--wep-space-2);
  border-top: 1px solid var(--wep-color-border-1);
}
.wep-settings-section-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}
.wep-settings-popover .wep-settings-nav-link {
  display: block; width: 100%; text-align: left;
  color: var(--wep-color-fg-2); text-decoration: none;
}
.wep-settings-popover .wep-settings-nav-link:hover { color: var(--wep-color-fg-1); }

/* ----------------------------------------------------------------
   Plan 2026-05-13-001 catalog page header parity.

   U2 — Status counter strip rendered as a peer of <PageHead>.
        Lifted verbatim from docs/design/prototypes/catalog-index.html
        lines 504-524. Dot color modifiers map the seven Plan A
        PackageStatus values to existing --wep-state-*-rule tokens;
        attested borrows the clean ramp because the prototype was
        authored pre-7-state-lifecycle.

   U3 — Keyboard-hints chip lifted from prototype lines 526-541.
        .wep-btn-key is NOT lifted here — it already exists earlier
        in this file.
   ---------------------------------------------------------------- */
.cat-counts {
  display: flex;
  gap: var(--wep-space-6);
  margin-top: var(--wep-space-3);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-mono);
  flex-wrap: wrap;
}
.cat-counts > div {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-2);
}
.cat-counts .num {
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-semi);
}
.cat-counts .dot {
  width: 6px; height: 6px; border-radius: 999px; display: inline-block;
}
.cat-counts .dot--cve-blocked         { background: var(--wep-state-cveblocked-rule); }
.cat-counts .dot--quarantined         { background: var(--wep-state-quarantined-rule); }
.cat-counts .dot--rolled-back         { background: var(--wep-state-rolledback-rule); }
.cat-counts .dot--detonating          { background: var(--wep-state-detonating-rule); }
.cat-counts .dot--queued-for-detonate { background: var(--wep-state-sbomready-rule); }
.cat-counts .dot--observed-unattested { background: var(--wep-color-fg-3); }
.cat-counts .dot--attested            { background: var(--wep-state-clean-rule); }

.cat-keyhint {
  margin-left: auto;
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
}
.cat-keyhint kbd {
  display: inline-block;
  padding: 1px 5px;
  border: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  color: var(--wep-color-fg-2);
  margin: 0 2px;
}

/* ─── Auth surfaces (login / setup / community-agents) ───
 * Moved here from the scoped BlazorAuthLayout.razor.css: under
 * RazorComponentResult<T> SSR the rendered scope attribute (b-…) did not
 * match the scoped-CSS bundle's, so the centering/card-width never applied
 * and the login card pinned top-left. Global selectors sidestep the
 * scope-id entirely. The .wep-card base (below) still provides bg/border/
 * padding/radius via tokens.
 */
.wep-auth-body {
  margin: 0;
  min-height: 100vh;
  background: var(--wep-color-bg-0);
  color: var(--wep-color-fg-1);
}
.wep-auth-main {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--wep-space-6);
}
.wep-auth-card { width: 100%; max-width: 420px; }
.wep-auth-card-wide { max-width: 720px; }
/* Content links inside the auth surface (agent-enrollment installer
 * download, inline help links) had no colour rule and fell back to the
 * UA default dark-blue — unreadable on the dark card. Accent them.
 * :not(.wep-btn) leaves button-styled anchors (wep-btn wep-btn-default)
 * to their own treatment.
 * #1750: moved off accent-1 onto the link token for the same reason .wep-link
 * did — accent-1 as *text* is under 4.5:1 on every light backdrop. This rule
 * is .wep-link's own ancestor (it was promoted verbatim), so the two must not
 * drift apart. */
.wep-auth-card a:not(.wep-btn) {
  color: var(--wep-color-link);
  text-underline-offset: 2px;
}
.wep-auth-card a:not(.wep-btn):hover { color: var(--wep-color-link-hover); }

/* ─── Card surface (general) ───
 * Per Plan 2026-05-15-002 Phase 2 U1: the .wep-card class was referenced
 * by BlazorAuthLayout (Phase 1 U1) and various surfaces but had no CSS
 * rules — auth surfaces and the row drawer rendered frameless. This
 * rule gives every card the border + bg + padding the design system
 * already implied via its naming.
 */
.wep-card {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-5);
}

/* ─── Home page (root surface) ───
 * Per Plan 2026-05-15-002 Phase 2 U1 / F3 + F13.
 * The home page (/) is the operator's first-paint surface after sign-in.
 * The Razor template emits a card-grid composition; these rules realize
 * the visual treatment that was already encoded in the markup but never
 * styled.
 */
.wep-home-section {
  margin: var(--wep-space-6) 0;
}
.wep-home-section-label {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  margin-bottom: var(--wep-space-3);
}
.wep-home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--wep-space-3);
}
.wep-home-card {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-4);
  color: var(--wep-color-fg-1);
  text-decoration: none;
  transition: border-color 80ms ease, background 80ms ease;
}
.wep-home-card:hover {
  border-color: var(--wep-color-border-2);
  background: var(--wep-color-bg-2);
}
.wep-home-card:focus-visible {
  outline: 2px solid var(--wep-color-border-focus);
  outline-offset: 1px;
}
.wep-home-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wep-space-2);
}
.wep-home-card-name {
  font-size: var(--wep-size-text-md);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  letter-spacing: var(--wep-tracking-tight);
}
.wep-home-card-hotkey {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-2);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  color: var(--wep-color-fg-3);
  letter-spacing: var(--wep-tracking-wide);
}
.wep-home-card-route {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-home-card-desc {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
  line-height: 1.4;
}
.wep-home-foot {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-8);
  padding-top: var(--wep-space-3);
  border-top: 1px solid var(--wep-color-border-1);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-home-kbd {
  display: inline-block;
  padding: 1px 5px;
  border: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-3xs);
  color: var(--wep-color-fg-2);
}

/* ─── Workspace-landing surface index ───
 * Per Plan 2026-05-15-002 Phase 2 U1 / F2 + F4 + F13.
 * /fleet, /policy, and any future workspace landing emit a
 * <section class="wep-section-list">
 *   <h2 class="wep-section-title">Surfaces</h2>
 *   <ul class="wep-section-links">
 *     <li><a class="wep-section-link"><strong>Name</strong>
 *         <span class="wep-section-link-meta">desc</span></a></li>
 *   </ul>
 * </section>
 * pattern. Rules below match the home-card visual language so /fleet
 * and / read as the same family.
 */
.wep-section-list {
  margin: var(--wep-space-4) 0;
}
.wep-section-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--wep-space-3);
}
.wep-section-link {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-1);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  padding: var(--wep-space-3) var(--wep-space-4);
  color: var(--wep-color-fg-1);
  text-decoration: none;
  transition: border-color 80ms ease, background 80ms ease;
}
.wep-section-link:hover {
  border-color: var(--wep-color-border-2);
  background: var(--wep-color-bg-2);
}
.wep-section-link:focus-visible {
  outline: 2px solid var(--wep-color-border-focus);
  outline-offset: 1px;
}
.wep-section-link > strong {
  font-size: var(--wep-size-text-md);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  letter-spacing: var(--wep-tracking-tight);
}
.wep-section-link-meta {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
  line-height: 1.4;
}

/* ─── Empty state primitive ───
 * Per Plan 2026-05-15-002 Phase 2 U1 / F6 + F12 + F13.
 * Used by /fleet/cve-matrix ("No CVE matches…"), /inbox ("No events
 * match these filters."), /audit/evidence/{id} ("Evidence id … is not
 * in this tenant's audit ledger."). Subtle, not loud: a centered
 * dashed-border block with secondary text. The tan demo-data banner
 * pattern stays for "infrastructure-not-yet-bootstrapped" zero-states
 * (e.g., /fleet/agents); .wep-empty is for "no matches for current
 * filters" or "not found in this tenant" semantics.
 */
.wep-empty {
  margin: var(--wep-space-6) 0;
  padding: var(--wep-space-6) var(--wep-space-4);
  border: 1px dashed var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-3);
  font-size: var(--wep-size-text-sm);
  text-align: center;
}

/* ─── Honest-unavailable / empty-state panel ───
 * Plan 2026-07-01-002 U2 (R2): card treatment for the "action unavailable /
 * nothing to show" panels — FleetCveResponse's disabled-actions notice and
 * RollbackProposalQueue's clean / flag-off states. Same dashed, centered
 * card as .wep-empty; .wep-empty-title / .wep-empty-lead give the heading and
 * body restrained typography so a disabled-control explanation reads as a
 * calm, deliberate state rather than a browser-default h2 + p error dump.
 * (.wep-empty-title is a sentence heading, NOT the uppercase .wep-section-title
 * label idiom.)
 */
.wep-empty-state {
  margin: var(--wep-space-6) 0;
  padding: var(--wep-space-6) var(--wep-space-4);
  border: 1px dashed var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-1);
  color: var(--wep-color-fg-3);
  text-align: center;
}
.wep-empty-title {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-md);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  color: var(--wep-color-fg-1);
}
.wep-empty-lead {
  max-width: 60ch;
  margin: 0 auto;
  font-size: var(--wep-size-text-sm);
  line-height: 1.5;
  color: var(--wep-color-fg-3);
}

/* ─── Numbered steps list ───
 * Promoted from the CommunityBrokers.razor.css / EmptyTenant.razor.css scoped
 * copies (KTD5): the enable-steps <ol> idiom — decimal markers, kit li rhythm.
 * Pages keep their own layout glue (margins, centering) scoped.
 */
.wep-steps {
  list-style: decimal;
  padding-left: var(--wep-space-6);
}
.wep-steps li {
  padding: var(--wep-space-2) 0;
}

/* ─── Screen-reader-only ───
 * Plan 2026-05-15-003 Phase 2 U2: visually hidden but available to
 * assistive tech. Used by WepDataTable for the ineligible-reason text
 * that aria-describedby references on disabled row checkboxes. Pattern
 * is the standard "sr-only" utility — clip-path keeps the element in
 * the accessibility tree without taking visual space.
 */
.wep-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ─── Aria-disabled checkbox (table row eligibility) ───
 * Plan 2026-05-15-003 Phase 2 U2: row checkboxes that are aria-disabled
 * (NOT native `disabled` — we keep them in tab order so screen readers
 * can announce the aria-describedby reason) need a visual cue so sighted
 * operators see the ineligible state. The native browser-default disabled
 * styling does NOT fire on aria-disabled, so we provide it here.
 */
.wep-table-checkbox input[type="checkbox"][aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ─── Catalog drawer sections ───
 * Plan 2026-05-15-003 Phase 2 U3: visible grouping inside the drawer
 * body. Each section gets a top border so operators see the structural
 * break between Evidence / Recent activity / Actions. Section headings
 * reuse the existing .wep-section-title primitive.
 *
 * The Actions section additionally:
 *   - sits sticky-bottom on narrow viewports (full-sheet drawer mode)
 *     so promote/clear-soak/rollback stay reachable without scrolling
 *     the whole drawer body
 *   - has its own visual weight via slightly heavier top border
 */
/* ─── Detonation evidence sections ───
 * Plan 2026-06-05-002 U3/U5: the 5 footprint sections (Installation
 * footprint / Network / SBOM / Behavior / Probes) render the captured
 * install footprint. Each section is a labelled block; the high-cardinality
 * lists (files, registry) collapse into <details> with a scrollable body so
 * a 2000-file install can't dominate the drawer. */
.wep-catalog-evidence-section {
  padding-top: var(--wep-space-3);
  margin-top: var(--wep-space-3);
  border-top: 1px solid var(--wep-color-border-1);
}
.wep-catalog-evidence-section > h4 {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  font-weight: 600;
  color: var(--wep-color-fg-2);
}

.wep-evidence-list {
  margin-bottom: var(--wep-space-2);
}
.wep-evidence-list > summary {
  cursor: pointer;
  user-select: none;
  color: var(--wep-color-fg-1);
}
.wep-evidence-list > summary:hover {
  color: var(--wep-color-accent-text);
}
.wep-evidence-items {
  list-style: none;
  margin: var(--wep-space-2) 0 0;
  padding: var(--wep-space-2);
  max-height: 16rem;
  overflow-y: auto;
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  font-size: var(--wep-size-text-xs);
  line-height: 1.6;
  word-break: break-all;
}
.wep-evidence-items > li {
  padding: 1px 0;
}

/* #778: msiexec /l*v installer-log tail on a failed MSI detonation. Scrollable,
   monospace, height-capped so a verbose log doesn't dominate the page. */
.wep-installer-log {
  margin: var(--wep-space-2) 0 0;
  padding: var(--wep-space-2);
  max-height: 24rem;
  overflow: auto;
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  font-size: var(--wep-size-text-xs);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Probe checklist — one row per baseline probe, pass/fail coloured mark. */
.wep-evidence-probes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-1);
}
.wep-evidence-probes > li {
  display: flex;
  align-items: baseline;
  gap: var(--wep-space-2);
  font-size: var(--wep-size-text-xs);
}
.wep-probe-mark {
  font-weight: 700;
  width: 1em;
  flex: none;
}
/* No dedicated success token in the palette — reuse the "promoted" teal for
 * pass (reads as good on both themes); danger-bg for fail. */
.wep-probe-pass .wep-probe-mark { color: var(--wep-state-promoted-rule, #1c7886); }
.wep-probe-fail .wep-probe-mark { color: var(--wep-color-danger-bg, #b22338); }
/* #1786 — an upload (POST/push, not a git clone/fetch) to an allowlisted apex is the
 * exfil-shaped egress. Annotate the destination row as a caution. --wep-color-danger
 * is the FILL token (fails AA as text); the #1759 role split gives -text for copy. */
.wep-evidence-upload {
  color: var(--wep-color-danger-text);
  font-weight: var(--wep-weight-medium, 600);
}
.wep-probe-id { flex: none; }
.wep-probe-gate {
  flex: none;
  font-size: var(--wep-size-text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0 var(--wep-space-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  color: var(--wep-color-fg-2);
}
.wep-probe-detail { word-break: break-word; }
.wep-evidence-probe-summary { margin-top: var(--wep-space-2); }

/* ─── Docking: the proof sits BESIDE the claim ──────────────────────────
 * Push the page body aside so nothing hides behind a docked drawer —
 * "docks BESIDE the table" rather than over it. Driven entirely by CSS:
 * a drawer's <aside> only renders while the drawer is open and is a
 * descendant of .wep-main, so :has() flips the reserved gutter on/off
 * with no component state at all. Below the 1920 rung the drawer overlays
 * the content and no gutter is wanted.
 *
 * ONE DRAWER (#4213 U13, 2026-10-14). This rule shape was written for the
 * 432px catalog drawer, which docked at 1200, and plan 2026-09-06-2142 U2
 * generalised it to the evidence drawer. The catalog list's quick facts
 * now render in the evidence drawer, so the 432px panel, its 1200 dock and
 * its full-sheet fallback below 1200 are gone, and this is the one docking
 * rule:
 *
 *   .wep-drawer          520px, docks at the ladder's 1920 rung and steps
 *                        to 640px at 2560. Docking it lower would leave a
 *                        content column narrower than the dense tables its
 *                        callers render beside it (436px at 1200).
 *
 * ONLY THE NON-MODAL COMPOSITION DOCKS. WepEvidenceDrawer renders a
 * `.wep-drawer-backdrop` sibling when ShowBackdrop is on (the modal
 * callers — CVE row click, settings history). A modal dims the whole
 * workspace, so reserving a gutter beside it would reserve space nobody
 * can reach. Keying on the backdrop's ABSENCE extends the seam the
 * component already documents ("surfaces that compose the drawer
 * alongside the workspace") instead of inventing a second one, and needs
 * no markup change to work.
 * ─────────────────────────────────────────────────────────────────────── */
/* The gutter slides open and closed. Unqueried: below the 1920 rung nothing
 * docks, so the padding never changes and there is nothing to animate. */
.wep-main {
  transition: padding-right var(--wep-motion-base) var(--wep-ease-standard);
}

@media (min-width: 1920px) {
  .wep-main:has(.wep-drawer):not(:has(.wep-drawer-backdrop)) {
    padding-right: calc(520px + var(--wep-space-5));
  }
  /* #2086 — the page header's full-bleed escape is sized to the BASE
   * gutter, and the rule above has just replaced it with a panel
   * reservation. Left un-reset the header would reach 24px past the
   * reservation and slide under the docked panel's left edge. The
   * workspace's right edge IS the panel gutter while a panel is docked, so
   * on that side there is nothing left to escape. Both of the escape's
   * placements are reset (#2613): a header wrapped in its page root docks
   * beside the same panel. It is the ONLY reset needed: the 2560 block
   * below widens the same docked panel
   * through the same selector, and a min-width:1920 rule is still in force
   * at 2560. A second copy there would be dead weight. */
  .wep-main:has(.wep-drawer):not(:has(.wep-drawer-backdrop)) > .wep-page-head,
  .wep-main:has(.wep-drawer):not(:has(.wep-drawer-backdrop)) > * > .wep-page-head:first-child {
    margin-right: 0;
  }
}

@media (min-width: 2560px) {
  /* Surplus width buys evidence, not padding: the panel widens rather
   * than the page gaining margin. 2560 − 220px rail − 640px panel still
   * leaves the content column wider than the whole authored rung. */
  .wep-drawer { width: 640px; }
  .wep-main:has(.wep-drawer):not(:has(.wep-drawer-backdrop)) {
    padding-right: calc(640px + var(--wep-space-5));
  }
}

/* The gutter animates open and closed, and until now it did so no matter
 * what the operator had asked for. Each of this file's other
 * `prefers-reduced-motion` blocks names ONE element family — the
 * typed-confirm shake, the toggle, the theme/density buttons, the button
 * spinner — and none of them names `.wep-main`, and there is no global
 * `* { transition-duration: 1ms }` reset here (the single-file prototypes
 * carry one; this sheet does not). So the gutter's transition had
 * always ignored the preference. It does not now: an operator who asks
 * for no motion gets the gutter instantly, at every rung. Declared after
 * the rungs so it wins on source order at equal specificity. */
@media (prefers-reduced-motion: reduce) {
  .wep-main { transition: none; }
}

/* ─── Version quick facts (#4213 U13): the catalog list's drawer body ───────
 * The list's quick facts render INSIDE the shared evidence drawer (.wep-drawer),
 * whose body already pads and scrolls. These two blocks are the verdict header
 * and the status row the 432px catalog drawer drew with its own gutters:
 * the same content, ruled off from the sections below, with no horizontal
 * padding of their own. */
.wep-catalog-facts-verdict {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
  padding-bottom: var(--wep-space-3);
  border-bottom: 1px solid var(--wep-color-border-1);
}
.wep-catalog-facts-status {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wep-space-2);
  padding: var(--wep-space-3) 0;
  margin-bottom: var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
}

/* U5: the behavioral-delta headline (counts vs the prior attested-clean version,
 * or the first-attestation fallback). Leads the verdict header. */
.wep-catalog-drawer-delta {
  margin: 0;
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-2);
}

/* Summary-only fallback field list (rendered until the deep-read Detail
 * resolves). Definition grid: uppercase label over mono value.
 * grid-template-columns: minmax(0, 1fr) caps the column to the panel
 * width — without it, the implicit `auto` column grows to the widest
 * cell's max-content (digests, CVE titles) and the whole field set
 * overflowed the drawer (clipped by the panel's overflow:hidden). */
.wep-catalog-drawer-fields {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--wep-space-3);
}
.wep-catalog-drawer-fields > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  min-width: 0;
}
.wep-catalog-drawer-fields dd { overflow-wrap: anywhere; }
.wep-catalog-drawer-fields dt {
  font-size: var(--wep-size-text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--wep-tracking-wide);
  color: var(--wep-color-fg-3);
}
.wep-catalog-drawer-fields dd {
  margin: 0;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
}

/* CVE list inside the evidence field set — shared by the docked drawer
 * and the full detail page. Previously an unstyled <ul>, so the long
 * titles overflowed (and clipped against) the narrow drawer. Flat list:
 * mono/num meta inline, title wraps, hairline separators between rows.
 * overflow-wrap:anywhere is the belt-and-suspenders against any long
 * unbroken token (digest, ZDI ref) blowing out the panel width. */
.wep-catalog-evidence-cves {
  margin: var(--wep-space-1) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
}
.wep-catalog-evidence-cves > li {
  font-size: var(--wep-size-text-xs);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-2);
  overflow-wrap: anywhere;
  padding-bottom: var(--wep-space-2);
  border-bottom: 1px solid var(--wep-color-border-1);
}
.wep-catalog-evidence-cves > li:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}
.wep-catalog-evidence-cves > li > span { margin-right: var(--wep-space-2); }
.wep-catalog-evidence-cves > li > span:last-child { margin-right: 0; }
.wep-catalog-evidence-cves > li > .wep-num { text-align: left; }

.wep-catalog-drawer-section {
  padding-top: var(--wep-space-4);
  border-top: 1px solid var(--wep-color-border-1);
  margin-top: var(--wep-space-3);
}
.wep-catalog-drawer-section:first-of-type {
  /* First section in the body doesn't need a top rule — the drawer-
   * status row above already separates the head from the body. */
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}
.wep-catalog-drawer-section > .wep-section-title {
  margin-bottom: var(--wep-space-2);
}

/* ─── Per-agent installer list (Plan 2026-05-17-002 U7) ────────────────
 * Container that holds N PerAgentInstallerBuilder rows on /community/agents.
 * A vertical-scroll cap keeps a large fleet (10s+) from pushing the
 * legacy enrollment-token mint form below the fold. No pagination yet;
 * the homelab-first profile rarely sees more than a handful of rows.
 * ───────────────────────────────────────────────────────────────────── */
.wep-agent-installer-list {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-2);
  max-height: 24rem;
  overflow-y: auto;
}

.wep-agent-installer-empty {
  /* Inline empty-state copy when no agents are enrolled yet. */
  margin: var(--wep-space-2) 0;
  font-style: italic;
}

/* ─── Bootstrap download button (Plan 2026-05-17-002 U8) ───────────────
 * Sibling-to-<pre><code> file-save control on /community/agents. The
 * button itself is the standard WepButton Default variant (per design.md
 * §5.11 — no ad-hoc button styles); this rule only owns the row layout
 * + the small error span beside it.
 * ───────────────────────────────────────────────────────────────────── */
.wep-bootstrap-download {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-2);
}

/* #1768 + #1759 — two defects in three lines. The size token --wep-text-sm has
   never existed (the scale is --wep-size-text-*), so the fallback rendered
   0.875rem = 14px where 13px was meant. And --wep-color-danger is the non-text
   danger colour, which fails AA as text on every light surface (4.38:1 at best) — the
   #1759 role split applies here too; this is error copy. */
.wep-bootstrap-download-error {
  color: var(--wep-color-danger-text);
  font-size: var(--wep-size-text-sm);
}

/* ─── Inventory roll-up source chips ───────────────────────────────────
 * Manager + source tokens inside the rolled-up /inventory row's Sources
 * cell. One rolled row collapses N raw observation tuples (arp +
 * winget-com across endpoints); the chips show the constituent channels
 * so the operator can see WHY the row used to render 2–4 times. Mono
 * 2xs on a bg-2 tile — same quiet-metadata idiom as .wep-fleet-mcard-tag,
 * boxed like .wep-update-badge but without its accent (chips are
 * informational, not interactive).
 * ───────────────────────────────────────────────────────────────────── */
.wep-inv-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  margin: 1px var(--wep-space-1) 1px 0;
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  color: var(--wep-color-fg-3);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: 0;
  white-space: nowrap;
}

/* "+N" overflow chip — the Sources cell caps visible chips at 2; the full
 * manager+source list rides in the title attribute, so the dashed border +
 * help cursor signal "hover for the rest" without widening the column. */
.wep-inv-chip-more {
  border-style: dashed;
  color: var(--wep-color-fg-2);
  cursor: help;
}

/* Plan 2026-06-29-002 U8 (R4/R5) — "N of M endpoints held" chip. Warn-amber
 * (the cross-cutting warn tokens, same aging idiom as .wep-stale-flag) so a
 * rolled row with some endpoints holding a last-known snapshot reads distinct
 * from the neutral manager/source chips. help cursor: the full explanation
 * rides the title attribute. */
.wep-inv-chip-held {
  color: var(--wep-color-warn-fg);
  background: var(--wep-color-warn-bg);
  border-color: var(--wep-color-warn-fg);
  cursor: help;
}

/* ─────────────────────────────────────────────────────────────────────────
 * Generic collapse-disclosure primitive (#1230). Generalizes the no-JS
 * <details> idiom already used ad-hoc by .wep-settings-menu / .wep-evidence-list
 * / .wep-demand-panel into a reusable primitive for SECONDARY, action-on-demand
 * content — e.g. the DivergenceFindingCard triage ceremonies (acknowledge /
 * author-expected-forward / revoke), which are collapsed by default so the card
 * leads with its verdict instead of a wall of stacked forms. Collapsed body is
 * still in the DOM (bUnit + progressive-enhancement friendly); only visually
 * hidden. Tokens only. */
.wep-disclosure {
  margin-top: var(--wep-space-3);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-1);
}

/* The clickable summary row, laid out as a flex row with a rotating caret;
   default marker suppressed, pointer + fg-1-on-hover like the sibling patterns.
   A fold's summary is its title (#4561 U3): a sentence, set as a subtitle
   (`.wep-section-subtitle`'s size, weight and ink), where it was the
   section title's old uppercase 2xs eyebrow. */
.wep-disclosure-summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: var(--wep-space-3) var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-2);
}
.wep-disclosure-summary::-webkit-details-marker { display: none; }
.wep-disclosure-summary::marker { content: ""; }
.wep-disclosure-summary:hover { color: var(--wep-color-fg-1); }
.wep-disclosure-summary::before {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent currentColor;
  transition: transform var(--wep-motion-fast) var(--wep-ease-standard);
}
.wep-disclosure[open] > .wep-disclosure-summary::before { transform: rotate(90deg); }
/* A trailing quiet hint after the label (e.g. "typed-confirm required"). */
.wep-disclosure-summary-hint {
  flex: none;
  text-transform: none;
  letter-spacing: normal;
  font-weight: var(--wep-weight-regular);
  color: var(--wep-color-fg-3);
}
.wep-disclosure-summary-label { flex: 1 1 auto; }
/* The revealed body: padded, hairline-separated from the summary. */
.wep-disclosure-body {
  padding: 0 var(--wep-space-4) var(--wep-space-4);
  border-top: 1px solid var(--wep-color-border-1);
  padding-top: var(--wep-space-3);
}

/* ───────────────────────────────────────────────────────────────────────
 * Blocked-install demand panel (Plan 2026-06-08-003 U10 + 2026-06-21-004 U6)
 * fix/blocked-demand-noise-cap-ux — the panel below the catalog Winget table
 * that shows what FENCED endpoints tried to install/search but the feed
 * refused. It is SECONDARY catalog-curation context, so it renders as a
 * collapsed-by-default native <details> disclosure (same no-JS idiom as
 * .wep-settings-menu / .wep-evidence-list). When expanded, the resolved /
 * unresolved tables reuse the .wep-table chrome (via WepDataTable) and the
 * intent indicator is a design-system pill — visual consistency with the
 * catalog grid, no parallel style language. Tokens only.
 * ───────────────────────────────────────────────────────────────────── */
.wep-demand-panel {
  margin-top: var(--wep-space-4);
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
}

/* The clickable disclosure row. Mirrors the .wep-section-title label idiom
   (uppercase 2xs tracking-wide fg-3) but laid out as a flex row carrying the
   count chip; pointer + fg-1-on-hover follow the .wep-evidence-list summary
   precedent, and the default marker is suppressed like .wep-settings-menu. */
.wep-demand-summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  padding: var(--wep-space-3) var(--wep-space-4);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-3);
}
.wep-demand-summary::-webkit-details-marker { display: none; }
.wep-demand-summary::marker { content: ""; }
.wep-demand-summary:hover { color: var(--wep-color-fg-1); }
/* Disclosure caret — a quiet triangle that rotates when the panel opens, so
   the row reads as expandable without a JS toggle or an icon font. */
.wep-demand-summary::before {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent currentColor;
  transition: transform var(--wep-motion-fast) var(--wep-ease-standard);
}
.wep-demand-panel[open] > .wep-demand-summary::before { transform: rotate(90deg); }
.wep-demand-summary-label { flex: 1 1 auto; }

/* The "(N)" total — a quiet count chip in the same boxed-metadata idiom as
   .wep-inv-chip (bg-2 tile, mono 2xs, hairline border). */
.wep-demand-summary-count {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-width: 1.5rem;
  justify-content: center;
  padding: 0 var(--wep-space-2);
  border-radius: var(--wep-radius-pill);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  color: var(--wep-color-fg-2);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: 0;
}

.wep-demand-body {
  padding: 0 var(--wep-space-4) var(--wep-space-4);
}
/* Muted one-line description under the header — fg-3 body, same role as the
   quiet metadata copy elsewhere. */
.wep-demand-panel-sub {
  margin: 0 0 var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-3);
}

/* Resolved / unresolved groups — spacing + a .wep-section-title-style
   subheading so the two groups read as distinct sections. */
.wep-demand-group + .wep-demand-group { margin-top: var(--wep-space-4); }
.wep-demand-group-title {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-2);
}
.wep-demand-group-hint {
  font-weight: var(--wep-weight-regular);
  text-transform: none;
  letter-spacing: var(--wep-tracking-normal);
  color: var(--wep-color-fg-3);
}
/* Right-align the Ingest action cell + collapse its column to the button. */
.wep-demand-action-col {
  text-align: right;
  width: 1%;
  white-space: nowrap;
}

/* Per-row intent indicator (R12) — design-system pills built on the same
   recipe as .wep-pill. install-attempt is the actionable demand, so it gets
   the present sbomready-blue ramp; search-only is informational, so it gets
   the muted neutral (fg-3 on bg-2, matching .wep-pill-offline). */
.wep-demand-intent {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  border-radius: var(--wep-radius-xs);
  border: 1px solid transparent;
  white-space: nowrap;
}
.wep-demand-intent + .wep-demand-intent { margin-left: var(--wep-space-1); }
.wep-demand-intent-install {
  color: var(--wep-state-sbomready-fg);
  background: var(--wep-state-sbomready-bg);
}
.wep-demand-intent-search {
  color: var(--wep-color-fg-3);
  background: var(--wep-color-bg-2);
}
/* #1553 downgrade-attempt: a version pinned BELOW what the feed currently
   serves — a security signal, so it takes the cveblocked red ramp (the
   established "refused for safety" color) and renders before install/search. */
.wep-demand-intent-downgrade {
  color: var(--wep-state-cveblocked-fg);
  background: var(--wep-state-cveblocked-bg);
}

/* #1553 pinned-version annotation beside the term/id: informational, muted,
   NOT a pill — "pinned 22.01". Untrusted text is @-encoded by Blazor. */
.wep-demand-pinned {
  margin-left: var(--wep-space-2);
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-2xs);
  color: var(--wep-color-fg-3);
  white-space: nowrap;
}

/* ─── Density: comfortable — fixed row rhythm the tokens cannot reach ───
 * Plan 2026-07-28-001 U4 (OQ4, R6/AE4), deltas amplified by U4b (operator
 * live-test: §26's literal +2px/+4px were invisible side-by-side).
 * Companion to the density block in tokens.css: there, the row-rhythm step
 * --wep-space-2 moves 8px -> 12px, so every token-padded row (.wep-table,
 * .wep-diff) gains +4px vertical padding = +8px row height with no rule
 * here changing. The fleet-table row rhythm is a FIXED height/padding (not
 * a spacing token), so the same deltas are re-stated here explicitly: +8px
 * row height, +3px vertical header padding (the header is chrome, not scan
 * surface — it loosens by less). U4b also loosens the canonical table's
 * LEADING to the relaxed token — leading is the strongest side-by-side
 * lever; the fleet cells are fixed-height/nowrap/centered, so height (not
 * leading) is their lever. Additive-only: each selector re-states a dense
 * rule above under the host attribute (App.razor writes data-density on
 * <html>) and touches the vertical axis ONLY — horizontal padding, widths
 * and gaps are identical in both modes. The 28px control chrome (.wep-btn,
 * .wep-input, .wep-select, the :where() baselines, chips, cmdk)
 * deliberately stays dense: §26's subject is the scan rhythm of rows, not
 * control size, and the select chevron geometry is tuned to the 28px box. */
[data-density="comfortable"] .wep-fleet-table thead th {
  padding-top: 14px;
  padding-bottom: 14px;
}
[data-density="comfortable"] .wep-fleet-table tbody td {
  height: 60px;
}
[data-density="comfortable"] .wep-table tbody td {
  line-height: var(--wep-line-relaxed);
}


/* ════════════════════════════════════════════════════════════════════════
 * #1865 part N — the sign-in surface. APPENDED AS ONE BLOCK on purpose:
 * part M is adding shared-vocabulary rules next to their existing siblings
 * in the middle of this file, so the two edits land in different regions.
 *
 * Every selector below is NEW — none of them had a rule anywhere before, in
 * this sheet or in any scoped one. The `wep-auth-*` family has been in the
 * markup since Plan 2026-05-15-002 (LoginForm, SetupForm, TwoFactorForm,
 * ChangePasswordForm) and never had a single declaration: the page held
 * together only because .wep-card, .wep-field and .wep-btn happened to do
 * enough work that nobody looked. A new selector cannot regress anything
 * that renders today — the only elements that change are the ones that were
 * already asking for a treatment and getting none.
 *
 * The composition rules (.wep-auth-body / -main / -card / -card-wide) and
 * the card's link colour already live upstairs, next to .wep-card; this
 * block is the CONTENT vocabulary that sits inside them.
 * ═══════════════════════════════════════════════════════════════════════ */

/* ─── Product identity on the card ───
 * The first screen anyone sees had no wordmark, no product name, nothing
 * saying what this is — a bare card on an empty field. Rendered once by
 * BlazorAuthLayout so every auth surface — login, setup, 2FA, forced
 * password change, agent enrollment — carries it identically.
 *
 * #4654 U4: the brand's stacked lockup, white on a navy panel across the
 * top of the card, as the approved "In context" board draws the sign-in:
 * the L tree mark at 132px, 16px above the outlined wordmark at 36px, in a
 * 32px pad. The panel is the shell's navy in both themes, like the bar and
 * the rail. It bleeds to the card's edges by cancelling the card's own
 * space-5 padding, and rounds its top corners to the card's inner radius.
 *
 * The mark carries flex:none so the wordmark can never squeeze it (#1859:
 * a rule that can clip pairs its clipping with an explicit flex:none), and
 * explicit width/height because an inline <svg> with neither renders at the
 * 300x150 replaced-element default and eats the card. */
.wep-auth-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wep-space-4);
  margin: calc(-1 * var(--wep-space-5)) calc(-1 * var(--wep-space-5)) var(--wep-space-5);
  padding: var(--wep-space-8);
  border-radius: calc(var(--wep-radius-sm) - 1px) calc(var(--wep-radius-sm) - 1px) 0 0;
  background: var(--wep-shell-bg);
  color: var(--wep-shell-fg);
}
.wep-auth-brand .wep-brand-mark {
  flex: none;
  width: 132px;
  height: 132px;
}
.wep-auth-brand .wep-brand-wordmark {
  font-size: 36px;
}

/* ─── Card heading + lede ───
 * The <h1> was rendering at the UA default (2em bold with 0.67em margins) —
 * the one piece of type on the whole surface that had never met the type
 * ramp. --wep-size-text-xl is the console's page-title step, which is what
 * this is. */
.wep-auth-title {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-xl);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
  line-height: var(--wep-line-tight);
  color: var(--wep-color-fg-1);
}
.wep-auth-lede {
  margin: 0 0 var(--wep-space-5);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-relaxed);
  color: var(--wep-color-fg-3);
}

/* ─── Form body ───
 * .wep-field already spaces a label against its own control (4px); nothing
 * spaced one FIELD against the next, so the fields ran together and every
 * label sat hard against the input above it. */
.wep-auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--wep-space-4);
  /* Collapses against whatever precedes the form — the title's 8px on
   * /community/login (no lede), the lede's 20px on setup / 2FA / change-
   * password, an error or notice's 16px when one is showing. The larger
   * margin always wins, so each surface keeps its own rhythm without an
   * adjacency selector per combination. */
  margin-top: var(--wep-space-4);
}

/* The kit input is a fixed 320px because it lives in dense console forms
 * next to siblings. Inside the 420px auth card that leaves a ragged right
 * edge under a full-width heading. Scoped to .wep-auth-form so no other
 * surface's field widths move. */
.wep-auth-form .wep-input,
.wep-auth-form .wep-textarea,
.wep-auth-form .wep-input-group {
  width: 100%;
}

/* #1868 — the Remember-me opt-in row + its plain-HTTP warning (D2: show +
 * warn). The warning borrows the warn ramp, NOT the error ramp: it is a
 * posture note beside an available choice, not a failure. Mirrored to
 * wwwroot/css/_shell.css at build. */
.wep-auth-remember {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  margin: var(--wep-space-2) 0 0;
  font-size: var(--wep-size-text-sm);
  cursor: pointer;
}
.wep-auth-remember-warning {
  margin: var(--wep-space-2) 0 0;
  padding: var(--wep-space-2) var(--wep-space-3);
  border-left: 3px solid var(--wep-state-detonating-rule);
  border-radius: 0 var(--wep-radius-xs) var(--wep-radius-xs) 0;
  background: var(--wep-color-warn-bg);
  color: var(--wep-color-warn-fg);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
}

.wep-auth-actions {
  display: flex;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-1);
}
.wep-auth-actions .wep-btn {
  flex: 1;
  justify-content: center;
}

/* ─── Messages ───
 * .wep-auth-error is the ONE place on this surface entitled to the alarm
 * ramp: it renders only when the POST actually failed. It borrows the
 * .wep-banner idiom (state rule + state bg + state fg) rather than inventing
 * a second error look.
 *
 * .wep-auth-notice is role="status", not role="alert" — "admin-created" and
 * friends are neutral information, so it gets neutral chrome. Deliberately
 * NOT a success ramp: the notice does not know whether anything succeeded,
 * and claiming so would be a fabricated value (KTD7). */
.wep-auth-error {
  margin: 0 0 var(--wep-space-4);
  padding: var(--wep-space-2) var(--wep-space-3);
  border-left: 3px solid var(--wep-state-cveblocked-rule);
  border-radius: 0 var(--wep-radius-xs) var(--wep-radius-xs) 0;
  background: var(--wep-state-cveblocked-bg);
  color: var(--wep-state-cveblocked-fg);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
}
.wep-auth-notice {
  margin: 0 0 var(--wep-space-4);
  padding: var(--wep-space-2) var(--wep-space-3);
  border-left: 3px solid var(--wep-color-border-2);
  border-radius: 0 var(--wep-radius-xs) var(--wep-radius-xs) 0;
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-2);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
}

/* ─── Secondary prose ───
 * .wep-auth-help is the explanatory line inside the recovery-code
 * disclosure. .wep-auth-muted is the page-level explainer that survived the
 * U2 banner sweep as PROSE on two settings pages (audit-partitions stakes,
 * storage re-point notice) — it is deliberately quiet chrome there for the
 * same reason it is quiet here: a permanent explainer rendered as an alert
 * is one operators learn to ignore. */
.wep-auth-help {
  margin: var(--wep-space-2) 0 var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-relaxed);
  color: var(--wep-color-fg-3);
}
.wep-auth-muted {
  margin: 0 0 var(--wep-space-4);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-relaxed);
  color: var(--wep-color-fg-3);
}

/* ─── Recovery-code disclosure (2FA) ───
 * <hr> was rendering as the UA's inset 3D groove — the one bevelled edge in
 * a design language with no bevels. */
.wep-auth-divider {
  margin: var(--wep-space-5) 0;
  border: 0;
  border-top: 1px solid var(--wep-color-border-1);
}
.wep-auth-recovery > summary {
  cursor: pointer;
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-medium);
  color: var(--wep-color-fg-2);
}
.wep-auth-recovery > summary:hover {
  color: var(--wep-color-fg-1);
}
.wep-auth-recovery[open] > summary {
  margin-bottom: var(--wep-space-3);
}

/* ─── Forced TOTP enrolment (Plan 2026-10-04-1018 U8, R5) ───
 * The authenticator key as a QR code on a white tile (a QR needs its quiet
 * zone light in both themes), and the recovery codes shown once, two to a row
 * in mono so they can be copied exactly. */
.wep-auth-qr {
  display: block;
  margin: 0 auto var(--wep-space-4);
  padding: var(--wep-space-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  background: #ffffff;
}
.wep-auth-codes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--wep-space-1) var(--wep-space-4);
  margin: 0 0 var(--wep-space-4);
  padding: var(--wep-space-3);
  list-style: none;
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-2);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
}

/* ════════════════════════════════════════════════════════════════════════
 * #1865 follow-up — form + modal idioms PROMOTED OUT OF A PAGE SCOPE.
 *
 * These six lived in `CatalogPackage.razor.css` as `.wep-pkg-page ::deep …`
 * rules. That resolved them for the cards THAT page hosts, and for nothing
 * else — and it turned out most of their callers are hosted somewhere else
 * entirely, where the markup asked for a class no rule answered:
 *
 *   .wep-modal-preconfirm       CatalogTypedConfirmModal, hosted by at least
 *                               six surfaces (bulk-adopt, bulk-attest,
 *                               derived-serve-flip, manual-attest, rollback
 *                               confirm, fleet group detail). The pre-confirm
 *                               line of the typed-confirm ceremony rendered
 *                               unstyled on every one of them.
 *   .wep-checkbox-row           PromoteReadBackChecklist is hosted by
 *   .wep-fieldset (+ legend)    CatalogBulkPromoteRingModal — NOT the package
 *                               page — so the promote read-back rendered
 *                               unstyled in its only real host.
 *   .wep-catalog-evidence-      CatalogAgentNativeSnippet reaches the evidence
 *     snippet (+ -copy)         DRAWER as well as the page.
 *   .wep-fieldlist (+ children) DivergenceFindingCard / BaselineGateWaiverCard
 *                               are page-only today, so no live defect — but
 *                               they were one re-host away from one.
 *
 * Declarations are VERBATIM from the page copy, so rendering inside
 * `.wep-pkg-page` is unchanged; everywhere else it is fixed. The page keeps its
 * one genuinely page-specific override (`.wep-pkg-evidence ::deep
 * .wep-catalog-drawer-fields`), which widens the drawer's grid on that page
 * only. ---------------------------------------------------------------- */

/* Label/value stacks inside evidence cards. */
.wep-fieldlist {
  margin: var(--wep-space-2) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--wep-space-3);
}

.wep-fieldlist > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  min-width: 0;
}

.wep-fieldlist dt {
  font-size: var(--wep-size-text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--wep-tracking-wide);
  color: var(--wep-color-fg-3);
}

.wep-fieldlist dd,
.wep-fieldlist li {
  margin: 0;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-1);
  overflow-wrap: anywhere;
}

/* A checkbox and its label on one baseline. */
.wep-checkbox-row {
  display: flex;
  align-items: baseline;
  gap: var(--wep-space-2);
  padding: var(--wep-space-1) 0;
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
}

/* A grouped set of those rows. */
.wep-fieldset {
  margin: var(--wep-space-3) 0 0;
  padding: var(--wep-space-3);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
}

.wep-fieldset > legend {
  padding: 0 var(--wep-space-2);
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
}

/* The sentence a typed-confirm modal shows above its phrase input. */
.wep-modal-preconfirm {
  margin: var(--wep-space-3) 0;
}

/* The agent-native snippet: a mono block with its copy button on the same row. */
.wep-catalog-evidence-snippet {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  flex-wrap: wrap;
}

.wep-catalog-evidence-snippet-copy {
  flex: none;
}
/* ════════════════════════════════════════════════════════════════════════
 * #1865 follow-up (2) — classes whose ONLY rule lived in an unrelated
 * component's scoped sheet, WITHOUT ::deep.
 *
 * Blazor scoped CSS compiles `.foo { }` in Parent.razor.css to
 * `.foo[b-parent]`, and only elements authored in Parent.razor carry
 * `b-parent`. So a plain scoped rule styles that ONE component and nothing
 * else — while the class census, which unions every stylesheet, counts it as
 * defined and stays silent. Each of these was in that state:
 *
 *   .wep-loading-placeholder  five surfaces render it (audit search, CVE
 *                             exposure, holds, inventory, rollback proposals)
 *                             and its only rule sat in CatalogIndex.razor.css
 *                             — a page whose entire body is a redirect to
 *                             /catalog/winget, so the rule styled nothing at
 *                             all while five real pages showed an unstyled
 *                             "Loading…".
 *   .wep-form-row (+ > label) four components (clear-soak modal, ring-soak
 *                             config, endpoint detail, fleet groups) against a
 *                             rule scoped to FleetGroupDetail.
 *   .wep-button-row           audit search, against a rule scoped to
 *                             CatalogActivity.
 *   .wep-migration-*          the audit partition-health panel, against rules
 *                             scoped to InstallerByteMigrationPanel.
 *
 * Declarations are verbatim from those sheets, so the original component
 * renders identically and the other callers stop rendering bare.
 *
 * NOTE the loading placeholder is deliberately NOT folded into `.wep-empty`
 * above: that is the dashed empty-state BOX, and a transient "Loading…" line
 * should not draw a box that is about to be replaced. ------------------- */

.wep-loading-placeholder {
  padding: var(--wep-space-6);
  color: var(--wep-color-fg-3);
  font-size: var(--wep-size-text-sm);
}

/* Label + control + button on one line, wrapping on narrow widths. */
.wep-form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wep-space-3);
  margin: var(--wep-space-3) 0 var(--wep-space-6);
}

.wep-form-row > label {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
}

/* An action / pagination row. */
.wep-button-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-3);
}

/* Migration panel action row + its inline confirm. */
.wep-migration-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wep-space-2);
  margin: var(--wep-space-3) 0;
}

.wep-migration-confirm {
  margin: var(--wep-space-3) 0;
  padding: var(--wep-space-3);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-xs);
  background: var(--wep-color-bg-2);
}

.wep-migration-confirm p {
  margin: 0 0 var(--wep-space-2);
  color: var(--wep-color-fg-2);
}

.wep-migration-confirm-counts {
  font-size: var(--wep-size-text-sm);
}

.wep-migration-confirm-actions {
  display: flex;
  gap: var(--wep-space-2);
  margin-top: var(--wep-space-2);
}
/* The button loading affordance. `wep-btn-*` is kit BUTTON vocabulary, but these
   three lived in WepButton.razor.css as plain scoped rules — so they styled the
   spinner WepButton renders and nothing else. InventoryTable hand-rolls the same
   markup inside a raw <button> for its Manage action, where the span therefore
   had no size, no border and no animation: an invisible spinner behind a live
   aria-busy. Moved here whole, keyframes and reduced-motion override included,
   so the animation cannot come adrift from the rule that references it. */
.wep-btn-loading-label {
  opacity: .55;
}

.wep-btn-spinner {
  width: 12px;
  height: 12px;
  border-radius: var(--wep-radius-pill);
  border: 1.5px solid currentColor;
  border-right-color: transparent;
  animation: wep-btn-spin 800ms linear infinite;
  flex-shrink: 0;
}

@keyframes wep-btn-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .wep-btn-spinner {
    animation: none;
    border-right-color: currentColor;
  }
}
/* ═══ Shell chrome mirrors — plan 2026-09-06-2143 U4 ═════════════════════
 * Every piece of layout chrome an artboard needs to draw the frame was
 * already mirrored here from its scoped sheet — .wep-shell, .wep-nav,
 * .wep-ctxbar, .wep-settings-menu — EXCEPT three: the "?" keyboard-map
 * overlay (KeyboardMapOverlay.razor.css), the community-edition footer
 * (CommunityFooter.razor.css) and the pending g-chord hint
 * (MainLayout.razor.css). With no rule here, no artboard could draw any of
 * them in the live class vocabulary, so none did, and the shell canvas had
 * a hole exactly where the operator's keyboard help and the CE status bar
 * sit. Values are the scoped sheets' own, verbatim; both copies move
 * together (#887). The scoped sheet still wins in the app on specificity,
 * so nothing that renders today changes.
 *
 * .wep-page-progress is the fourth mirror, from PageHead.razor.css: the 1px
 * header progress rule that design.md §3.5 and §10 name as the ONLY loading
 * affordance had a rule only in the component's scoped sheet, so the kit
 * sheet was hand-rolling a `.progress-rule` lookalike and the loading
 * archetype could not be drawn in the class the console emits.
 * ═════════════════════════════════════════════════════════════════════════ */

/* PageHead's progress rule is absolutely positioned against the header. The
 * scoped copy declares position: relative on .wep-page-head; this sheet's
 * own .wep-page-head rule (above) did not, so the rule is added here rather
 * than by editing a rule other artboards already render against. */
.wep-page-head { position: relative; }
.wep-page-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--wep-color-bg-2);
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
}
.wep-page-head[data-loading="true"] .wep-page-progress { visibility: visible; }
.wep-page-head[data-loading="true"] .wep-page-progress::after {
  content: "";
  position: absolute;
  left: -40%;
  top: 0;
  bottom: 0;
  width: 40%;
  background: var(--wep-color-accent-1);
  animation: wep-page-progress-slide 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes wep-page-progress-slide {
  0%   { left: -40%; }
  100% { left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .wep-page-head[data-loading="true"] .wep-page-progress::after {
    animation: none;
    left: 0;
    width: 100%;
  }
}

/* Community-edition footer — the shell grid's `foot` status-bar row. Flex +
 * gap at the container because Blazor strips whitespace-only text nodes and
 * the four segments used to render butted together. The interpunct is a
 * ::before on every segment after the first so a screen reader hears fields,
 * not punctuation. */
.wep-community-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--wep-space-1) var(--wep-space-3);
  padding: var(--wep-space-2) var(--wep-space-6);
  border-top: 1px solid var(--wep-color-border-1);
  background: var(--wep-color-bg-1);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
.wep-community-footer > span + span::before {
  content: "·";
  margin-right: var(--wep-space-3);
  color: var(--wep-color-fg-4);
}
.wep-community-footer-label { color: var(--wep-color-fg-2); }

/* Keyboard-map overlay — the "?" dialog. */
.wep-overlay-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: var(--wep-z-modal);
}
.wep-overlay-card {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-pop);
  width: 480px;
  max-height: 80vh;
  overflow: auto;
}
.wep-overlay-head {
  display: flex;
  align-items: center;
  padding: var(--wep-space-3) var(--wep-space-4);
  border-bottom: 1px solid var(--wep-color-border-1);
}
.wep-overlay-head h2 {
  flex: 1;
  margin: 0;
  font-size: var(--wep-size-text-lg);
  font-weight: var(--wep-weight-semi);
  letter-spacing: var(--wep-tracking-tight);
}
.wep-overlay-close {
  background: transparent;
  border: none;
  color: var(--wep-color-fg-3);
  font-size: var(--wep-size-text-lg);
  cursor: pointer;
  padding: 0 var(--wep-space-2);
}
.wep-overlay-close:hover { color: var(--wep-color-fg-1); }
.wep-overlay-body { padding: var(--wep-space-3) var(--wep-space-4) var(--wep-space-4); }
.wep-overlay-section {
  font-size: var(--wep-size-text-2xs);
  letter-spacing: var(--wep-tracking-wide);
  text-transform: uppercase;
  color: var(--wep-color-fg-3);
  margin: var(--wep-space-3) 0 var(--wep-space-2);
}
.wep-overlay-section:first-child { margin-top: 0; }
.wep-overlay-row {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: var(--wep-space-3);
  padding: 4px 0;
  align-items: baseline;
}
.wep-overlay-combo {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-1);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  padding: 1px 6px;
  text-align: center;
  justify-self: start;
}
.wep-overlay-desc {
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
}

/* Pending g-chord hint — a persistent polite live region that paints a box
 * only while a chord prefix is pending. Not a toast (§10): no motion, no
 * auto-dismiss. */
.wep-chord-hint {
  position: fixed;
  left: var(--wep-space-4);
  bottom: var(--wep-space-4);
  z-index: var(--wep-z-modal);
  display: flex;
  flex-wrap: wrap;
  gap: var(--wep-space-2) var(--wep-space-3);
  align-items: baseline;
}
.wep-chord-hint.is-pending {
  background: var(--wep-color-bg-1);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  box-shadow: var(--wep-shadow-pop);
  padding: var(--wep-space-2) var(--wep-space-3);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
  max-width: 480px;
}
.wep-chord-hint kbd {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-1);
  background: var(--wep-color-bg-2);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  padding: 1px 6px;
}
.wep-chord-hint-prefix { color: var(--wep-color-fg-3); }
.wep-chord-hint-opt { color: var(--wep-color-fg-2); white-space: nowrap; }

/* ═══ Bounded-record fold row — plan 2026-09-06-2143 U6 (R8, R9, KTD5) ════
 * A record set that grows with fleet age is rendered as a WINDOW with a true
 * COUNT(*) and a route to the rest, and a run of identical records inside
 * the window folds to ONE row that names the repetition:
 *
 *   ×6,295  Microsoft.Edge · 139.0.3405 → 139.0.3405 · 2026-06-01 → 2026-09-06
 *
 * The fold is evidence, not elision — 6,295 reinstalls of the same version
 * is a treadmill an operator can act on, and 6,295 separate rows bury it.
 * The count is the non-colour marker (it is text, so it survives High
 * Contrast); the bg-2 ground borrows the table group-header idiom so the row
 * reads as a summary of the rows it stands for rather than as one of them.
 * No new primitive: a fold is a <tr> in a .wep-table. */
.wep-table tbody tr.wep-row-fold > td {
  background: var(--wep-color-bg-2);
  border-top: 1px solid var(--wep-color-border-2);
  border-bottom: 1px solid var(--wep-color-border-2);
}
.wep-fold-count {
  font-family: var(--wep-font-mono);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────────────
 * #2053 — the risk vocabulary survives Windows High Contrast.
 *
 * Every state above is separated by HUE alone: the base is
 * `border: 1px solid transparent` and each state sets a fg/bg token pair,
 * under the no-pill-color-aliasing convention that says each must "read
 * distinctly". Under `forced-colors: active` the browser strips author
 * background and colour, so all of them collapse into one another — Safe to
 * deploy, Blocked · CVE, Quarantined and Held become the same pill on a
 * product whose thesis is proving things to people who get sued if they are
 * wrong.
 *
 * The design system already conceded this once, for exactly one state:
 * .wep-pill-servedunverified carries a dashed border "because it must read at
 * a glance, color-vision-independent". This generalises that concession.
 *
 * BORDER STYLE is the channel, because forced-colors overrides colour but not
 * style or width. Five families, grouped by what an operator must DO rather
 * than by which token ramp a state happens to use — under High Contrast the
 * ramps are indistinguishable anyway, so grouping by ramp would preserve a
 * distinction nobody can see and lose the one that matters:
 *
 *   double 3px   BLOCKED      — do not proceed; something is refusing
 *   dashed       ATTENTION    — proceeded but wants an operator
 *   dotted       UNVERIFIED   — the claim exists, the proof does not
 *   solid        GOOD         — verified, attested, done
 *   solid+Gray   INERT        — transient or nothing to act on
 *
 * Where two states of one family share a column, WIDTH separates them. The
 * endpoint status column pairs `match` solid 1px, `recognized` solid 2px,
 * `not driven` dotted 1px, `provenance pending` dotted 2px, `possible`
 * dotted 3px, `ahead of attestation` dashed 1px, `drift` dashed 2px,
 * `foreign` dashed 3px and `held` double 3px. ForcedColorsStateCensusTests
 * fails when two pills in one status column share a style-and-width pair.
 *
 * `forced-color-adjust: none` is deliberately NOT used. It would keep the
 * author palette and defeat the operator's own accessibility setting — the
 * point is to work WITH the system colours, not to opt out of them.
 *
 * Everything here is inside the media query, so normal rendering is
 * byte-identical. Mirrored in the other copy (#887 — both move together),
 * except the :focus-visible rule. It is global by design, and a scoped copy
 * would reach only the pill's own <a>. The palette's selected-row rule is not
 * a pill rule either: its other copy is WepCommandPalette.razor.css (#2965).
 * ───────────────────────────────────────────────────────────────────────── */
@media (forced-colors: active) {
    /* Focus survives too. Every focus ring here is a box-shadow
       (--wep-focus-ring), and forced colours sets box-shadow to none, so the
       ring vanishes and so does the only sign of where the keyboard is.
       Highlight is the system's own focus colour. The rules that paint the
       ring also carry `outline: 2px solid transparent`, which forced colours
       paints in a system colour, so a scoped rule that outranks this one still
       leaves an outline. Pinned by ForcedColorsFocusIndicatorTests. */
    :focus-visible {
        outline: 2px solid Highlight;
        outline-offset: 2px;
    }

    /* So does the command palette's selected row (#2965). Focus stays in the
       palette's input, so the row Enter will run carries no focus ring: its
       mark is the hover fill plus an inset accent rail, and forced colours
       strip both. Highlight is also the system's colour for a selected item.
       The negative offset keeps the outline inside the row, since the palette
       clips at its edges. Mirrored in WepCommandPalette.razor.css, not in the
       pill's sheet; CommandPaletteRowTreatmentDesignTests pins the pair. */
    .wep-cmdk-row.is-active {
        outline: 2px solid Highlight;
        outline-offset: -2px;
    }

    /* The transparent base border vanishes when backgrounds are stripped;
       without this every pill renders as bare text with no shape. */
    .wep-pill {
        border-style: solid;
        border-width: 1px;
    }

    /* BLOCKED — deliberately the loudest. A 3px double border is chunky at the
       12px dense size, and that is the point: this family is the one an
       operator must not misread. `double` collapses to `solid` below 3px, so
       the width is load-bearing, not decoration. */
    .wep-pill-cveblocked,
    .wep-pill-quarantined,
    .wep-pill-held,
    .wep-pill-withdrawnupstream,
    .wep-pill-brokerreenrollmentrequired,
    .wep-pill-brokerdecommissioned,
    .wep-pill-sub.wep-origin-waived-expired {
        border-style: double;
        border-width: 3px;
    }

    /* ATTENTION — it moved, and it wants someone. */
    .wep-pill-degraded,
    .wep-pill-drift,
    .wep-pill-rolledback,
    .wep-pill-requiresinteraction,
    .wep-pill-selfrejecting,
    .wep-pill-uninstalllooping,
    .wep-pill-exceedsdetonationbudget,
    .wep-pill-exceedsobservabilitybudget,
    .wep-pill-networkposturemitm,
    .wep-pill-brokerrevoked,
    .wep-pill-notcheckingin,
    .wep-pill-criticalcve,
    .wep-pill-installermismatch,
    .wep-pill-installernotaccepted,
    .wep-pill-sub.wep-origin-waived {
        border-style: dashed;
        border-width: 2px;
    }

    /* The same family at its two other widths (#2829). `foreign` and `ahead of
       attestation` both render in the endpoint status column beside `drift`
       (dashed 2px above), and `ahead of attestation` is drawn BESIDE the row's
       own pill, so it can sit next to `drift` in one row. `foreign` (present
       on the machine, never declared) came here from BLOCKED, where it shared
       `held`'s double 3px: the next step is to adopt it or remove it, which
       wants an operator rather than refuses. It keeps 3px, so the row does not
       grow. `ahead of attestation` is its own class rather than a narrower
       `degraded`, which dozens of other surfaces use. Its dismissed form
       stays on `not driven`'s dotted 1px, the #2212 answered weight, and the
       `dismissed` sub-label tells the two apart. */
    .wep-pill-foreign {
        border-style: dashed;
        border-width: 3px;
    }

    .wep-pill-aheadofattestation {
        border-style: dashed;
        border-width: 1px;
    }

    /* UNVERIFIED — the claim is present and the evidence is not. Keeps
       servedunverified's existing dashed intent but separates it from
       ATTENTION, which is a different operator question. */
    .wep-pill-servedunverified,
    .wep-pill-lastknownunverified,
    .wep-pill-networkpostureunknown,
    .wep-pill-observedunattested,
    .wep-pill-unsignedattestation,
    .wep-pill-requiresusercontext,
    .wep-pill-installerunverified,
    .wep-pill-installeraccepted,
    .wep-pill-sub.wep-origin-manualattest {
        border-style: dotted;
        border-width: 2px;
    }

    /* UNVERIFIED at 1px — `not driven` (Plan 2026-09-08-1838 U3, R2a). It belongs
       in this family (the claim of catalog coverage exists; the proof of placement
       does not) but it shares the endpoint status column with `provenance pending`
       (observedunattested, dotted 2px above), and the two mean OPPOSITE things about
       management. Only four border styles exist, so family alone cannot separate
       them; WIDTH is the channel — the same one BLOCKED (3px double) already uses to
       differ from ATTENTION (2px dashed). A style-and-width pair is what an operator
       actually sees, and this pair is unique in that column. */
    .wep-pill-notdriven {
        border-style: dotted;
        border-width: 1px;
    }

    /* Recognition — `recognized` and `possible` (#2053). Both mean "identity
       known, placement unproven", and both render in the endpoint status
       column beside `match` (solid 1px), `not driven` (dotted 1px) and
       `provenance pending` (dotted 2px). At the family defaults they were
       indistinguishable from `match` and `provenance pending`. `recognized`
       keeps a solid line, because the identity is settled, and takes 2px to
       stay apart from GOOD's 1px. `possible` stays UNVERIFIED as the loudest
       dotted, apart from both of its dotted neighbours. */
    .wep-pill-recognizedconfident {
        border-style: solid;
        border-width: 2px;
    }

    .wep-pill-recognizedpossible {
        border-style: dotted;
        border-width: 3px;
    }

    /* INERT — transient or nothing to act on. GrayText is a real forced-colors
       system colour and carries "inactive" semantically, so this family reads
       as quiet rather than as another alarm. */
    .wep-pill-offline,
    .wep-pill-decommissioned,
    .wep-pill-cancelled,
    .wep-pill-appinusedeferred,
    .wep-pill-settling,
    .wep-pill-detonating,
    .wep-pill-brokerreconnecting,
    .wep-pill-brokerconnecting {
        border-style: solid;
        border-width: 1px;
        color: GrayText;
        border-color: GrayText;
    }

    /* GOOD is the 1px solid base — stated explicitly so a future state added
       to this family is a deliberate choice rather than a default it fell
       into. .wep-pill-clean, -attested, -sbomready, -promoted,
       -derivedartifact, -networkpostureoffline, -brokerhealthy,
       -installerverified. */
}

/* ─── List pages: the content wrapper and the list shell (#4213 U10, KTD15) ───
 * /catalog is a list of apps an operator scans, and /inventory follows it. Both
 * share the rules below: this sheet is the one the console serves and every
 * artboard links, so a second list page reuses them with no copy.
 *
 * THE CONTENT WRAPPER, WHICH NO LONGER CAPS. `.wep-content-cap` held the content
 * to 1440px, and to 1600px at the 2560 rung (R28). Marc removed the cap on
 * 2026-10-16: on a wide screen it left a third to a half of the workspace
 * empty beside the list. The wrapper now fills the workspace at every width,
 * and the class carries no width rule of its own. It stays because the list
 * shell below is rooted at it (`.wep-list-page .wep-content-cap …`), so those
 * rules reach the list and never a drawer the page opens. It sits on an INNER
 * wrapper after the page head, never on the page root: the root's first child
 * is the head band, which escapes the workspace gutter to both edges (see
 * `.wep-main > * > .wep-page-head:first-child` above). Prose keeps its own
 * measure (60ch, 72ch, 75ch), so filling the screen widens rows and grids,
 * never a line of text. From 1920 the docked evidence drawer takes its gutter
 * from the workspace, and the wrapper narrows beside it (design.md §3.6). */

/* THE LIST SHELL: one toolbar row (the search form, then the sort choice, which
 * the list pages set beside the search), the view chips beneath it (the kit's `.wep-chip-row`), a summary
 * line, the table, and the pager. Every control in it is a link or a GET form,
 * so each one is an address (R1, R3). */
.wep-list-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--wep-space-3);
  margin: var(--wep-space-4) 0 var(--wep-space-3);
}
.wep-list-search {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  flex: 1 1 320px;
  max-width: 560px;
  margin: 0;
}
/* The kit input is 320px wide by declaration; in the search form it takes the
 * room the form has. Two classes, so it outranks `.wep-input` whatever the order. */
.wep-list-search .wep-list-search-input {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}
.wep-list-search-clear {
  font-size: var(--wep-size-text-sm);
  white-space: nowrap;
}
/* The sort choice: two links, the one in force marked. Not a select and not
 * column headers: each sort is an address, and the list sorts in SQL. */
.wep-list-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--wep-space-1);
  margin-left: auto;
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-3);
}
.wep-list-sort-label {
  margin-right: var(--wep-space-1);
}
.wep-list-sort-link {
  padding: 2px var(--wep-space-2);
  border-radius: var(--wep-radius-xs);
  color: var(--wep-color-fg-3);
  text-decoration: none;
}
.wep-list-sort-link:hover {
  color: var(--wep-color-fg-1);
}
.wep-list-sort-link.is-active {
  background: var(--wep-color-bg-2);
  color: var(--wep-color-fg-1);
  font-weight: var(--wep-weight-medium);
}
.wep-list-views {
  margin: 0 0 var(--wep-space-3);
}
/* A view the read could not evaluate, or that a manager has no data for (ADR
 * 2026-10-08-001 §3): a dash, not a count, so it is not a link. The kit chip is
 * a control (a pointer cursor, a solid border); this one is neither, and says
 * so with a dashed border and the default cursor. Its reason is text beside it,
 * so the border is never the only channel. */
.wep-list-chip-unevaluated {
  border-style: dashed;
  color: var(--wep-color-fg-3);
  cursor: default;
}
.wep-list-summary {
  margin: 0 0 var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  line-height: var(--wep-line-snug);
  color: var(--wep-color-fg-3);
}
.wep-list-pager {
  display: flex;
  align-items: center;
  gap: var(--wep-space-4);
  margin: var(--wep-space-3) 0 0;
  font-size: var(--wep-size-text-sm);
}
.wep-list-pager-status {
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
/* The Previous of the first page and the Next of the last: said, not linked. */
.wep-list-pager-off {
  color: var(--wep-color-fg-4);
}

/* ═════════════════════════════════════════════════════════════════════
 * #4323 — THE TWO LIST PAGES AS THE APPROVED #4213 DRAWING HAS THEM.
 *
 * /catalog and /inventory wear `.wep-list-page` on their page root, and so do
 * the boards that draw them (catalog-index.html, inventory.html). Every rule
 * here is rooted at it, so no other page changes. One copy, in this sheet, because the pages and their boards both
 * load it; there is no scoped mirror to drift.
 *
 * The head and its tabs left this block (#4561 U2, 2026-10-20): they are the
 * kit's `.wep-page-head` and `.wep-subtabs` now, on every page. The chips and
 * the lowercase pills left it in U3: they are the kit's `.wep-chip` and
 * `.wep-pill`.
 *
 * Specificity. The scoped bundle loads AFTER this sheet, and a scoped rule is
 * one class plus its scope attribute, (0,2,0). A rule here that must beat one
 * (PageHead's padding, the Actions menu's anchor) is
 * written at (0,2,1), (0,3,0) or more, never with !important.
 * ═════════════════════════════════════════════════════════════════════ */

/* The search: ONE box holding a search glyph, the field and a `/` hint, as
 * drawn. Enter submits (it is the form's only text field), so no button shows;
 * the submit stays in the form for assistive technology and comes into view
 * when it is focused. The box is the form, so its ring follows focus into the
 * field. */
.wep-list-page .wep-list-toolbar {
  gap: var(--wep-space-3);
  margin: var(--wep-space-4) 0 var(--wep-space-3);
}
.wep-list-page .wep-list-toolbar .wep-list-search {
  position: relative;
  flex: 0 1 380px;
  max-width: 380px;
  gap: var(--wep-space-2);
  padding: 0 var(--wep-space-2) 0 var(--wep-space-3);
  border: 1px solid var(--wep-color-border-2);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-0);
  color: var(--wep-color-fg-3);
}
.wep-list-page .wep-list-toolbar .wep-list-search:focus-within {
  border-color: var(--wep-color-border-focus);
  outline: 2px solid transparent;
  box-shadow: var(--wep-focus-ring);
}
.wep-list-page .wep-list-search .wep-list-search-glyph {
  flex: none;
  display: inline-flex;
  color: var(--wep-color-fg-3);
}
.wep-list-page .wep-list-search .wep-list-search-input.wep-input {
  height: 32px;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  font-size: var(--wep-size-text-sm);
}
/* The ring is the box's (above): the field draws none of its own, and keeps a
 * transparent outline, which forced colours paints. */
.wep-list-page .wep-list-search .wep-list-search-input.wep-input:focus,
.wep-list-page .wep-list-search .wep-list-search-input.wep-input:focus-visible {
  outline: 2px solid transparent;
  box-shadow: none;
}
.wep-list-page .wep-list-search .wep-list-search-key {
  flex: none;
  padding: 0 5px;
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-xs);
  font-family: var(--wep-font-mono);
  font-size: var(--wep-size-text-xs);
  line-height: 18px;
  color: var(--wep-color-fg-4);
}
.wep-list-page .wep-list-search .wep-list-search-clear {
  font-size: var(--wep-size-text-xs);
}
/* Out of sight until focused, the way a skip link is: Tab still reaches it,
 * and while it holds focus it shows in the box. */
.wep-list-page .wep-list-search .wep-list-search-submit:not(:focus) {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* The sort sits beside the search it belongs with (#4432 follow-up, Marc): the kit's
 * `.wep-list-sort` takes the row's far end (margin-left: auto), and once the list
 * filled the workspace that end was a 2560 or 3440 screen away from the search and
 * from the table's first heading. One wide gap apart, the row still reads search,
 * then sort, as drawn. */
.wep-list-page .wep-list-toolbar .wep-list-sort {
  margin-left: var(--wep-space-6);
  font-size: var(--wep-size-text-xs);
}

/* The app column: the initials tile, then the name in bold ink over its id
 * line. The name is still a real link to the package page; its underline shows
 * on hover and on focus, not at rest. */
.wep-list-page .wep-content-cap .wep-pkg-initials {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
}
.wep-list-page .wep-content-cap .wep-pkg-cell {
  gap: 10px;
}
.wep-list-page .wep-content-cap .wep-pkg-text {
  line-height: 1.3;
}
.wep-list-page .wep-content-cap .wep-pkg-id-secondary {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
}
.wep-list-page .wep-content-cap a.wep-list-name,
.wep-list-page .wep-content-cap strong.wep-list-name {
  font-size: var(--wep-size-text-sm);
  font-weight: var(--wep-weight-semi);
  color: var(--wep-color-fg-1);
  text-decoration: none;
}
.wep-list-page .wep-content-cap a.wep-list-name:hover,
.wep-list-page .wep-content-cap a.wep-list-name:focus-visible {
  text-decoration: underline;
}
/* A column name stays on one line, and a number column's name is set like
 * every other column's, as drawn (the kit sets it in mono). */
.wep-list-page .wep-content-cap .wep-table thead th {
  white-space: nowrap;
}
.wep-list-page .wep-content-cap .wep-table thead th.wep-num {
  font-family: var(--wep-font-sans);
}
/* A count that opens the endpoints drawer, a version that opens its package
 * page: mono in the link colour, underlined on hover and on focus. */
.wep-list-page .wep-content-cap .wep-table a.wep-link.wep-mono,
.wep-list-page .wep-content-cap .wep-table td.wep-num > a.wep-link {
  font-family: var(--wep-font-mono);
  text-decoration: none;
}
.wep-list-page .wep-content-cap .wep-table a.wep-link.wep-mono:hover,
.wep-list-page .wep-content-cap .wep-table a.wep-link.wep-mono:focus-visible,
.wep-list-page .wep-content-cap .wep-table td.wep-num > a.wep-link:hover,
.wep-list-page .wep-content-cap .wep-table td.wep-num > a.wep-link:focus-visible {
  text-decoration: underline;
}
/* A line the list says about itself above the table ("This list opens on the
 * apps that need attention."): a breath of space before the table. */
.wep-list-page .wep-content-cap > .wep-fleet-view-note {
  margin-bottom: var(--wep-space-3);
}
/* The summary under a list ("Showing 5 of 9 apps"): a quiet line at the label size. */
.wep-list-page .wep-content-cap .wep-list-summary {
  margin: var(--wep-space-3) 0 var(--wep-space-2);
  font-size: var(--wep-size-text-xs);
}
/* A relative time ("3 h", "2 d"): quiet words at the label size, not mono. */
.wep-list-page .wep-content-cap .wep-table td.wep-list-when {
  font-family: var(--wep-font-sans);
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-fg-3);
}
/* "Open →" ends a row: a quiet link in the link colour, as drawn. */
.wep-list-page .wep-content-cap a.wep-list-open {
  font-size: var(--wep-size-text-xs);
  color: var(--wep-color-link);
  text-decoration: none;
  white-space: nowrap;
}
.wep-list-page .wep-content-cap a.wep-list-open:hover,
.wep-list-page .wep-content-cap a.wep-list-open:focus-visible {
  text-decoration: underline;
}

/* #4323 — end of the list pages' block (ListPageRulesStayOnTheListPagesTests reads up to here). */

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════
 * #4388 PR D (U11) — App in use. The pieces the approved drawing (app-in-use-modes-proposal.html) draws on three
 * surfaces at once — the package drawer, the group section and the settings page — and the endpoint section, so they
 * are kit rules (DeadStateClassTests: a shared idiom is defined globally, not inside one page's scope). The drawing's
 * own aiu-* rules are its review scaffolding; these are the shipped names the bound boards use. Tokens only, except
 * the narrow number and unit widths the settings boards already use.
 * ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════ */
.wep-aiu-fieldset { margin: var(--wep-space-4) 0 0; }
.wep-aiu-fieldset > legend { padding: 0 var(--wep-space-1); }
.wep-aiu-fieldset .wep-form-hint { margin: 0; }
.wep-aiu-fieldset > .wep-form-hint { margin-top: var(--wep-space-3); }
.wep-aiu-choices { display: flex; flex-direction: column; gap: var(--wep-space-3); }
.wep-aiu-choices.wep-aiu-gap-top { margin-top: var(--wep-space-3); }
.wep-aiu-choice { display: flex; align-items: flex-start; }
.wep-aiu-choice .wep-radio { flex-shrink: 0; margin-top: var(--wep-space-1); }
/* A choice's second line outside the package page, whose own .wep-pkg-sub is scoped to it: the same look. */
.wep-aiu-sub { display: block; font-size: var(--wep-size-text-sm); color: var(--wep-color-fg-3); }
.wep-aiu-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--wep-space-4);
  margin: var(--wep-space-3) 0 0 var(--wep-space-6);
}
.wep-aiu-fields-flush { margin-left: 0; }
.wep-aiu-fields-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.wep-aiu-fields .wep-form-hint,
.wep-aiu-fields .wep-field-error { margin: 0; }
.wep-aiu-duration {
  display: flex;
  align-items: center;
  gap: var(--wep-space-2);
  font-size: var(--wep-size-text-sm);
  color: var(--wep-color-fg-2);
}
.wep-aiu-num, .wep-aiu-unit { width: 120px; }
.wep-aiu-preview {
  margin: var(--wep-space-4) 0 0;
  padding: var(--wep-space-3) var(--wep-space-4);
  border: 1px solid var(--wep-color-border-1);
  border-radius: var(--wep-radius-sm);
  background: var(--wep-color-bg-0);
}
.wep-aiu-preview .wep-pkg-kv { font-size: var(--wep-size-text-sm); }
.wep-aiu-preview p { margin: 0 0 var(--wep-space-2); font-size: var(--wep-size-text-sm); }
.wep-aiu-hint { margin: var(--wep-space-1) 0 0; max-width: 92ch; }
/* #4388 PR D (review, round 4): the permission's name in an editor's read-only reason or refusal (AppInUsePermissionText)
 * is the drawing's .wep-pkg-mono on boards 8a and 8e, 13px mono, on the group and settings pages too, which do not load
 * the package page's sheet. 13px is _package.css's literal for an identifier in a sentence; the kit has no 13px step. */
.wep-aiu-permission { font-family: var(--wep-font-mono); font-size: 13px; }
.wep-aiu-hint-first { margin-top: var(--wep-space-3); }
.wep-aiu-indented { margin-left: var(--wep-space-6); }
.wep-aiu-section { margin-top: var(--wep-space-6); }
.wep-aiu-section .wep-section-lead { max-width: 80ch; }
.wep-aiu-measure { max-width: 92ch; }
/* (on the endpoint page the note is the page's .wep-endpoint-note, whose scoped margin would otherwise win) */
.wep-aiu-note-gap, .wep-endpoint-section p.wep-aiu-note-gap { margin-bottom: var(--wep-space-3); }
.wep-aiu-banner { margin: var(--wep-space-3) 0 0; }
/* A count that opens the list it counts (ADR 2026-10-08-001 as extended by #4388): a <button> in the drawing's link
 * look (the package page's .wep-pkg-link: the link colour, no underline until it is pointed at), and the list it
 * opens in place, one endpoint per line. */
button.wep-aiu-count {
  height: auto;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--wep-color-link);
  text-decoration: none;
  cursor: pointer;
}
button.wep-aiu-count:hover { color: var(--wep-color-link-hover); text-decoration: underline; }
.wep-aiu-reach-list { margin: var(--wep-space-1) 0 var(--wep-space-2); padding-left: var(--wep-space-5); }
.wep-aiu-reach-list li { margin: 0; }
/* The endpoint section's cells hold a sentence, so they wrap (the installer label's precedent); the package cell
 * keeps its one line. */
.wep-table.wep-aiu-endpoint-table tbody td { white-space: normal; min-width: 16ch; }
.wep-table.wep-aiu-endpoint-table tbody td.wep-endpoint-pkg-cell { white-space: nowrap; min-width: 0; }
/* The endpoint page's own cell rules (.wep-endpoint-pkg-name, -pkg-sub, -cell-sub, the indicator) are scoped to
 * FleetEndpointDetail and do not reach the section's child component, so the section carries them here, in the same
 * values (docs/design/prototypes/_endpoint.css mirrors the page's for the boards). */
.wep-aiu-endpoint-table thead th { white-space: nowrap; }
.wep-aiu-endpoint-table .wep-endpoint-pkg-name { display: block; }
.wep-aiu-endpoint-table .wep-endpoint-pkg-sub { display: block; font-size: var(--wep-size-text-sm); }
.wep-aiu-endpoint-table .wep-endpoint-cell-sub { display: block; margin-top: var(--wep-space-1); font-size: var(--wep-size-text-sm); }
/* The older-agent indicator: .wep-endpoint-indicator's values (that rule is the page's, and reaches only its markup). */
.wep-aiu-indicator { margin: var(--wep-space-2) 0; font-size: var(--wep-size-text-sm); }
