/* GovHub 2.0 — runtime stylesheet (GENERATED — do not edit by hand).
   Built from docs/specs/design-framework/reference/{tokens,components}.css
   by scripts/build-css.sh. Edit the reference, then run `task css:build`.
   Drift between this file and the reference is caught by `task css:check`. */

/* ---------- Self-hosted Inter (App UI font; the brand display font is
   login-only and falls back to system). Served from /static/fonts via
   embed.FS — no CDN. Must be named exactly `Inter` so var(--font-ui)
   resolves to it without a separate alias. ---------- */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/InterVariable.woff2") format("woff2");
}

/* ============================================================
   GovHub 2.0 — Design Tokens (v2, "Precision SaaS")
   Single source of truth for color, type, spacing, elevation.
   Brand values: CLOUDPILOTS CI manual (as of November 2025).

   DESIGN DIRECTION v2: crisp, hairline-border, flat-surface UI.
   Borders define structure; shadows are reserved for floating
   layers (dropdown, dialog, toast, peek panel). Cool slate
   neutrals, two-role blue system (brand navy = identity,
   action blue = interaction), squared-badge status language,
   14px data-grade type on an 8-pt grid.
   North stars: Linear (structure/density), Stripe Dashboard
   (data/table/form language), Attio (temperature/chips).

   RULE: No raw hex / px values anywhere outside this file.
         Components and pages reference var(--token) only.
   SSR : Pure CSS custom properties — render server-side with
         zero JavaScript. Link this file first, before components.
   ============================================================ */

/* ---------- Webfonts ----------
   Inter Variable — App UI (body + headings). Data-dense friendly,
   tabular figures, broad glyph coverage. Self-hosted in production;
   the CDN import is for the standalone reference pages only.
   PT Sans Narrow — Brand/marketing/login ONLY (display), not in the app. */
/* (CDN @import from fonts.googleapis.com intentionally omitted —
    self-hosted @font-face above provides Inter at runtime.) */

:root {
  /* =========================================================
     1. CORE BRAND COLORS  (CI manual p. 7–8)
     Two-role blue system:
       brand  (navy #345E93)  — identity: logo, sidebar rail
                                accents, selected-nav, focus ring
       action (blue-600)      — interaction: buttons, links,
                                active controls. Same hue family,
                                higher chroma → clear affordance.
     ========================================================= */
  --clp-blue:      #345E93;   /* primary brand (identity) */
  --clp-yellow:    #FFBB29;   /* accent only — never body text */
  --clp-black:     #212121;
  --clp-gray-text: #424242;
  --clp-white:     #FFFFFF;

  /* Brand blue scale (hue ≈ 214, chroma peaks at 600) */
  --clp-blue-950: #142338;
  --clp-blue-900: #1d3350;
  --clp-blue-800: #274568;
  --clp-blue-700: #345E93;   /* = brand navy */
  --clp-blue-600: #2f6cba;   /* = action blue */
  --clp-blue-500: #4d84c9;
  --clp-blue-400: #7ba6d8;
  --clp-blue-300: #aac6e6;
  --clp-blue-200: #d0e0f2;
  --clp-blue-100: #e7eff8;
  --clp-blue-50:  #f3f7fb;

  /* Neutral scale — cool slate, pairs with the blue family */
  --clp-neutral-950: #0d1220;
  --clp-neutral-900: #1a2233;
  --clp-neutral-800: #2c3547;
  --clp-neutral-700: #414c61;
  --clp-neutral-600: #57637a;
  --clp-neutral-500: #717d92;
  --clp-neutral-400: #9ba4b5;
  --clp-neutral-300: #cdd3dc;
  --clp-neutral-200: #e1e5eb;
  --clp-neutral-150: #eaedf2;
  --clp-neutral-100: #f1f3f6;
  --clp-neutral-50:  #f8f9fb;
  --clp-neutral-25:  #fcfcfd;
  --clp-neutral-0:   #ffffff;

  /* Accent (yellow) scale — accent + warning family only */
  --clp-yellow-700: #a56b06;
  --clp-yellow-600: #cd8c0e;
  --clp-yellow-500: #FFBB29;
  --clp-yellow-400: #ffc955;
  --clp-yellow-300: #ffd882;
  --clp-yellow-200: #ffe7b0;
  --clp-yellow-100: #fff3d6;

  /* Status hues — each hue ships base (icons/dots), 700 (text on
     light), bg (tint fill), border (tint outline). Canon and usage
     rules: 02-colors-and-status.md. */
  --status-success:        #1a9e6d;
  --status-success-700:    #0e6b47;
  --status-success-bg:     #e6f6ef;
  --status-success-border: #b9e6d2;
  --status-warning:        #e09a12;
  --status-warning-700:    #8f5b06;
  --status-warning-bg:     #fdf3df;
  --status-warning-border: #f3ddaa;
  --status-danger:         #c2372c;
  --status-danger-700:     #9c2b22;
  --status-danger-bg:      #fceceb;
  --status-danger-border:  #f2c7c3;
  --status-info:           #2f6cba;
  --status-info-700:       #274568;
  --status-info-bg:        #e7eff8;
  --status-info-border:    #c4d9ee;
  --status-neutral:        #717d92;
  --status-neutral-700:    #414c61;
  --status-neutral-bg:     #f1f3f6;
  --status-neutral-border: #dbe0e7;

  /* =========================================================
     2. SEMANTIC COLOR TOKENS  (use these, not the raw scale)
     ========================================================= */
  --bg-app:        var(--clp-neutral-25);   /* page canvas — near-white */
  --bg-sidebar:    var(--clp-neutral-50);   /* app-shell sidebar rail */
  --bg-surface:    var(--clp-white);        /* cards, panels, rows */
  --bg-subtle:     var(--clp-neutral-100);  /* inset wells, skeletons */
  --bg-muted:      var(--clp-blue-50);
  --bg-inverse:    var(--clp-neutral-900);
  --bg-brand:      var(--clp-blue-700);     /* identity surfaces */
  --bg-action:     var(--clp-blue-600);     /* primary buttons */
  --bg-action-hover: var(--clp-blue-700);
  --bg-brand-soft: var(--clp-blue-100);     /* active nav item, selected */
  --bg-accent:     var(--clp-yellow-500);
  --bg-hover:      var(--clp-neutral-100);  /* row / item hover */

  --fg-primary:    var(--clp-neutral-900);
  --fg-body:       var(--clp-neutral-700);
  --fg-muted:      var(--clp-neutral-500);
  --fg-brand:      var(--clp-blue-700);
  --fg-action:     var(--clp-blue-600);
  --fg-on-brand:   var(--clp-white);
  --fg-on-action:  var(--clp-white);
  --fg-on-accent:  var(--clp-neutral-900);
  --fg-on-inverse: var(--clp-white);
  --fg-link:       var(--clp-blue-600);
  --fg-link-hover: var(--clp-blue-700);

  --border-subtle:  var(--clp-neutral-150); /* hairline card / divider */
  --border-default: var(--clp-neutral-200); /* controls, tables */
  --border-strong:  var(--clp-neutral-300); /* hover / emphasis */
  --border-brand:   var(--clp-blue-700);
  --border-action:  var(--clp-blue-600);

  --ring-focus: color-mix(in srgb, var(--clp-blue-600) 40%, transparent);

  /* =========================================================
     3. TYPOGRAPHY  (14px data-grade scale, tabular numerals)
     ========================================================= */
  --font-ui:    'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-brand: 'PT Sans Narrow', var(--font-ui);   /* marketing / login only */
  --font-mono:  ui-monospace, 'SF Mono', Menlo, Consolas, 'Roboto Mono', monospace;

  --fs-display: 1.75rem;    /* 28px — hero numbers, login */
  --fs-stat:    1.625rem;   /* 26px — KPI numbers */
  --fs-h1:      1.375rem;   /* 22px — page title */
  --fs-h2:      1.125rem;   /* 18px — section */
  --fs-h3:      1rem;       /* 16px — card / subsection */
  --fs-h4:      0.875rem;   /* 14px — label-ish heading (semibold) */
  --fs-body:    0.875rem;   /* 14px — base body */
  --fs-small:   0.8125rem;  /* 13px — secondary, table cells */
  --fs-caption: 0.75rem;    /* 12px — captions, table heads, meta */

  --fw-regular: 400;
  --fw-medium:  500;
  --fw-semibold:600;
  --fw-bold:    700;

  --lh-tight: 1.2;
  --lh-snug:  1.4;
  --lh-normal:1.5;
  --lh-loose: 1.65;

  --ls-wide:   0.05em;      /* uppercase eyebrows / table heads */
  --ls-tight: -0.02em;      /* h1 / h2 / stat numbers */
  --ls-snug:  -0.01em;      /* h3 / buttons */
  --ls-normal: 0;

  /* =========================================================
     4. SPACING  (8-pt rhythm; 4px half-step allowed)
     ========================================================= */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;

  /* =========================================================
     5. RADIUS / ELEVATION / MOTION
        Precision-SaaS: tighter radii; borders carry structure,
        shadows belong to floating layers only.
     ========================================================= */
  --radius-none: 0;
  --radius-sm:   4px;    /* chips, small pills' inner bits */
  --radius-md:   6px;    /* default control radius (buttons, inputs) */
  --radius-lg:   10px;   /* cards, panels */
  --radius-xl:   14px;   /* dialogs, large overlays */
  --radius-pill: 999px;

  /* xs/sm: resting hints on interactive surfaces (cards keep border-only).
     md: dropdowns / popovers. lg: dialogs / peek panels / toasts. */
  --shadow-xs: 0 1px 2px rgba(13,18,32,0.05);
  --shadow-sm: 0 1px 3px rgba(13,18,32,0.07), 0 1px 2px rgba(13,18,32,0.04);
  --shadow-md: 0 4px 12px rgba(13,18,32,0.10), 0 1px 3px rgba(13,18,32,0.06);
  --shadow-lg: 0 16px 40px rgba(13,18,32,0.16), 0 4px 12px rgba(13,18,32,0.08);
  --shadow-brand: 0 4px 14px rgba(47,108,186,0.28);

  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 100ms;
  --dur-base: 150ms;
  --dur-slow: 240ms;

  /* =========================================================
     6. LAYOUT SCALE  (app shell + z-index)
     ========================================================= */
  --layout-sidebar-w:           240px;
  --layout-sidebar-w-collapsed: 64px;
  --layout-topbar-h:            52px;
  --layout-content-max:         1320px;
  --layout-gutter:              var(--space-8);

  --icon-sm: 16px;
  --icon-md: 20px;
  --icon-lg: 24px;

  --z-base:     0;
  --z-sticky:   100;   /* sticky table head, page header */
  --z-nav:      200;   /* app shell sidebar / topbar */
  --z-dropdown: 300;
  --z-overlay:  400;   /* dialog backdrop */
  --z-dialog:   401;
  --z-toast:    500;

  /* =========================================================
     7. CATEGORY PALETTE  (data-vis, NOT status)
     Stable 8-slot palette for categories without status meaning —
     framework chips, relationship-map edges, multi-series charts.
     Saturated stroke/text value + low-saturation surface tint.
     Assign deterministically (hash of stable id mod 8); never
     reuse for status. Retuned to the cool slate temperature.
     ========================================================= */
  --c-cat-1: #2f6cba;  --c-cat-1-bg: #e7eff8;  /* action blue  — slot 0 */
  --c-cat-2: #7c58d6;  --c-cat-2-bg: #efeafb;  /* violet       — slot 1 */
  --c-cat-3: #0f8f83;  --c-cat-3-bg: #e2f3f1;  /* teal         — slot 2 */
  --c-cat-4: #c05f1e;  --c-cat-4-bg: #faece0;  /* deep orange  — slot 3 */
  --c-cat-5: #4f52d8;  --c-cat-5-bg: #ebebfb;  /* indigo       — slot 4 */
  --c-cat-6: #b13087;  --c-cat-6-bg: #f8e8f2;  /* magenta      — slot 5 */
  --c-cat-7: #4e7d22;  --c-cat-7-bg: #edf3e3;  /* moss green   — slot 6 */
  --c-cat-8: #57637a;  --c-cat-8-bg: #eaedf2;  /* slate        — slot 7 */

  /* =========================================================
     8. BREAKPOINTS  (reference values — see note below)
     --bp-* are documentation tokens; use them via the media
     queries listed in 03-layout-and-app-shell.md. Custom
     properties cannot be used directly inside @media, so the
     literal values are repeated there intentionally.
     ========================================================= */
  --bp-sm: 640px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;

  /* =========================================================
     9. DATA-VIZ CATEGORICAL PALETTE  (diagram archetype — ADR-0054)
     Categorical, 8-slot palette for the Compliance-flow Sankey
     (v2 restyle 2026-07-05). Deterministic mapping
     framework_id → slot N via FNV-1a of the UUID modulo 8.
     Slot 1 is the v2 action blue so the palette head reads
     on-brand; slots 2–8 follow a CVD-optimised fixed order
     (validated on white surface: worst adjacent Machado-2009
     ΔE 24.2, lightness band + chroma floor PASS). Three slots
     sit below 3:1 mark-contrast on white — relief is the
     always-visible node labels + the parallel ?view=list table.
     Distinct from the status canon (--status-*) so a series
     colour never reads as "compliance outcome". Fixed order,
     assigned in sequence, never cycled.
     ========================================================= */
  --sankey-ribbon-1: #2f6cba;  /* blue (action)  — slot 1 */
  --sankey-ribbon-2: #1baf7a;  /* aqua           — slot 2 */
  --sankey-ribbon-3: #eda100;  /* amber          — slot 3 */
  --sankey-ribbon-4: #008300;  /* green          — slot 4 */
  --sankey-ribbon-5: #4a3aa7;  /* violet         — slot 5 */
  --sankey-ribbon-6: #e34948;  /* red            — slot 6 */
  --sankey-ribbon-7: #e87ba4;  /* magenta        — slot 7 */
  --sankey-ribbon-8: #eb6834;  /* orange         — slot 8 */

  --sankey-node-fg: var(--clp-neutral-900);  /* node label ink */
  --sankey-node-bg: var(--bg-surface);       /* diagram surface (white card) */
  --sankey-edge-hover: color-mix(in srgb, var(--clp-blue-700) 45%, transparent);
}

/* Dark mode is intentionally NOT shipped yet. Components read the
   semantic layer (section 2) only, so a future [data-theme="dark"]
   block re-maps semantics without touching any component CSS. */
/* ============================================================
   GovHub 2.0 — Components & Base
   Reset + base typography + layout helpers + all component
   classes. Consumes tokens.css ONLY (no raw values here either,
   except structural zeroes and 100%/1px hairlines).
   Link order:  <link tokens.css> then <link components.css>

   DIRECTION v2: "Precision SaaS" — crisp hairline-border,
   flat-surface UI. Borders define structure; shadows are
   reserved for floating layers (dropdown, dialog, toast,
   panels). Tinted-rail sidebar on a near-white canvas,
   action-blue interactions, squared status badges.
   Status always: color + icon + text.
   ============================================================ */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body { min-height: 100vh; color: var(--fg-body); background: var(--bg-app);
  font-family: var(--font-ui); font-size: var(--fs-body);
  line-height: var(--lh-normal); -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility; }
img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }
:where(a) { color: var(--fg-link); }

/* Visible focus everywhere — accessibility is markup, not opt-in */
:focus-visible { outline: 2px solid var(--ring-focus); outline-offset: 2px;
  border-radius: var(--radius-sm); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important;
    transition-duration: .001ms !important; }
}

/* Skip link — first focusable element, jumps to <main id="content"> */
.skip-link { position: absolute; left: var(--space-3); top: -100%;
  z-index: var(--z-toast); background: var(--bg-surface); color: var(--fg-link);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md); }
.skip-link:focus { top: var(--space-3); }

/* ---------- Base typography ---------- */
h1, h2, h3, h4 { font-family: var(--font-ui); font-weight: var(--fw-semibold);
  color: var(--fg-primary); line-height: var(--lh-tight); text-align: left;
  text-wrap: balance; letter-spacing: var(--ls-tight); }
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }
p  { text-wrap: pretty; }
strong, b { font-weight: var(--fw-semibold); }
small { font-size: var(--fs-small); }
a { text-decoration: none; color: var(--fg-link);
  transition: color var(--dur-fast) var(--ease-out); }
a:hover { color: var(--fg-link-hover); }
:where(p, .field__help, .prose) a { text-decoration: underline;
  text-underline-offset: 2px; text-decoration-thickness: 1px; }
code, kbd, samp { font-family: var(--font-mono); font-size: .9em; }
.muted   { color: var(--fg-muted); }
.eyebrow { font-weight: var(--fw-semibold); font-size: var(--fs-caption);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-brand); }
.caption { font-size: var(--fs-caption); color: var(--fg-muted); }

/* ============================================================
   ICON  (reference/icons.svg → <svg class="icon"><use href="#i-…"></svg>)
   Inherits text color; sized by font context. No raw SVG in markup.
   ============================================================ */
.icon { width: var(--icon-md); height: var(--icon-md); flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  vertical-align: middle; }
.icon--sm { width: var(--icon-sm); height: var(--icon-sm); }
.icon--lg { width: var(--icon-lg); height: var(--icon-lg); }

/* ============================================================
   LAYOUT HELPERS  (the only spacing primitives — see 03)
   ============================================================ */
.stack        { display: flex; flex-direction: column; gap: var(--space-6); }
.stack--tight { gap: var(--space-3); }
.stack--loose { gap: var(--space-8); }
.cluster      { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.split        { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.grid-cards   { display: grid; gap: var(--space-6);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.grid-stats   { display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/* ============================================================
   APP SHELL  (03-layout-and-app-shell.md)
   ============================================================ */
.shell { display: grid; grid-template-columns: var(--layout-sidebar-w) 1fr;
  min-height: 100vh; }

.shell__sidebar { background: var(--bg-sidebar);
  border-right: 1px solid var(--border-subtle);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
  z-index: var(--z-nav); }
.shell__main { display: flex; flex-direction: column; min-width: 0; }
.shell__content { width: 100%; max-width: var(--layout-content-max);
  margin-inline: auto; padding: var(--layout-gutter); }

/* Brand block — logo mark + wordmark */
.sidebar__brand { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-4); min-height: var(--layout-topbar-h);
  font-weight: var(--fw-bold); font-size: var(--fs-h3); color: var(--fg-primary);
  letter-spacing: var(--ls-tight); }
.sidebar__mark { width: 30px; height: 30px; flex: none; border-radius: var(--radius-md);
  background: var(--bg-brand); color: var(--fg-on-brand);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: var(--fw-bold); }

.sidebar__nav { display: flex; flex-direction: column; gap: 2px;
  padding: var(--space-2) var(--space-3); flex: 1; overflow-y: auto; }
.sidebar__section { padding: var(--space-4) var(--space-3) var(--space-1);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-muted); }
.sidebar__link { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  color: var(--fg-body); text-decoration: none; min-height: 34px;
  font-weight: var(--fw-medium); font-size: var(--fs-small);
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out); }
.sidebar__link .icon { color: var(--fg-muted); }
.sidebar__link:hover { background: var(--clp-neutral-150); color: var(--fg-primary); }
.sidebar__link:hover .icon { color: var(--fg-body); }
.sidebar__link[aria-current="page"] { background: var(--bg-brand-soft);
  color: var(--fg-brand); font-weight: var(--fw-semibold); }
.sidebar__link[aria-current="page"] .icon { color: var(--fg-brand); }
.sidebar__count { margin-left: auto; font-size: var(--fs-caption);
  font-weight: var(--fw-semibold); color: var(--fg-muted);
  background: var(--bg-subtle); border-radius: var(--radius-pill);
  padding: 1px var(--space-2); min-width: 22px; text-align: center; }

/* Settings context: "back to app" link sits above the settings nav, set apart
   by muted weight and a divider so it reads as an exit rather than a peer item. */
.sidebar__link--back { color: var(--fg-muted); font-weight: var(--fw-medium);
  margin-bottom: var(--space-1); padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle); border-radius: 0; }
.sidebar__link--back:hover { background: transparent; color: var(--fg-primary); }

.sidebar__footer { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); margin: var(--space-2);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  color: var(--fg-body); font-size: var(--fs-small); }
.sidebar__footer .icon { color: var(--fg-muted); }
.sidebar__footer small { display: block; color: var(--fg-muted); }

/* Topbar */
.topbar { display: flex; align-items: center; gap: var(--space-4);
  height: var(--layout-topbar-h); padding-inline: var(--layout-gutter);
  background: color-mix(in srgb, var(--bg-surface) 88%, transparent);
  backdrop-filter: saturate(140%) blur(6px);
  border-bottom: 1px solid var(--border-subtle);
  position: sticky; top: 0; z-index: var(--z-sticky); }
.topbar__spacer { flex: 1; }
/* Chained selector: must outrank the later .icon-btn display rule. */
.icon-btn.topbar__menu-btn { display: none; }

/* Breadcrumb */
.breadcrumb { display: flex; align-items: center; gap: var(--space-1);
  font-size: var(--fs-small); color: var(--fg-muted); }
.breadcrumb a { color: var(--fg-muted); }
.breadcrumb a:hover { color: var(--fg-body); }
.breadcrumb .icon { width: var(--icon-sm); height: var(--icon-sm); color: var(--clp-neutral-300); }
.breadcrumb [aria-current="page"] { color: var(--fg-primary); font-weight: var(--fw-medium); }

/* Search box with leading icon */
.searchbox { display: flex; align-items: center; gap: var(--space-2);
  background: var(--bg-subtle); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding-inline: var(--space-3);
  min-height: 34px; min-width: 240px;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out); }
.searchbox:focus-within { border-color: var(--border-brand);
  box-shadow: 0 0 0 3px var(--ring-focus); background: var(--bg-surface); }
.searchbox .icon { color: var(--fg-muted); width: var(--icon-sm); height: var(--icon-sm); }
.searchbox input { flex: 1; border: 0; background: transparent; outline: none;
  font-size: var(--fs-small); min-width: 0; }

/* Icon button (topbar utility actions) */
.icon-btn { display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border: 1px solid transparent; border-radius: var(--radius-md);
  background: transparent; color: var(--fg-body); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out); }
.icon-btn:hover { background: var(--bg-hover); }
/* Active state for the topbar settings gear while inside the settings context. */
.icon-btn.is-active { background: var(--bg-brand-soft); color: var(--fg-brand); }
.icon-btn.is-active .icon { color: var(--fg-brand); }

/* Page header — title left, primary CTA right (03 + 04 button rules) */
.page-header { gap: var(--space-4); align-items: flex-start; }
.page-header__title { display: flex; flex-direction: column; gap: var(--space-1); }
.page-header__title h1 { display: flex; align-items: center; gap: var(--space-3); }
/* Header action cluster (title left, buttons right): a long intro paragraph
   must not squeeze the buttons into a vertical wrap — the cluster keeps its
   intrinsic width and stays pinned to the right edge on one row. On narrow
   viewports the whole header wraps instead, dropping the cluster below the
   title rather than overflowing horizontally. */
.page-header > .cluster { flex-shrink: 0; }
@media (max-width: 640px) { .split.page-header { flex-wrap: wrap; } }
/* Collection-size count appended to a list-page H1 (e.g. "Workloads · 24"). */
.page-header__count { font-weight: var(--fw-regular); font-size: var(--fs-h3); color: var(--fg-muted); }
/* Sticky variant — opt-in modifier. Use on long designer-style detail pages
   where the single primary CTA in the header (e.g. "Save changes") must stay
   reachable while the user scrolls through deep accordions. Sticks BELOW the
   sticky topbar (top = --layout-topbar-h). Negative inline padding +
   matching positive padding bleeds the surface to the gutter so the sticky
   bar visually covers the content scrolling underneath it. Do NOT add to
   every detail page — most pages are short enough that the top-right CTA is
   reachable without sticking. */
.page-header--sticky { position: sticky; top: var(--layout-topbar-h);
  z-index: var(--z-sticky); background: var(--bg-app);
  margin-inline: calc(-1 * var(--layout-gutter));
  padding: var(--space-3) var(--layout-gutter);
  border-bottom: 1px solid var(--border-subtle); }

/* ============================================================
   BUTTON  (04 → Button)
   ============================================================ */
.btn { display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2); min-height: 36px; padding: 0 var(--space-4);
  border: 1px solid transparent; border-radius: var(--radius-md);
  font-family: var(--font-ui); font-weight: var(--fw-semibold); font-size: var(--fs-small);
  letter-spacing: var(--ls-snug);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out); }
.btn .icon { width: var(--icon-sm); height: var(--icon-sm); }
.btn--primary   { background: var(--bg-action); color: var(--fg-on-action);
  box-shadow: var(--shadow-xs); }
.btn--primary:hover   { background: var(--bg-action-hover); box-shadow: var(--shadow-sm); }
.btn--secondary { background: var(--bg-surface); color: var(--fg-body);
  border-color: var(--border-default); }
.btn--secondary:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.btn--ghost     { background: transparent; color: var(--fg-body); }
.btn--ghost:hover     { background: var(--bg-hover); }
.btn--danger    { background: var(--status-danger); color: var(--clp-white); }
.btn--danger:hover    { background: var(--status-danger-700); }
.btn--sm { min-height: 30px; padding: 0 var(--space-3); }
.btn--lg { min-height: 42px; padding: 0 var(--space-5); font-size: var(--fs-body); }
.btn[disabled], .btn.is-disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.btn.is-loading { position: relative; color: transparent; pointer-events: none; }
.btn.is-loading::after { content: ""; position: absolute; width: 16px; height: 16px;
  border: 2px solid currentColor; border-top-color: transparent;
  border-radius: var(--radius-pill); animation: spin var(--dur-slow) linear infinite;
  color: var(--fg-on-brand); }
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================
   STATUS PILL + DOT + CHIP  (02 + 04)  — Farbe + Icon + Text
   ============================================================ */
/* Squared status badge (v2): light tint + darker text, no border,
   4px radius — calmer in dense tables than filled rounded pills.
   Anatomy stays color + icon + text (canon, never color alone). */
.pill { display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-2); border-radius: var(--radius-sm);
  font-size: var(--fs-caption); font-weight: var(--fw-medium); line-height: var(--lh-snug);
  white-space: nowrap; }
.pill .icon { width: 13px; height: 13px; }
.pill--success { background: var(--status-success-bg); color: var(--status-success-700); }
.pill--warning { background: var(--status-warning-bg); color: var(--status-warning-700); }
.pill--danger  { background: var(--status-danger-bg);  color: var(--status-danger-700); }
.pill--info    { background: var(--status-info-bg);    color: var(--status-info-700); }
.pill--neutral { background: var(--status-neutral-bg); color: var(--status-neutral-700); }
/* Draft / provisional: no fill, dashed hairline — reads as "not yet real". */
.pill--outline { background: transparent; color: var(--fg-muted);
  border: 1px dashed var(--border-strong); padding-block: 1px; }

.dot { display: inline-block; width: 8px; height: 8px; border-radius: var(--radius-pill);
  vertical-align: middle; }
.dot--success { background: var(--status-success); }
.dot--warning { background: var(--status-warning); }
.dot--danger  { background: var(--status-danger); }
.dot--info    { background: var(--status-info); }
.dot--neutral { background: var(--status-neutral); }

.chip { display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px var(--space-2); border-radius: var(--radius-sm);
  background: var(--bg-subtle); border: 1px solid var(--border-subtle);
  color: var(--fg-body); font-size: var(--fs-caption); font-weight: var(--fw-medium); }
.chip__remove { display: inline-flex; cursor: pointer; color: var(--fg-muted);
  background: none; border: 0; padding: 0; }
.chip__remove:hover { color: var(--fg-primary); }
.chip__remove .icon { width: 13px; height: 13px; }
/* Data-driven chip color (tag-value / scope-value Color column). Templates
   render the user-chosen hex as the inline CSS custom property
   `--chip-color`; we accept that one inline form (R2: only `style="--…"`
   exceptions allowed) because the value originates in data, not in markup
   authoring. The border darkens via color-mix so the chip still reads as a
   bounded shape regardless of how light the user-chosen background is. */
.chip[style*="--chip-color"] {
  background: var(--chip-color);
  border-color: color-mix(in srgb, var(--chip-color) 70%, var(--clp-neutral-900));
}
/* Reveal-on-open <details>: a summary acts as the trigger ("Add assignment",
   "Add filter", …) and the body underneath holds an inline form/control.
   Opt-in via data-reveal so vanilla disclosure UIs (FAQ accordions etc.)
   keep their natural flush layout. Adds breathing room between summary and
   body when the panel is open. */
details[data-reveal][open] > *:not(summary) { margin-top: var(--space-3); }
/* Read-only chip — used when a tag category is configured via a scope-side
   tag_category field (ADR-0039 amendment 2026-06-19). The Tags section still
   shows the same chips so the user sees what is selected, but inputs are
   disabled there; the scope-side picker is the single edit surface. */
.chip--readonly { opacity: 0.6; cursor: not-allowed; }
.chip--readonly input { pointer-events: none; }
.field--readonly .field__label .caption { font-style: italic; }

/* Interactive toggle chip — click-to-hide/click-to-show affordance for
   diagram legends + layer toolbars (ADR-0054 §Amendment 2026-07-03 —
   user-testing feedback on PR-82). Extends .chip with hover + focus +
   an "inactive" state (aria-pressed="false"): the swatch dims and the
   label goes muted so the reader sees at a glance which relations /
   layers are currently hidden. Keyboard-accessible via role="button"
   + tabindex="0". */
.chip--toggle { cursor: pointer; user-select: none;
  transition: opacity var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out); }
.chip--toggle:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.chip--toggle:focus-visible { outline: 2px solid var(--ring-focus); outline-offset: 2px; }
.chip--toggle[aria-pressed="false"] { opacity: 0.42; color: var(--fg-muted);
  background: var(--bg-surface); border-style: dashed; }
.chip--toggle[aria-disabled="true"] { cursor: not-allowed; opacity: 0.62;
  background: var(--bg-subtle); }
.chip--toggle[aria-disabled="true"]:hover { background: var(--bg-subtle); border-color: var(--border-subtle); }

/* ============================================================
   SCOPE MULTI-SELECT  (workload form — toggle buttons + fill-in cards)
   ============================================================ */
.scope-group { display: flex; flex-direction: column; gap: var(--space-2); }
.scope-group__label { font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-muted); }
.scope-toggle { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.scope-toggle__btn { display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 36px; padding: 0 var(--space-3);
  border: 1px solid var(--border-default); border-radius: var(--radius-pill);
  background: var(--bg-surface); color: var(--fg-body);
  font-family: var(--font-ui); font-weight: var(--fw-medium); font-size: var(--fs-small);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out); }
.scope-toggle__btn .icon { width: var(--icon-sm); height: var(--icon-sm); color: var(--fg-muted); }
.scope-toggle__btn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.scope-toggle__btn.is-selected { background: var(--bg-brand-soft); color: var(--fg-brand);
  border-color: var(--border-brand); font-weight: var(--fw-semibold); }
.scope-toggle__btn.is-selected .icon { color: var(--fg-brand); }
.scope-fields { background: var(--bg-subtle); }

/* CovBar — segmented rollup (width via --seg inline var, the only allowed inline) */
.covbar { display: flex; height: 8px; border-radius: var(--radius-pill);
  overflow: hidden; background: var(--bg-subtle); min-width: 120px; }
.covbar__seg { width: var(--seg, 0%); }
.covbar__seg--ok      { background: var(--status-success); }
.covbar__seg--await   { background: var(--status-info); }
.covbar__seg--rework  { background: var(--status-danger); }
.covbar__seg--todo    { background: var(--clp-neutral-300); }

/* ============================================================
   CARD / SECTION  (04)
   ============================================================ */
.card { background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); }
.card__header { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle); }
.card__header h2, .card__header h3 { font-size: var(--fs-h3); }
.card__body { padding: var(--space-5); }
/* Use when a card body needs to butt directly against the table or
   footer that follows — e.g. a .stat-strip preceding a full-bleed .table. */
.card__body--flush-bottom { padding-bottom: 0; }
.card__footer { padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-subtle); background: var(--bg-subtle);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg); }

/* Stat / KPI card */
.stat { background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); display: flex; flex-direction: column;
  gap: var(--space-1); }
.stat__label { display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-small); color: var(--fg-muted); font-weight: var(--fw-medium); }
.stat__label .icon { width: var(--icon-sm); height: var(--icon-sm); }
.stat__value { font-size: var(--fs-stat); font-weight: var(--fw-semibold);
  color: var(--fg-primary); line-height: var(--lh-tight); letter-spacing: var(--ls-tight);
  font-variant-numeric: tabular-nums; }
.stat__sub { font-size: var(--fs-caption); color: var(--fg-muted); }
.stat__sub--up   { color: var(--status-success-700); }
.stat__sub--down { color: var(--status-danger-700); }

/* Collapsible entry card — scope designer "Data" section. Native <details>
   wraps the title cluster in the header so a click on the chevron/label
   collapses the body; action buttons (Pin/Remove) live outside <summary>
   and stay reachable in either state. CSS :has() ties body visibility and
   header divider to the details' open state — no JS. */
.entry-card__header { padding-left: var(--space-3); }
.entry-toggle { flex: 1; min-width: 0; }
.entry-toggle__summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-2);
  margin: calc(-1 * var(--space-2));
  border-radius: var(--radius-md);
}
.entry-toggle__summary::-webkit-details-marker { display: none; }
.entry-toggle__summary:hover { background: var(--bg-hover); }
.entry-toggle__chevron { color: var(--fg-muted); flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out); }
.entry-toggle:not([open]) .entry-toggle__chevron { transform: rotate(-90deg); }
.entry-toggle__label { font-weight: var(--fw-semibold); color: var(--fg-primary); }
.entry-card:has(> .entry-card__header > .entry-toggle:not([open])) .entry-card__body {
  display: none;
}
.entry-card:has(> .entry-card__header > .entry-toggle:not([open])) > .entry-card__header {
  border-bottom: 0;
}

/* Collapsible follow-up field card — same shape as .entry-toggle but scoped
   to per-field <details class="field-toggle">. Without this the native marker
   ▶ shows alongside the custom chevron and the body never actually collapses. */
.field-toggle { flex: 1; min-width: 0; }
.field-toggle__summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-2);
  margin: calc(-1 * var(--space-2));
  border-radius: var(--radius-md);
}
.field-toggle__summary::-webkit-details-marker { display: none; }
.field-toggle__summary:hover { background: var(--bg-hover); }
.field-toggle__chevron { color: var(--fg-muted); flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out); }
.field-toggle:not([open]) .field-toggle__chevron { transform: rotate(-90deg); }
[data-field-card]:has(> .field-card__header > .field-toggle:not([open])) .field-card__body {
  display: none;
}
[data-field-card]:has(> .field-card__header > .field-toggle:not([open])) > .field-card__header {
  border-bottom: 0;
}

/* Compose-new disclosures — when the details is open the "Add another…"
   summary button visually disappears (otherwise it duplicates the composer's
   own header) but stays in the tab order + a11y tree via sr-only-style clip,
   so keyboard users retain the native <summary> collapse affordance even
   when JS-wired Cancel is unavailable. */
.add-entry[open] > summary,
.add-field[open] > summary {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  border: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}
.add-field__card { margin-top: var(--space-3); }
.add-entry__card { margin-top: var(--space-3); }

/* ============================================================
   READING-ROW  (04 — novel component, ADR-0058 §UI Decisions)
   Expandable Control row for the framework "reading view". The framework
   detail's Controls card renders one <details class="reading-row"> per
   Control so a framework reads like the policy document it represents.
   Collapsed, the summary reads ≈ a table row (leading chevron + section-
   label chip + control title). Expanded, the body shows the pinned
   requirement text as plain paragraphs at a constrained reading measure +
   reading line-height — no Markdown, no truncation. Native <details>
   disclosure = zero JS; an expand-all toggle (phase 03) just flips the
   [open] attribute on every row. Variants: normative (with text, default),
   --empty (Section / no requirement text yet → a muted note in the body),
   --withdrawn (dimmed title; template adds a neutral .pill--neutral per the
   status canon). Status colour lives on the pill, never on the row itself. */
.reading-row {
  /* Reading line length. Component-local convenience constant (ch measure),
     same pattern as .figure-help's --fh-max-inline — there is no framework
     token for a text measure. */
  --reading-row-measure: 68ch;
  border-bottom: 1px solid var(--border-subtle);
}
.reading-row:last-child { border-bottom: 0; }

.reading-row__summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  /* Indent by tree depth so children sit under their parent section (same
     --space-6-per-level rhythm as .controltree). --depth is a data-driven
     custom property set on the row (0 = top-level). */
  padding-block: var(--space-3);
  padding-inline: calc(var(--space-4) + var(--depth, 0) * var(--space-6)) var(--space-4);
}
.reading-row__summary::-webkit-details-marker { display: none; }
.reading-row__summary:hover { background: var(--bg-hover); }
/* Full-row summary: keep the focus ring inside the row instead of the global
   2px outset (which would overflow the card edge). */
.reading-row__summary:focus-visible { outline-offset: -2px; }

.reading-row__chevron {
  color: var(--fg-muted); flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease-out);
}
.reading-row[open] > .reading-row__summary .reading-row__chevron {
  transform: rotate(90deg);
}

/* Section-label chip (e.g. "Art. 5", "A.5.1") — neutral, monospaced, never
   a status tint. */
.reading-row__label {
  flex-shrink: 0;
  font-family: var(--font-mono); font-size: var(--fs-caption);
  color: var(--fg-muted); background: var(--bg-subtle);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
  padding: 2px var(--space-2); white-space: nowrap;
}
.reading-row__title {
  min-width: 0; color: var(--fg-primary);
  font-size: var(--fs-small); font-weight: var(--fw-medium);
}
.reading-row--withdrawn .reading-row__title { color: var(--fg-muted); }

/* Body indents under the title (past the chevron + gap) so requirement text
   lines up with the control title, not the disclosure marker. The same
   per-depth indent as the summary keeps the text block aligned under its
   own label when the row is nested. */
.reading-row__body {
  padding-block: 0 var(--space-4);
  padding-inline: calc(var(--space-4) + var(--icon-sm) + var(--space-3) + var(--depth, 0) * var(--space-6)) var(--space-4);
}
.reading-row__body p {
  max-inline-size: var(--reading-row-measure);
  font-size: var(--fs-body); line-height: var(--lh-loose); color: var(--fg-body);
}
.reading-row__body p + p { margin-top: var(--space-3); }
/* --empty variant: a Section (title-only) or a Control with no published
   text yet. The body carries a single muted note instead of prose. */
.reading-row__note { font-size: var(--fs-small); color: var(--fg-muted); }

/* ============================================================
   FIGURE-HELP  (04 — novel component, ADR-0035 §UI Decisions)
   Captioned illustration block for help/process content. Consistent
   framing across the six process SVGs authored in F16 Phase 04.
   Real <figure> semantics; inline <svg> preferred; <img> falls back
   to its own substantive alt= text on 404 (never a broken-image icon).
   ============================================================ */
.figure-help {
  --fh-max-inline: 640px;
  display: flex; flex-direction: column; gap: var(--space-3);
  align-items: center;
  max-inline-size: var(--fh-max-inline);
  margin-inline: auto;
  padding: var(--space-4) var(--space-5);
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
.figure-help > svg,
.figure-help > img,
.figure-help > .figure-help__zoom > img {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
  color: var(--fg-primary);
}
/* Zoom-into-new-tab link that wraps the <img>. The tray is narrow, so
   the inline diagram is often unreadable — clicking the figure opens
   the raw SVG in a new tab where the browser scales it to the viewport
   for detail reading. No JS, no lightbox library — the browser is the
   viewer. Reduced-motion safe (no scale transitions).

   Sizing: `.figure-help` is a flex column with `align-items: center`.
   A naive `<a>{img}</a>` collapses to 0 width — the anchor's intrinsic
   width waits on the img, and the img with `max-inline-size: 100%`
   waits on the anchor. `align-self: stretch` breaks the loop by
   giving the anchor the flex container's cross-axis width (i.e. the
   figure's inline size, up to fh-max-inline). The img then resolves
   its `max-inline-size: 100%` against that concrete width. */
.figure-help__zoom {
  display: block;
  align-self: stretch;
  cursor: zoom-in;
  border-radius: var(--radius-md);
  transition: box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.figure-help__zoom:hover { box-shadow: 0 0 0 1px var(--border-brand); }
.figure-help__zoom:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--ring-focus);
}
@media (prefers-reduced-motion: reduce) {
  .figure-help__zoom { transition: none; }
}

/* ============================================================
   FIGURE LIGHTBOX  (F16 UX follow-up)
   Full-viewport overlay that enlarges a process diagram in place.
   Rendered at the body/shell level (outside the helptray, whose
   `transform` establishes a containing block that would clip a
   nested `position: fixed` overlay). Activation is CSS-only via
   `:target`: the `.figure-help__zoom` link inside the tray points
   at `#lightbox-<slug>` which matches the overlay id and reveals
   it. The close button targets `#lightbox-close`, a bogus fragment
   no element carries — `:target` stops matching, all overlays hide.
   ============================================================ */
.figure-lightbox {
  display: none;
  position: fixed; inset: 0;
  /* --z-dialog (401) covers the helptray; the lightbox has to render
     ABOVE the tray. --z-toast (500) is the next tier in tokens.css
     and is only ever occupied by transient notifications, so reusing
     it for a user-invoked modal is safe (they never co-occur). */
  z-index: var(--z-toast);
  padding: var(--space-6);
  align-items: center; justify-content: center;
}
.figure-lightbox:target { display: flex; }
.figure-lightbox__backdrop {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.72);
  cursor: zoom-out;
}
.figure-lightbox__content {
  position: relative;
  display: flex; flex-direction: column; gap: var(--space-3);
  align-items: center;
  /* Explicit width (not just max) so the content box doesn't shrink to
     the SVG's natural size — the whole point is to enlarge a small
     inline diagram. 90vw on narrow, 1200px on wide. */
  inline-size: min(90vw, 1200px);
  max-block-size: 90vh;
  padding: var(--space-5) var(--space-6);
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.figure-lightbox__close {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-end: var(--space-3);
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: 36px; block-size: 36px;
  color: var(--fg-muted);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.figure-lightbox__close:hover {
  color: var(--fg-primary);
  border-color: var(--border-strong);
  background: var(--bg-hover);
}
.figure-lightbox__close:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--ring-focus);
}
.figure-lightbox__img {
  display: block;
  /* Fill the content-box's explicit width (min(90vw,1200px)). SVGs
     scale losslessly, so upscaling from natural 300×133 to 1200×… is
     the whole point of the lightbox. Cap block-size so a tall diagram
     doesn't push the caption off screen. */
  inline-size: 100%;
  block-size: auto;
  max-block-size: calc(90vh - 8rem);
  object-fit: contain;
  color: var(--fg-primary);
}
.figure-lightbox__caption {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--fg-body);
  text-align: center;
}
@media (prefers-reduced-motion: reduce) {
  .figure-lightbox__close { transition: none; }
}
/* The <img> fallback: on a broken URL the browser paints the alt text
   in-flow (no icon). Keep it legible: match the caption's rhythm and
   colour so the fallback still reads as an accessible caption block. */
.figure-help > img {
  min-block-size: var(--space-8);
  font-size: var(--fs-small);
  color: var(--fg-muted);
  font-style: italic;
  text-align: center;
}
.figure-help > figcaption {
  font-size: var(--fs-small);
  color: var(--fg-body);
  line-height: var(--lh-snug);
  text-align: center;
  max-inline-size: 56ch;
}
.figure-help__legend {
  display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-4);
  justify-content: center; align-items: center;
  font-size: var(--fs-caption); color: var(--fg-muted);
  line-height: var(--lh-snug);
}
.figure-help__legend > * {
  display: inline-flex; align-items: center; gap: var(--space-2);
}
/* Data-driven swatch: colour comes from the caller as
   `style="--fh-swatch: var(--status-info)"` (R2 exception — the same
   `style="--…"` custom-property pattern used by CovBar/control-map). */
.figure-help__swatch {
  inline-size: 12px; block-size: 12px;
  border-radius: var(--radius-sm);
  background: var(--fh-swatch, var(--fg-muted));
  border: 1px solid color-mix(in srgb, var(--fh-swatch, var(--fg-muted)) 60%, var(--clp-neutral-900));
  flex: none;
}

/* ============================================================
   SEARCH-RESULT  (04 — F16 Phase 02; ADR-0036 + ADR-0035)
   One row = one hit inside the /search results page and the future
   typeahead partial. The whole row is the target (no nested link).
   `--faq` variant is the FAQ-kind hit whose href deep-links into the
   Help Tray without a full-page nav.
   ============================================================ */
.search-result {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  color: var(--fg-primary);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.search-result + .search-result { margin-block-start: var(--space-2); }
.search-result:hover {
  background: var(--bg-hover);
  border-color: var(--border-default);
}
.search-result:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--ring-focus);
  border-color: var(--border-brand);
}
.search-result__chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--status-neutral-bg);
  color: var(--status-neutral);
  border: 1px solid var(--border-default);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  text-transform: uppercase; letter-spacing: var(--ls-wide);
  flex: none;
  grid-column: 1;
  grid-row: 1;
}
.search-result__title {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--fs-body); font-weight: var(--fw-semibold);
  color: var(--fg-primary);
  line-height: var(--lh-snug);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.search-result__snippet {
  grid-column: 2;
  grid-row: 2;
  font-size: var(--fs-small);
  color: var(--fg-muted);
  line-height: var(--lh-snug);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-width: 0;
}
.search-result__snippet mark {
  background: var(--status-warning-bg);
  color: inherit;
  padding: 0 2px;
  border-radius: var(--radius-sm);
  font-weight: var(--fw-medium);
}
/* --faq: FAQ-kind hit. Chip stays neutral (HANDOFF F16 Phase 02 d1);
   the variant modifier exists so future kinds can layer their own tint
   without touching the base rules. */
.search-result--faq .search-result__chip {
  background: var(--status-neutral-bg);
  color: var(--status-neutral);
}

/* ============================================================
   TABLE + TOOLBAR + PAGINATION  (04)
   ============================================================ */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.toolbar__search { flex: 1 1 260px; }

.table-wrap { background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); overflow-x: auto;
  /* Right-edge gradient = scroll affordance. background-attachment:local pins the
     gradient to the scroll viewport so it slides off once scrolled all the way right. */
  background-image: linear-gradient(to left, var(--bg-surface), transparent 28px);
  background-repeat: no-repeat;
  background-position: right top;
  background-attachment: local; }
/* An open row-action menu must escape the wrapper's overflow clip, otherwise the
   panel is cut off by the rounded corners instead of layering above the table. */
.table-wrap:has(details.menu[open]) { overflow: visible; }
/* …and it must layer ABOVE the action controls of the following rows. Each
   actions cell is a stacking context pinned at z-index:1 (the row-lift rule
   below), so an open panel is trapped at level 1 and later rows — also level 1,
   later in DOM — paint over it. While a menu is open, lift its whole cell to the
   dropdown tier so the panel floats above every following row's controls. */
.table td.table__actions:has(details.menu[open]) { z-index: var(--z-dropdown); }
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.table th, .table td { text-align: left; padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle); vertical-align: middle; }
.table thead th { font-size: var(--fs-caption); text-transform: uppercase;
  letter-spacing: var(--ls-wide); color: var(--fg-muted); font-weight: var(--fw-medium);
  background: transparent; padding-block: var(--space-2);
  border-bottom: 1px solid var(--border-default); }
/* Sticky head: opt-in for long, full-page tables (NOT inside short cards). */
.table--sticky thead th { position: sticky; top: var(--layout-topbar-h);
  z-index: var(--z-sticky); }
.table thead th a { color: inherit; display: inline-flex; align-items: center; gap: var(--space-1); }
.table thead th a:hover { color: var(--fg-body); }
.table thead th a .icon { width: 13px; height: 13px; opacity: .7; }
.table tbody tr { transition: background var(--dur-fast) var(--ease-out); position: relative; }
.table tbody tr:hover { background: var(--bg-hover); }
.table tbody tr:last-child td { border-bottom: 0; }
/* v2: zebra is demoted to a whisper — hairline row borders carry the
   rhythm; the stripe survives only as a near-invisible tint. */
.table--zebra tbody tr:nth-child(even) { background: var(--clp-neutral-25); }
.table--zebra tbody tr:nth-child(even):hover { background: var(--bg-hover); }
/* "You are here" — a row targeted by a #fragment (e.g. after a form POST
   redirects to …#cv-<id>) gets a soft brand-tinted background so the reader's
   eye lands on the row the action affected. Neutral/informational, not a status
   signal; wins over the zebra tint. CSS-only (:target) — no JS. */
.table tbody tr:target,
.table--zebra tbody tr:nth-child(even):target { background: var(--bg-muted); }
.table tbody tr:target:hover,
.table--zebra tbody tr:nth-child(even):target:hover { background: var(--bg-muted); }
.table__rowlink { color: var(--fg-primary); font-weight: var(--fw-semibold); }
.table__rowlink:hover { color: var(--fg-brand); }
/* Phase A: the row-link anchor extends to cover the whole row via a position:
   absolute overlay. Cells that hold their own interactive content (action menu,
   secondary buttons, chip-style links) lift above the overlay with z-index:1. */
.table__rowlink::after { content: ""; position: absolute; inset: 0; cursor: pointer; }
.table__rowlink:focus-visible { outline: 2px solid var(--ring-focus); outline-offset: 2px;
  border-radius: var(--radius-sm); }
.table td.table__actions,
.table td .menu,
.table td .icon-btn,
.table td .btn,
.table td .chip[href],
.table td a:not(.table__rowlink) { position: relative; z-index: 1; }
.table__actions { text-align: right; width: 1%; white-space: nowrap; }
.table__num { text-align: right; font-variant-numeric: tabular-nums; }
/* Evidence-cell links (proofs table): a raw URL would stretch the column and
   shove the Actions cell out of view. Bound each link and truncate its text
   with an ellipsis; the full URL stays reachable via the title tooltip, and the
   external-link icon never shrinks. A provided label renders instead of the URL
   (template-side); truncation is the safety net for long raw URLs. */
.evidence-link { display: inline-flex; align-items: center; gap: var(--space-1);
  max-width: 11rem; vertical-align: top; }
.evidence-link .icon { flex: none; }
.evidence-link__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Evidence-cell reason: the proof's plain-language justification, shown above
   the links/documents. Muted + 2-line clamped so it never dominates the row;
   the full text stays reachable via the title tooltip. */
.evidence-reason { margin: 0 0 var(--space-1); max-width: 16rem;
  font-size: var(--fs-caption); color: var(--fg-muted); line-height: var(--lh-snug);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  line-clamp: 2; overflow: hidden; }

.pagination { display: flex; align-items: center; gap: var(--space-1); }
.pagination a, .pagination span { min-width: 36px; min-height: 36px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: var(--radius-md);
  color: var(--fg-body); font-size: var(--fs-small); font-weight: var(--fw-medium); }
.pagination a:hover { background: var(--bg-hover); }
.pagination a[aria-current="page"] { background: var(--bg-action); color: var(--fg-on-action); }
.pagination .is-disabled { color: var(--clp-neutral-300); pointer-events: none; }

/* ============================================================
   MENU  (row actions / identity) — native <details>, no JS
   ============================================================ */
.menu { position: relative; display: inline-block; }
.menu > summary { list-style: none; cursor: pointer; display: inline-flex; }
.menu > summary::-webkit-details-marker { display: none; }
.menu__panel { position: absolute; right: 0; top: calc(100% + var(--space-1));
  min-width: 200px; background: var(--bg-surface); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md); padding: var(--space-1);
  z-index: var(--z-dropdown); }
.menu__item { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  color: var(--fg-body); font-size: var(--fs-small); width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left; }
.menu__item .icon { width: var(--icon-sm); height: var(--icon-sm); color: var(--fg-muted); }
.menu__item:hover { background: var(--bg-hover); color: var(--fg-primary); }
.menu__item--danger { color: var(--status-danger-700); }
.menu__item--danger .icon { color: var(--status-danger-700); }
.menu__sep { height: 1px; background: var(--border-subtle); margin: var(--space-1) 0; }
/* Small forms nested inside a menu (e.g. Move up / Move down POST buttons).
   Strips the default form margin so the inner button aligns with sibling
   .menu__item anchors. */
.menu__form { margin: 0; padding: 0; }
.menu__header { display: flex; flex-direction: column; padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border-subtle); margin-bottom: var(--space-1); }
.menu__header strong { font-size: var(--fs-small); }
/* A muted helper line inside a menu panel — explains *why* a particular
   action is the one shown (e.g. "this control requires Control-Owner review").
   Sits below the action it qualifies; never interactive. */
.menu__hint { padding: var(--space-1) var(--space-3) var(--space-2);
  font-size: var(--fs-caption); color: var(--fg-muted); margin: 0; max-width: 240px;
  white-space: normal; }
.menu__header small { color: var(--fg-muted); }

/* Identity trigger in topbar */
.identity { display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1);
  border-radius: var(--radius-pill); border: 1px solid transparent; }
.identity:hover { background: var(--bg-hover); }
.avatar { width: 30px; height: 30px; border-radius: var(--radius-pill); flex: none;
  background: var(--bg-brand-soft); color: var(--fg-brand);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-caption); font-weight: var(--fw-bold); }
.identity__name { font-size: var(--fs-small); font-weight: var(--fw-medium); }

/* ============================================================
   FORM FIELD + WIZARD  (04)
   ============================================================ */
/* The HTML `hidden` attribute must beat the component's own display rule
   (e.g. `.field { display: flex }`); without this, [hidden] elements stay
   visible whenever a class sets display explicitly. */
[hidden] { display: none !important; }
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field__label { font-weight: var(--fw-semibold); font-size: var(--fs-small); color: var(--fg-primary); }
.field__label .req { color: var(--status-danger); }
.field__control { width: 100%; min-height: 36px; padding: var(--space-2) var(--space-3);
  background: var(--bg-surface); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); font-size: var(--fs-small);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out); }
.field__control:focus-visible { border-color: var(--border-brand); outline: none;
  box-shadow: 0 0 0 3px var(--ring-focus); }
.field__control[disabled] { background: var(--bg-subtle); cursor: not-allowed; }
textarea.field__control { min-height: 88px; padding: var(--space-3); }
/* Tag-category filter (Phase 7.4.d) — narrow, in-cluster search input.
   Hidden by default via [hidden]; JS removes the attribute to enable it. */
.tag-filter { max-width: 22rem; min-height: 32px;
  padding: var(--space-1) var(--space-2); font-size: var(--fs-small); }
.field__help  { font-size: var(--fs-caption); color: var(--fg-muted); }
.field__error { font-size: var(--fs-caption); color: var(--status-danger-700);
  display: flex; align-items: center; gap: var(--space-1); }
.field__error .icon { width: 14px; height: 14px; }
.field.has-error .field__control { border-color: var(--status-danger); }

/* Links repeater (Phase 7.4.b) — URL-first inline row.
   One link per line: a leading icon anchors the row, then a compact type
   select, the dominant URL, an optional label and an icon-only delete. The
   grid keeps the controls on a single row (a bare .field__control would
   otherwise stack at width:100%) and reflows URL-first on narrow widths. */
.link-row { display: grid; align-items: center; gap: var(--space-2);
  grid-template-columns: auto minmax(7rem, 8rem) minmax(0, 2fr) minmax(0, 1.2fr) auto;
  grid-template-areas: "icon type url label del";
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); background: var(--bg-surface); }
.link-row__icon   { grid-area: icon; color: var(--fg-muted); }
.link-row__type   { grid-area: type; }
.link-row__url    { grid-area: url; }
.link-row__label  { grid-area: label; }
.link-row__delete { grid-area: del; color: var(--fg-muted); }
.link-row__delete:hover { color: var(--status-danger); }
.link-row .field__control { min-width: 0; }
.link-row.has-error { border-color: var(--status-danger); }
@media (max-width: 720px) {
  .link-row { grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "icon url   del"
                         "icon type  type"
                         "icon label label"; }
}

/* Builder rows (approval rules) — compact inline rows of selects for the scope
   builder (dimension/operator/value) and the approver picker (one approver per
   row). A grid keeps controls on one line (a bare .field__control is width:100%
   and would otherwise stack) and reflows on narrow widths. Mirrors .link-row. */
.builder-rows { display: flex; flex-direction: column; gap: var(--space-3); }
.builder-row { display: grid; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); background: var(--bg-surface); }
.builder-row .field__control { min-width: 0; }
.builder-row__del { grid-area: del; color: var(--fg-muted); }
.builder-row__del:hover { color: var(--status-danger); }
.builder-head { display: grid; gap: var(--space-2); padding: 0 var(--space-3);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--fg-muted); }
.builder-row--scope, .builder-head--scope {
  grid-template-columns: minmax(0, 2fr) minmax(6rem, 8rem) minmax(0, 2fr) auto;
  grid-template-areas: "dim op val del"; }
.builder-row--approver {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "who del"; }
.builder-row__dim { grid-area: dim; } .builder-row__op { grid-area: op; }
.builder-row__val { grid-area: val; } .builder-row__who { grid-area: who; }
@media (max-width: 720px) {
  .builder-head--scope { display: none; }
  .builder-row--scope { grid-template-columns: 1fr auto;
    grid-template-areas: "dim del" "op op" "val val"; }
}

/* Tag-category value rows — one controlled-vocabulary value per row: a native
   color picker doubles as the swatch, the label dominates, an icon-only delete
   removes the row. Mirrors .link-row / .builder-row. */
.value-rows { display: flex; flex-direction: column; gap: var(--space-2); }
.value-row { display: grid; align-items: center; gap: var(--space-3);
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); background: var(--bg-surface); }
.value-row .field__control { min-width: 0; }
.value-row__color { inline-size: 2.25rem; block-size: 2.25rem; flex: none; padding: 2px;
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  background: var(--bg-surface); cursor: pointer; }
.value-row__color::-webkit-color-swatch-wrapper { padding: 0; }
.value-row__color::-webkit-color-swatch { border: none; border-radius: var(--radius-sm); }
.value-row__color::-moz-color-swatch { border: none; border-radius: var(--radius-sm); }
.value-row__del { color: var(--fg-muted); }
.value-row__del:hover { color: var(--status-danger); }

.steps { display: flex; gap: var(--space-4); align-items: center; flex-wrap: wrap; }
.steps__item { display: flex; align-items: center; gap: var(--space-2);
  color: var(--fg-muted); font-size: var(--fs-small); }
.steps__item[aria-current="step"] { color: var(--fg-brand); font-weight: var(--fw-semibold); }
.steps__num { width: 24px; height: 24px; border-radius: var(--radius-pill);
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-default); font-size: var(--fs-caption); }
.steps__item[aria-current="step"] .steps__num { background: var(--bg-brand);
  color: var(--fg-on-brand); border-color: transparent; }
.steps__item--done .steps__num { background: var(--status-success);
  color: var(--clp-white); border-color: transparent; }

.form-footer { display: flex; justify-content: space-between; gap: var(--space-4);
  padding-top: var(--space-5); border-top: 1px solid var(--border-subtle); }

/* ============================================================
   FORM-PAGE LAYOUT  (F1 — constrained shell, F2 — sections, F3 — grid)
   The canonical form-page body sits in a max-width column (NOT the
   .detail-grid two-column shell). Inside a single .card, sections
   are separated by thin rules using .form-section; rows use a
   two-column .form-grid where simple inputs sit side-by-side and
   wider rows span the full width via .form-grid__full.
   Replaces the native <fieldset>/<legend> pattern entirely.
   ============================================================ */
.form-shell { max-width: 720px; margin-inline: auto; width: 100%;
  display: flex; flex-direction: column; gap: var(--space-6); }

.form-section + .form-section { border-top: 1px solid var(--border-subtle);
  padding-top: var(--space-6); margin-top: var(--space-6); }
.form-section__head { margin-bottom: var(--space-4); }
.form-section__head h3 { font-size: var(--fs-h4); font-weight: var(--fw-semibold);
  color: var(--fg-primary); }
.form-section__head p { font-size: var(--fs-caption); color: var(--fg-muted);
  margin-top: 2px; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-5) var(--space-4); align-items: start; }
.form-grid__full { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   MATRIX  (F4 — checkbox/property grid)
   Replaces inline nested-checkbox patterns ("Workloads □ required")
   with a real labeled matrix: column headers + one row per target,
   each row carrying a name on the left and a property cell on the
   right. Use for "Applies to (and whether required)", role-pillar
   matrices, permission tables — anywhere two boolean axes meet.
   ============================================================ */
.matrix { display: grid; grid-template-columns: minmax(0, 1fr) 120px;
  column-gap: var(--space-4); row-gap: var(--space-3); align-items: center;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.matrix__head { font-size: var(--fs-caption); color: var(--fg-muted);
  font-weight: var(--fw-semibold); letter-spacing: var(--ls-wide);
  text-transform: uppercase; padding: var(--space-2) var(--space-4);
  background: var(--bg-subtle); }
.matrix__head:first-child { border-top-left-radius: var(--radius-md); }
.matrix__head:nth-child(2) { border-top-right-radius: var(--radius-md);
  text-align: center; }
.matrix__row { display: contents; }
.matrix__name { padding: var(--space-3) var(--space-4); font-size: var(--fs-small);
  color: var(--fg-primary); border-top: 1px solid var(--border-subtle); }
.matrix__name .caption { display: block; color: var(--fg-muted);
  font-size: var(--fs-caption); margin-top: 2px; }
.matrix__toggle { padding: var(--space-3); text-align: center;
  border-top: 1px solid var(--border-subtle); }
.matrix__toggle--unavailable { color: var(--fg-muted); font-style: italic;
  font-size: var(--fs-caption); }

/* ============================================================
   LIST-PAGE FILTER-BAR  (L2 — active-filter chips below the toolbar)
   Rendered as a horizontal row of removable chips below `.toolbar`,
   one per applied filter, plus a "Clear all" link. Visible summary
   of what's currently restricting the visible rows.
   ============================================================ */
.filter-bar { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2); padding: var(--space-3) 0; }
.filter-bar__label { font-size: var(--fs-caption); color: var(--fg-muted);
  margin-right: var(--space-2); }
.filter-chip { display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 4px var(--space-2) 4px var(--space-3); border-radius: var(--radius-pill);
  background: var(--bg-brand-soft); color: var(--fg-brand);
  font-size: var(--fs-caption); font-weight: var(--fw-medium); }
.filter-chip__remove { all: unset; cursor: pointer; padding: 2px;
  border-radius: var(--radius-pill); display: inline-flex; }
.filter-chip__remove:hover { background: var(--bg-hover); }
.filter-chip__remove .icon { width: 12px; height: 12px; }

/* ============================================================
   DIALOG + FLASH + EMPTY/ERROR/SKELETON  (04)
   ============================================================ */
.dialog { border: 0; border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
  padding: 0; max-width: 480px; width: calc(100% - var(--space-8));
  margin: auto; }  /* re-assert UA centering (global reset zeroes margins) */
.dialog::backdrop { background: rgba(15,20,25,.5); backdrop-filter: blur(2px); }
.dialog__body { padding: var(--space-6); }
.dialog__title { display: flex; align-items: center; gap: var(--space-2);
  margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold);
  color: var(--fg-primary); }
.dialog__actions { display: flex; justify-content: flex-end; gap: var(--space-3);
  margin-top: var(--space-6); }

/* Form-shaped dialog — wider, with header/body/footer rows and a scrolling
   body so longer forms (capture-proof) stay usable on shorter viewports. */
.dialog--form { max-width: 720px; }
.dialog--form .dialog__body { padding: var(--space-5) var(--space-6);
  max-height: calc(100vh - 14rem); overflow-y: auto; }
.dialog__header { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6) var(--space-4);
  border-bottom: 1px solid var(--border-subtle); }
.dialog__header-text { min-width: 0; }
.dialog__footer { display: flex; justify-content: flex-end; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border-subtle); background: var(--bg-subtle);
  border-bottom-left-radius: var(--radius-xl);
  border-bottom-right-radius: var(--radius-xl); }

/* Capture-proof form — risk acceptance reveals only when the assessment is
   set to risk_accepted, driven by CSS :has() over the select's checked
   option. No JS needed for the toggle. */
.capture-form .capture-risk { display: none; }
.capture-form:has(.capture-assessment option[value="risk_accepted"]:checked) .capture-risk {
  display: flex; flex-direction: column; gap: var(--space-4);
  padding: var(--space-4); border: 1px solid var(--status-warning);
  border-radius: var(--radius-md);
  background: var(--status-warning-bg);
}
/* Symmetric hide: when the assessment is Risk accepted, the Evidence fieldset
   collapses so the modal shows ONLY the risk-acceptance block. Any other
   assessment keeps Evidence visible (default) and Risk hidden (rule above). */
.capture-form:has(.capture-assessment option[value="risk_accepted"]:checked) .capture-evidence {
  display: none;
}
.capture-form .capture-risk > legend,
.capture-form .capture-evidence > legend {
  padding: 0 var(--space-2); font-size: var(--fs-caption);
  font-weight: var(--fw-semibold); letter-spacing: var(--ls-wide);
  text-transform: uppercase; color: var(--fg-muted);
}
.capture-form .capture-risk > legend { color: var(--status-warning-700); }

/* Evidence group — one section per modality (Links / Documents) inside the
   Evidence fieldset. The group head spans the full width with a title and
   an optional inline action ("Add link"); the rows below share the
   .evidence-row visual rhythm so links and files read as the same shape. */
.evidence-group { display: flex; flex-direction: column; gap: var(--space-2); }
.evidence-group__head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); }
.evidence-group__title { margin: 0; font-size: var(--fs-small);
  font-weight: var(--fw-semibold); color: var(--fg-primary); }
.evidence-rows { display: flex; flex-direction: column; gap: var(--space-2); }

/* Single evidence row — a unified shape used for URL links, existing-file
   blobs and the file-picker drop target. Each row is a card with a leading
   icon, a flexible main column and a trailing remove control. The picker
   variant uses a <label> so the whole row is clickable. */
.evidence-row { display: grid; align-items: center; gap: var(--space-3);
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr) auto;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  background: var(--bg-surface);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out); }
.evidence-row:focus-within { border-color: var(--border-brand);
  box-shadow: 0 0 0 3px var(--ring-focus); }
.evidence-row__icon { width: 32px; height: 32px; border-radius: var(--radius-md);
  background: var(--bg-subtle); color: var(--fg-muted);
  display: inline-flex; align-items: center; justify-content: center; flex: none; }
.evidence-row__icon .icon { width: 16px; height: 16px; }
.evidence-row__delete { color: var(--fg-muted); }
.evidence-row__delete:hover { color: var(--status-danger); background: var(--bg-hover); }
.evidence-row .field__control { min-width: 0; }
.evidence-row.has-error { border-color: var(--status-danger); }
.evidence-row--blob { grid-template-columns: auto minmax(0, 1fr) auto; }
.evidence-row__filename { font-size: var(--fs-small); color: var(--fg-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Trailing group in a picked-file row: the size caption sits beside the remove
   control so both share the row's single trailing grid cell. */
.evidence-row__trailing { display: inline-flex; align-items: center;
  gap: var(--space-2); justify-self: end; }
.evidence-row--upload { grid-template-columns: auto minmax(0, 1fr) auto;
  cursor: pointer; border-style: dashed;
  background: var(--bg-subtle); }
.evidence-row--upload:hover { border-color: var(--border-brand); background: var(--bg-surface); }
.evidence-row__upload-label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.evidence-row__upload-label strong { font-size: var(--fs-small); color: var(--fg-primary); }
.evidence-row__upload-hint { font-size: var(--fs-caption); color: var(--fg-muted); }
.evidence-row__upload-input { display: none; }

@media (max-width: 720px) {
  .evidence-row { grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "icon main del"
                         "icon sub  sub"; }
  .evidence-row > .evidence-row__icon { grid-area: icon; }
  .evidence-row > .evidence-row__delete { grid-area: del; }
  .evidence-row > .evidence-row__url,
  .evidence-row > .evidence-row__filename,
  .evidence-row > .evidence-row__upload-label { grid-area: main; }
  .evidence-row > .evidence-row__label { grid-area: sub; }
}

/* Inline cluster — wraps a one-button form so its <form> wrapper doesn't break
   the row's horizontal cluster layout. Used by the draft actions strip. */
.cluster__inline { display: inline-flex; align-items: center; }
.cluster--split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 560px) {
  .cluster--split { grid-template-columns: 1fr; }
}

.flash { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  border: 1px solid; font-size: var(--fs-small); }
.flash .icon { flex: none; }
.flash--success { background: var(--status-success-bg); color: var(--status-success-700);
  border-color: var(--status-success-border); }
.flash--danger  { background: var(--status-danger-bg); color: var(--status-danger-700);
  border-color: var(--status-danger-border); }
.flash--warning { background: var(--status-warning-bg); color: var(--status-warning-700);
  border-color: var(--status-warning-border); }
.flash--info    { background: var(--status-info-bg); color: var(--status-info-700);
  border-color: var(--status-info-border); }

.empty-state { display: flex; flex-direction: column; align-items: center;
  text-align: center; padding: var(--space-16) var(--space-6); gap: var(--space-3);
  color: var(--fg-muted); }
.empty-state__icon { width: 56px; height: 56px; border-radius: var(--radius-pill);
  background: var(--bg-subtle); color: var(--fg-muted);
  display: inline-flex; align-items: center; justify-content: center; }
.empty-state__icon .icon { width: 28px; height: 28px; }
.empty-state__title { color: var(--fg-primary); font-size: var(--fs-h3); }
.empty-state p { max-width: 42ch; }

/* .empty-state--filter-required : the diagram-page "pick a filter" gate
   (ADR-0054 D-6). Same layout as .empty-state; the .pill--warning above
   the title makes the mandatory nature visible without changing the
   overall composition. Icon tile picks up the warning surface so the
   gate reads at a glance without shouting like an error. */
.empty-state--filter-required .empty-state__icon { background: var(--status-warning-bg);
  color: var(--status-warning-700); }

.error-state { display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-md);
  background: var(--status-danger-bg); color: var(--status-danger-700);
  border: 1px solid color-mix(in srgb, var(--status-danger) 30%, transparent); }
.error-state .icon { flex: none; margin-top: 2px; }

.skeleton { background: linear-gradient(90deg,
    var(--bg-subtle) 25%, var(--clp-neutral-150) 37%, var(--bg-subtle) 63%);
  background-size: 400% 100%; border-radius: var(--radius-md); height: 1em;
  animation: shimmer 1.4s var(--ease-in-out) infinite; }
.skeleton--row { height: 44px; margin-bottom: var(--space-2); }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* ============================================================
   FILTER-SYSTEM  (04 + ADR-0020) — field → operator → values, SSR-first
   No-JS: <form method="get"> + native <details> popovers.
   ============================================================ */
.filterbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.filterbar__spacer { flex: 1; }
/* Progressive enhancement: filterbar.js stamps `js-on` on <html>; elements
   marked js:hidden (the no-JS Apply submit) disappear once live-search runs. */
.js-on .js\:hidden { display: none !important; }
.card__header > .filterbar { flex: 1; }

/* Segmented control (quick-scope + operator switcher). No-JS:
   Links with aria-current OR <label> with hidden radio (:has). */
.segmented { display: inline-flex; background: var(--bg-subtle);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  padding: 2px; gap: 2px; }
.segmented__item { display: inline-flex; align-items: center; gap: var(--space-1);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-sm);
  font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--fg-body);
  background: transparent; border: 0; cursor: pointer; white-space: nowrap; }
.segmented__item .icon { width: var(--icon-sm); height: var(--icon-sm); }
.segmented__item:hover { color: var(--fg-primary); }
.segmented__item input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.segmented__item:focus-within { outline: 2px solid var(--ring-focus); outline-offset: 1px; }
.segmented__item[aria-current="true"], .segmented__item[aria-pressed="true"],
.segmented__item:has(input:checked) { background: var(--bg-surface);
  color: var(--fg-primary); box-shadow: var(--shadow-xs); }
.segmented__count { font-weight: var(--fw-semibold); color: var(--fg-muted); }

/* "+ Add filter" popover = .menu; per field one nested
   <details class="filter-editor">. */
.filter-add summary { list-style: none; }
.filter-add summary::-webkit-details-marker { display: none; }

.filter-editor { border-radius: var(--radius-sm); }
.filter-editor > summary { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  font-size: var(--fs-small); color: var(--fg-body); cursor: pointer; list-style: none; }
.filter-editor > summary::-webkit-details-marker { display: none; }
.filter-editor > summary .icon { width: var(--icon-sm); height: var(--icon-sm); color: var(--fg-muted); }
.filter-editor > summary:hover { background: var(--bg-hover); }
.filter-editor[open] > summary { background: var(--bg-hover);
  color: var(--fg-primary); font-weight: var(--fw-semibold); }
.filter-editor__body { padding: var(--space-3); display: flex; flex-direction: column;
  gap: var(--space-3); border-top: 1px solid var(--border-subtle); margin-top: var(--space-1); }
.filter-editor__title { font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-muted); }
.filter-editor__help { font-size: var(--fs-caption); color: var(--fg-muted); }
.filter-editor__actions { display: flex; justify-content: flex-end; gap: var(--space-2);
  padding-top: var(--space-1); }

/* Checkbox multi-select (enum fields, e.g. Owning Team) */
.checklist { display: flex; flex-direction: column; gap: 2px;
  max-height: 240px; overflow-y: auto; }
.checklist__item { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2); border-radius: var(--radius-sm); cursor: pointer;
  font-size: var(--fs-small); }
.checklist__item:hover { background: var(--bg-hover); }
.checklist__item input { width: 16px; height: 16px; flex: none; accent-color: var(--bg-action); }
.checklist__item .icon { width: var(--icon-sm); height: var(--icon-sm); color: var(--fg-muted); }

/* Token/tag input (text/tags fields). Tokens are .chip. No-JS:
   comma-separated input; JS enhancement tokenises on space/enter. */
.tokeninput { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-2); min-height: 40px; background: var(--bg-surface); }
.tokeninput:focus-within { border-color: var(--border-brand); box-shadow: 0 0 0 3px var(--ring-focus); }
.tokeninput input { flex: 1; min-width: 80px; border: 0; outline: none;
  background: transparent; font-size: var(--fs-small); padding: var(--space-1); }

/* Active filter as removable chip (brand-tinted) */
.chip--filter { background: var(--bg-brand-soft); color: var(--fg-brand);
  border-color: color-mix(in srgb, var(--clp-blue-700) 22%, transparent);
  font-weight: var(--fw-medium); }
.chip--filter strong { font-weight: var(--fw-semibold); }

/* ============================================================
   TABS  (04) — Detail page sections; No-JS: ?tab=… (R1)
   ============================================================ */
.tabs { display: flex; gap: var(--space-1); border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto; }
.tab { display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3); margin-bottom: -1px; border-bottom: 2px solid transparent;
  font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--fg-muted);
  white-space: nowrap; }
.tab .icon { width: var(--icon-sm); height: var(--icon-sm); }
.tab:hover { color: var(--fg-body); }
.tab[aria-selected="true"], .tab[aria-current="page"] { color: var(--fg-brand);
  border-bottom-color: var(--border-brand); font-weight: var(--fw-semibold); }

/* ============================================================
   RELATIONSHIP MAP  (04) — Symmetric incoming/hub/outgoing graph
   Two channels: category (--cat / --cat-bg) drives colour;
   relation type (--solid / --dashed / --dotted / --double / --super)
   drives stroke style + .control-map__rel chip caption.
   Inline CSS variables permitted on rows (R2): --cat, --cat-bg, --y.
   ============================================================ */
.control-map { display: grid;
  grid-template-columns: 1fr minmax(280px, 360px) 1fr;
  gap: var(--space-6); align-items: stretch; position: relative; margin: 0; }
.control-map__edges { position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; }
.control-map__edges path { fill: none; stroke-width: 1.5px;
  stroke: var(--cat, var(--border-strong)); opacity: 0.65;
  transition: opacity var(--dur-fast) var(--ease-out),
              stroke-width var(--dur-fast) var(--ease-out); }
.control-map__edges path.is-hover { opacity: 1; stroke-width: 2.25px; }
.control-map__edges path.control-map__edge--dashed { stroke-dasharray: 6 4; }
.control-map__edges path.control-map__edge--dotted { stroke-dasharray: 2 4; }
.control-map__edges path.control-map__edge--double { stroke-width: 2.5px; }
.control-map__edges path.control-map__edge--super  { stroke-dasharray: 10 3 2 3; }

.control-map__lhs, .control-map__rhs { position: relative; z-index: 1;
  min-height: var(--col-h, 0); }
.control-map__center { display: flex; align-items: center; justify-content: center;
  position: relative; z-index: 1; }

.control-map__hub { background: var(--bg-brand); color: var(--fg-on-brand);
  border-radius: var(--radius-lg); padding: var(--space-5) var(--space-6);
  box-shadow: var(--shadow-brand); width: 100%; max-width: 320px; }
.control-map__hub .eyebrow { color: color-mix(in srgb, var(--fg-on-brand) 70%, transparent);
  margin-bottom: var(--space-2); }
.control-map__hub h2 { font-size: var(--fs-h3); margin: 0 0 var(--space-2) 0;
  line-height: var(--lh-snug); }
.control-map__hub p { font-size: var(--fs-small); margin: 0;
  color: color-mix(in srgb, var(--fg-on-brand) 80%, transparent); }

.control-map__group-header { position: absolute; left: 0; right: 0;
  top: var(--y, 0); display: flex; align-items: baseline; gap: var(--space-2);
  height: 24px; font-size: var(--fs-caption); letter-spacing: var(--ls-wide);
  text-transform: uppercase; font-weight: var(--fw-semibold);
  color: var(--cat, var(--fg-muted)); margin: 0; }
.control-map__group-count { color: var(--fg-muted); font-weight: var(--fw-regular);
  text-transform: none; letter-spacing: var(--ls-normal); }

.control-map__row { position: absolute; left: 0; right: 0; top: var(--y, 0);
  display: block; padding: var(--space-2) var(--space-3);
  height: 56px; box-sizing: border-box; overflow: hidden;
  background: var(--bg-surface); border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--cat, var(--border-strong));
  border-radius: var(--radius-md); text-decoration: none; color: inherit;
  transition: box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out); }
.control-map__row:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.control-map__row:focus-visible { outline: 2px solid var(--ring-focus); outline-offset: 2px; }

.control-map__label { display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-mono); font-size: var(--fs-caption);
  font-weight: var(--fw-semibold); color: var(--cat, var(--fg-primary));
  background: var(--cat-bg, var(--bg-subtle));
  padding: 1px var(--space-2); border-radius: var(--radius-sm); }
.control-map__headline { display: block; margin-top: var(--space-1);
  font-size: var(--fs-small); color: var(--fg-body);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.control-map__rel { display: inline-flex; align-items: center;
  font-size: var(--fs-caption); color: var(--fg-muted); margin-left: var(--space-2);
  letter-spacing: var(--ls-normal); }

.control-map__more { position: absolute; left: 0; right: 0; top: var(--y, 0); }
.control-map__more > summary { cursor: pointer; list-style: none;
  font-size: var(--fs-caption); color: var(--fg-muted);
  padding: var(--space-1) var(--space-2); }
.control-map__more > summary::-webkit-details-marker { display: none; }
.control-map__more[open] > summary { color: var(--fg-body); }
.control-map__more > div { display: flex; flex-direction: column; gap: var(--space-2);
  margin-top: var(--space-2); }
.control-map__more > div .control-map__row { position: static; }

.control-map__empty { font-size: var(--fs-small); color: var(--fg-muted);
  text-align: center; padding: var(--space-6) var(--space-4); }

.control-map__row.is-loading { background: var(--bg-subtle); height: 44px;
  border-left-color: var(--border-default); }
.control-map__row--error { border-color: var(--status-danger);
  border-left-color: var(--status-danger); color: var(--status-danger-700); }

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

@media (max-width: 767px) {
  .control-map { grid-template-columns: 1fr; gap: var(--space-4); }
  .control-map__edges { display: none; }
  .control-map__center { order: -1; }
}

/* ============================================================
   DIAGRAM–SANKEY  (compliance-flow · ADR-0054 · v2 restyle 2026-07-05)
   Vendored D3-sankey renders the client SVG into
   .diagram--sankey__svg. Columns are DYNAMIC — d3-sankey derives
   them from graph topology (Framework → Control → … → Workload;
   flow-crossrefs push concretizing Controls into deeper columns).
   Every colour reads from the --sankey-ribbon-* categorical band
   (validated, see tokens.css §9) — no raw hex anywhere. Variants:
   --loading / --populated / --error are pass-through modifiers;
   loading + error replace the SVG region with SSR markup (D-8).
   ============================================================ */
.diagram--sankey { margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-3);
  background: var(--sankey-node-bg); }

/* Scroll shell — tall graphs scroll inside the card, never the page. */
.diagram__scroll { overflow: auto; max-height: 78vh;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--sankey-node-bg); }
.diagram--sankey__svg { display: block; width: 100%; height: auto;
  min-height: 320px; }

/* Dynamic column headers — one per topological column, uppercase micro-label. */
.diagram--sankey__svg .col-head { fill: var(--fg-muted);
  font-family: var(--font-ui); font-size: 10.5px;
  font-weight: var(--fw-semibold); letter-spacing: .08em;
  text-transform: uppercase; }

/* ---------- Nodes ----------
   Narrow vertical bars; fill = the entity's categorical slot via the
   inline --node-fill custom property. Workloads are the neutral
   terminus (slate). A hairline ink ring keeps light slots (amber,
   aqua, magenta) crisp on the white surface. */
.diagram--sankey__svg .node rect { fill: var(--node-fill, var(--clp-neutral-500));
  stroke: color-mix(in srgb, var(--clp-neutral-950) 18%, transparent);
  stroke-width: .5px; }
.diagram--sankey__svg .node--workload rect { fill: var(--clp-neutral-400); }
.diagram--sankey__svg .node:focus-visible { outline: none; }
.diagram--sankey__svg .node:focus-visible rect {
  stroke: var(--clp-blue-600); stroke-width: 2px; }
.diagram--sankey__svg .node.is-dim { opacity: .16; }

/* Node labels — ink text beside the bar, never on the series colour.
   Code tspan semibold; name + count tspans in secondary ink. */
.diagram--sankey__svg .node text { fill: var(--fg-body);
  font-family: var(--font-ui); font-size: 12px; font-weight: var(--fw-medium);
  paint-order: stroke; stroke: var(--sankey-node-bg); stroke-width: 3px;
  stroke-linejoin: round; }
.diagram--sankey__svg .node text .label-code { font-weight: var(--fw-semibold); }
.diagram--sankey__svg .node text .label-dim { fill: var(--fg-muted);
  font-weight: var(--fw-regular); }

/* ---------- Flow ribbons ----------
   Width = d3-sankey layout width (real throughput). Colour follows the
   entity (framework slot) via the inline --ribbon custom property.
   multiply-blend keeps overlapping ribbons legible on white. */
.diagram--sankey__svg path.link { fill: none;
  stroke: var(--ribbon, var(--clp-neutral-400));
  stroke-opacity: .3; mix-blend-mode: multiply;
  transition: stroke-opacity .12s ease; }
.diagram--sankey__svg path.link:hover,
.diagram--sankey__svg path.link.is-hover { stroke-opacity: .62; }
.diagram--sankey__svg path.link.is-dim { stroke-opacity: .05; }

/* Flow-crossref ribbons (concretizes / implements / derives_from) are
   first-class flows; the midpoint relation badge (below) names the
   relation kind. Slightly stronger resting opacity so a reference
   ribbon stands out against bulk framework flow. */
.diagram--sankey__svg path.link--flow-crossref { stroke-opacity: .46; }
.diagram--sankey__svg path.link--flow-crossref:hover { stroke-opacity: .74; }

/* ---------- Relation badge (midpoint of crossref ribbons + chords) ----
   Small surface-filled disc + relation initial in the relation colour —
   readable on top of any ribbon, never colour-alone (title tooltip +
   legend chip echo the full name). */
.diagram--sankey__svg .rel-badge circle { fill: var(--sankey-node-bg);
  stroke: var(--chord-color, var(--fg-muted)); stroke-width: 1.5px; }
.diagram--sankey__svg .rel-badge text { fill: var(--chord-color, var(--fg-body));
  font-family: var(--font-ui); font-size: 10px; font-weight: var(--fw-bold); }
.diagram--sankey__svg .rel-badge { pointer-events: none; }
.diagram--sankey__svg .rel-badge.is-dim { opacity: .12; }

/* ---------- Overlay chords (equivalent_to / supersedes) ----------
   Non-flow relations: thin dashed arcs, colour per relation. */
.diagram--sankey__svg path.chord { fill: none;
  stroke: var(--chord-color, var(--fg-muted));
  stroke-width: 1.5px; stroke-dasharray: 5 4; opacity: .75;
  transition: opacity .12s ease; }
.diagram--sankey__svg path.chord--equivalent_to { stroke-dasharray: 1.5 3.5;
  stroke-linecap: round; }
.diagram--sankey__svg path.chord:hover { opacity: 1; stroke-width: 2px; }
.diagram--sankey__svg path.chord.is-dim,
.diagram--sankey__svg path.chord.chord--dim { opacity: .1; }

/* ---------- Toolbar + legends ----------
   One quiet control row above the SVG: layer toggles, relation toggles,
   framework colour key. All chips reuse the v2 chip language (hairline
   border, small radius, squared). aria-pressed=false → muted + struck. */
.diagram--sankey__toolbar { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2) var(--space-3); }
.diagram--sankey__toolbar-label { color: var(--fg-muted);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  letter-spacing: .06em; text-transform: uppercase; }
.diagram--sankey__legend { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2); }
.diagram--sankey__legend-hint { flex-basis: 100%; }

/* Legend chip — colour-swatch + label. Interactive instances additionally
   carry .chip--toggle (defined with the chip family above); the swatch
   ::before reads the inline --ribbon / --chord-color custom property. */
.diagram--sankey__legend-chip { display: inline-flex; align-items: center;
  gap: var(--space-1); padding: 2px var(--space-2);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  background: var(--bg-surface); color: var(--fg-body);
  font-size: var(--fs-caption); font-weight: var(--fw-medium);
  line-height: 18px; user-select: none; }
.diagram--sankey__legend-chip::before { content: "";
  width: 10px; height: 10px; border-radius: 3px; flex: none;
  background: var(--ribbon, var(--chord-color, var(--clp-neutral-400))); }
.diagram--sankey__legend-chip[aria-pressed="false"]::before { opacity: .35; }

/* ---------- Loading skeleton (D-8) ---------- */
.diagram--sankey__skeleton { display: flex; gap: var(--space-8);
  justify-content: space-between; padding: var(--space-6);
  min-height: 320px; border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); }
.diagram--sankey__skeleton span { flex: 1; border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--bg-subtle) 25%,
    var(--bg-hover) 37%, var(--bg-subtle) 63%);
  background-size: 400% 100%; animation: shimmer 1.4s ease infinite; }

/* Source caption under the figure. */
.funnel-diagram__source { margin: 0; }

/* ============================================================
   DEFINITION LIST + BACK-LINK + NARROW CONTENT  (detail/form)
   ============================================================ */
.dl { display: grid; grid-template-columns: max-content 1fr; gap: var(--space-3) var(--space-6); }
.dl dt { color: var(--fg-muted); font-size: var(--fs-small); }
.dl dd { color: var(--fg-primary); font-size: var(--fs-small); }

.back-link { display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--fg-muted); }
.back-link .icon { width: var(--icon-sm); height: var(--icon-sm); }
.back-link:hover { color: var(--fg-body); }

.shell__content--narrow { max-width: 760px; }

/* Standalone confirmation page (no-JS fallback for the dialog, ADR-0020/R1) */
.confirm { max-width: 480px; margin-inline: auto; }

/* Standalone pre-auth / error pages: centre the single card both axes. */
.page-centered { display: grid; place-items: center; padding: var(--space-6); }
.page-centered > main { width: 100%; }
.confirm__icon { width: 48px; height: 48px; border-radius: var(--radius-pill);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--status-danger-bg); color: var(--status-danger-700); }

/* ============================================================
   DETAIL-PAGE LAYOUT  (D17 — two-column shell)
   The canonical detail-page body splits into a wide main column
   and a narrow meta-sidebar. Single-value metadata (type, owner,
   dates, scope chips, tags, links) belongs in the sidebar via
   .meta-list. Wide content (descriptions, tables, history) stays
   in main. Collapses to one column below 960px.
   ============================================================ */
.detail-grid { display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(280px, 30%, 360px);
  gap: var(--space-6); align-items: start; }
.detail-grid__main,
.detail-grid__aside { display: flex; flex-direction: column;
  gap: var(--space-6); min-width: 0; }
@media (max-width: 960px) {
  .detail-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   META-LIST  (D18 — compact label/value pairs for meta-sidebar)
   Replaces .dl in narrow sidebars where the label-above-value
   pattern reads better than label-left-of-value. Use .dl for
   wide bodies; use .meta-list for narrow sidebars and footers.
   ============================================================ */
.meta-list { display: flex; flex-direction: column; gap: var(--space-4); }
.meta-list__row { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.meta-list__label { font-size: var(--fs-caption); color: var(--fg-muted);
  font-weight: var(--fw-medium); }
.meta-list__value { font-size: var(--fs-small); color: var(--fg-primary);
  word-break: break-word; }
.meta-list__value .chip { vertical-align: middle; }
/* Secondary caption line below the value (e.g. "in 68 days" under a date).
   First-class slot so detail templates don't reach for inline style overrides. */
.meta-list__hint { font-size: var(--fs-caption); color: var(--fg-muted);
  margin-top: 2px; }

/* ============================================================
   STAT-STRIP  (D22 — three-up numerical rollup)
   Used inside a Status / Compliance card header to surface
   aggregate counts (fulfilled / open / overdue, pass / fail).
   Replaces a row of three pills that read as "noise" when all
   values are zero. Paired with .covbar above and a detail
   table below in the same card.
   ============================================================ */
.stat-strip { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3); margin-top: var(--space-3); }
.stat-strip__item { padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--bg-surface);
  display: flex; flex-direction: column; gap: 2px; }
.stat-strip__num { font-size: var(--fs-h3); font-weight: var(--fw-semibold);
  line-height: 1; font-variant-numeric: tabular-nums; }
.stat-strip__num--ok     { color: var(--status-success-700); }
.stat-strip__num--warn   { color: var(--status-warning-700); }
.stat-strip__num--danger { color: var(--status-danger-700); }
.stat-strip__num--info   { color: var(--status-info-700); }
.stat-strip__label { font-size: var(--fs-caption); color: var(--fg-muted); }

/* ============================================================
   FRAMEWORK DETAIL — CONTROL TREE / LINKS / CALLOUT
   ADR-0004 (+ amendment 2026-06-16). Law-style hierarchical view + the
   external-links section. Mirrors the design framework canon — uses existing
   tokens only, no new colors.
   ============================================================ */

/* Right-side meta dl on the framework header (Owning team / Version / …). */
.framework-meta { grid-template-columns: max-content max-content;
  align-self: start; row-gap: var(--space-2); }

/* Info callout banner above the tabs (e.g. "Read-only — global framework
   maintained by CLOUDPILOTS"). Same shape as a card but with brand-soft fill
   to read as informational, not interactive. */
.callout { display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); background: var(--bg-subtle); }
.callout .icon { color: var(--fg-muted); flex: 0 0 auto;
  width: var(--icon-md); height: var(--icon-md); margin-top: 2px; }
.callout--info { background: var(--bg-brand-soft); border-color: var(--bg-brand-soft); }
.callout--info .icon { color: var(--fg-brand); }
/* Warning variant — same shape as --info, painted with the status-warning
   tokens. Used e.g. on the invite success page when SMTP dispatch failed
   (F15 Phase 03, ADR-0056 §UI Decisions), where the operator still has a
   valid copy-link block but needs to hand the URL over out-of-band. */
.callout--warning { background: var(--status-warning-bg); border-color: var(--status-warning-bg);
  color: var(--status-warning-700); }
.callout--warning .icon { color: var(--status-warning-700); }

/* Spread the two ends of a cluster onto either side (caption left, filter
   chip-group right) — paired with .cluster's existing flex layout. */
.cluster--between { justify-content: space-between; width: 100%; }
/* Right-aligns the cluster's children — used inside table action cells where
   we render an action button alongside a secondary link (e.g. History). */
.cluster--end { justify-content: flex-end; }

/* ============================================================
   TIMELINE  (audit trail: Proof history, future cycle events)
   Vertical list of events; left rail with a status dot per row.
   ============================================================ */
.timeline { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: var(--space-4); }
.timeline__item { display: grid; grid-template-columns: var(--space-5) 1fr;
  gap: var(--space-3); position: relative; }
.timeline__item:not(:last-child)::before {
  content: ""; position: absolute; left: calc(var(--space-3) - 1px);
  top: var(--space-5); bottom: calc(-1 * var(--space-4));
  width: 2px; background: var(--border-subtle); }
.timeline__dot { width: 12px; height: 12px; border-radius: 50%;
  background: var(--clp-neutral-300); margin-top: var(--space-1);
  justify-self: center; box-shadow: 0 0 0 3px var(--bg-surface); }
.timeline__dot[data-kind="success"]  { background: var(--status-success-700); }
.timeline__dot[data-kind="warning"]  { background: var(--status-warning-700); }
.timeline__dot[data-kind="info"]     { background: var(--status-info-700); }
.timeline__dot[data-kind="neutral"]  { background: var(--clp-neutral-400); }
.timeline__title { font-weight: var(--fw-semibold); }
.timeline__meta  { font-size: var(--fs-caption); color: var(--fg-muted); }
.timeline__comment { margin: var(--space-2) 0 0; padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--border-default); background: var(--bg-subtle);
  border-radius: var(--radius-sm); font-style: italic; color: var(--fg-body); }

/* One Proof revision card on the history page — a quiet container that
   stacks header / description / timeline / collapsed details. */
.proof-rev { padding: var(--space-4); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); background: var(--bg-surface); }
.proof-rev__details > summary { cursor: pointer; color: var(--fg-brand);
  font-size: var(--fs-small); }
.proof-rev__details[open] > summary { margin-bottom: var(--space-3); }

/* Hierarchical control tree on the framework Controls tab. <ol> for the
   semantics; row layout = 3-column grid (section_label | title | refs). */
.controltree { list-style: none; padding: 0; margin: 0;
  border-top: 1px solid var(--border-subtle); }
.controltree__row { display: grid; grid-template-columns: minmax(96px, 120px) 1fr auto;
  align-items: baseline; column-gap: var(--space-4);
  padding: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--border-subtle); }
.controltree__row:hover { background: var(--bg-hover); }
.controltree__label { display: contents; color: inherit; }
.controltree__section { color: var(--fg-brand); font-weight: var(--fw-medium);
  font-size: var(--fs-small); text-align: right; }
.controltree__title { color: var(--fg-primary); font-size: var(--fs-small); }
.controltree__refs { display: inline-flex; align-items: center; gap: var(--space-1);
  color: var(--fg-link); font-size: var(--fs-small); }
.controltree__refs .icon { width: var(--icon-sm); height: var(--icon-sm); }
/* Indent by depth — children move their section column to the right; the
   title column shifts with them so the visual gutter stays clean. */
.controltree__row--d1 { padding-left: var(--space-6); }
.controltree__row--d2 { padding-left: calc(var(--space-6) * 2); }
.controltree__row--d3 { padding-left: calc(var(--space-6) * 3); }
.controltree__row--d4 { padding-left: calc(var(--space-6) * 4); }
.controltree__row--d5 { padding-left: calc(var(--space-6) * 5); }
/* Top-level rows are headings (Article 5) — bolder section label. */
.controltree__row--d0 .controltree__section { color: var(--fg-brand);
  font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.controltree__row--d0 .controltree__title { font-weight: var(--fw-semibold); }

/* Workload list — platform-child indent. A child workload (parent_workload_id
   → an is_platform=true parent) indents under its platform so admins can see
   at a glance which workloads run on which platform. Depth is capped at 1 in
   practice (platforms cannot nest), but the pattern mirrors .controltree so
   deeper trees could plug in with the same modifier.

   Branch is INLINE, not absolutely positioned, on purpose: promoting the
   name <td> to a positioned ancestor (position:relative) would re-anchor
   the .table__rowlink::after row-click overlay to the cell and silently
   break whole-row clicks on every child row. */
.workload-name--d1 { padding-left: var(--space-6); }
.workload-name__branch { display: inline-block; vertical-align: middle;
  width: var(--space-3); height: 0.6em;
  margin-left: calc(-1 * var(--space-4));
  margin-right: var(--space-2);
  border-left: 1px solid var(--border-default);
  border-bottom: 1px solid var(--border-default);
  border-bottom-left-radius: var(--radius-sm);
  pointer-events: none; }

/* Link list on the Links tab — icon + label + host, host right-aligned. */
.linklist { list-style: none; padding: 0; margin: 0; }
.linklist__row { display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; column-gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle); }
.linklist__row:last-child { border-bottom: 0; }
.linklist__row > .icon { width: var(--icon-sm); height: var(--icon-sm);
  color: var(--fg-brand); }
.linklist__label { color: var(--fg-link); font-size: var(--fs-small); }
.linklist__host { color: var(--fg-muted); }
/* Two-column row inside the edit-details slidepanel: URL + label inputs. */
.linklist__editor { display: grid; grid-template-columns: 2fr 1fr;
  gap: var(--space-3); }

/* ============================================================
   RESPONSIVE  (03)
   ============================================================ */
@media (max-width: 1023px) {
  :root { --layout-gutter: var(--space-6); }
  .shell { grid-template-columns: var(--layout-sidebar-w-collapsed) 1fr; }
  .sidebar__link span, .sidebar__count, .sidebar__section,
  .sidebar__footer > :not(.icon), .sidebar__brand span { display: none; }
  .sidebar__brand { justify-content: center; padding-inline: 0; }
  .sidebar__link { justify-content: center; padding-inline: 0; }
  .sidebar__footer { justify-content: center; }
}
/* =========================================================
   Help Tray (ADR-0031, component contract in design-framework/04-components.md)
   Right-side slide-in panel. Modal-less (aria-modal="false"): the underlying
   page stays mounted and interactive. Tokens only — no bespoke hue.
   ========================================================= */
:root {
  --layout-helptray-w: 480px;
}
.helptray {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: var(--layout-helptray-w); max-width: 100vw;
  background: var(--bg-surface);
  border-left: 1px solid var(--border-default);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  z-index: var(--z-dialog);
  transform: translateX(100%);
  transition: transform var(--dur-base) var(--ease-out);
  visibility: hidden;
}
.helptray.is-open { transform: translateX(0); visibility: visible; }
.helptray__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  /* Same defensive stacking as .helptray__tabs — see note there. */
  background: var(--bg-surface);
  position: relative; z-index: 1;
  /* Never shrink: without this, opening a very tall <details> in the body
     (flex-basis:auto = content-size, huge) makes flex share the deficit
     across every flex-shrink:1 sibling — the header would compress to a
     few pixels the moment body content exceeds one viewport. */
  flex-shrink: 0;
}
.helptray__brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.helptray__chip {
  font-size: var(--fs-caption); letter-spacing: var(--ls-wide);
  text-transform: uppercase; font-weight: var(--fw-semibold);
  color: var(--fg-muted);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  background: var(--bg-subtle);
}
.helptray__title {
  margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold);
  color: var(--fg-primary); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.helptray__tabs {
  display: flex; gap: var(--space-1);
  padding: var(--space-2) var(--space-3) 0;
  border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto;
  /* Chrome (fixed dialog + flex column + long scrolling body) sometimes
     paints body content over the tab strip when the compositor promotes
     the scroll area to its own layer. Explicit background + own stacking
     context guarantee the tabs stay visually on top of the scrolling
     body, regardless of GPU-composited paint order. */
  background: var(--bg-surface);
  position: relative; z-index: 1;
  /* Same non-shrink guarantee as .helptray__header — a very tall body
     would otherwise compress the tab strip to a few pixels via the
     default flex-shrink:1. */
  flex-shrink: 0;
}
.helptray__tab {
  padding: var(--space-2) var(--space-3);
  border-bottom: 2px solid transparent;
  color: var(--fg-body); text-decoration: none;
  font-size: var(--fs-small); font-weight: var(--fw-medium);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.helptray__tab:hover { color: var(--fg-primary); }
.helptray__tab.is-active {
  color: var(--fg-brand); border-bottom-color: var(--border-brand);
}
.helptray__body {
  flex: 1 1 auto; overflow-y: auto;
  padding: var(--space-5);
  /* Flexbox min-content default (auto = content-height) prevents flex-shrink
     from working; without min-block-size:0 a body taller than the parent
     bubbles the overflow up into the header/tabs strip in Chrome and Safari.
     See CSS-Tricks "Preventing a Grid Blowout" — same failure mode for flex. */
  min-block-size: 0;
}
.helptray__footer {
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--fs-small); color: var(--fg-muted);
}
.help__eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-2);
  text-transform: uppercase; letter-spacing: var(--ls-wide);
  font-size: var(--fs-caption); color: var(--fg-muted);
  margin: 0;
}
.help__flow { row-gap: var(--space-2); }
.help__flow-sep { color: var(--fg-muted); }
.help__card {
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
.help__card > summary {
  list-style: none;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  font-weight: var(--fw-semibold); color: var(--fg-primary);
}
.help__card > summary::-webkit-details-marker { display: none; }
.help__card > summary::after {
  content: ""; flex: 1;
}
.help__card[open] > summary { border-bottom: 1px solid var(--border-subtle); }
.help__card > p,
.help__card > ul {
  margin: 0; padding: var(--space-3) var(--space-4) var(--space-4);
  color: var(--fg-body);
}
.help__example-list {
  list-style: none; margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.help__example-list > li {
  display: grid; grid-template-columns: minmax(7.5rem, max-content) 1fr;
  gap: var(--space-3); align-items: start;
}
.help__uc-list {
  list-style: disc inside; margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-1);
}
.help__footer { margin: 0; }

/* FAQ entry (F16 Phase 03/04/05) — extensions of the `.help__card` frame
   for the shared card shape across the Glossary / Processes /
   Documentation tabs. Selectors chain on `.help__card` so specificity
   beats the shared `.help__card > p` body rule above. */
.help__card > p.faq-entry__seealso {
  border-top: 1px solid var(--border-subtle);
  color: var(--fg-muted);
  font-size: var(--fs-small);
}
.help__card > p.faq-entry__seealso a {
  color: var(--fg-body);
  text-decoration: underline;
  text-decoration-color: var(--border-default);
  text-underline-offset: 2px;
}
.help__card > p.faq-entry__seealso a:hover { color: var(--fg-primary); }

/* Figure-help placement inside an FAQ card — matches the horizontal
   padding of the sibling `<p>` rule so the diagram lines up under the
   paragraph above it. Vertical padding is tighter because the figure
   carries its own internal padding. */
.help__card > figure {
  margin: 0;
  padding: var(--space-2) var(--space-4);
}

/* FAQ entry inline table (F16 Phase 05) — small structured tables inside
   the Documentation cards (role × verbs, setting × default × effect,
   scope field type × example). Direct child of `.help__card`, matches
   the horizontal padding rhythm of the sibling `<p>` rule via an
   explicit margin. Thin bottom borders, no zebra stripes — the tray is
   480 px on `lg` and full viewport on `sm`, so horizontal density is
   expensive. */
.help__card > table.faq-entry__table {
  width: auto;
  margin: var(--space-2) var(--space-4) var(--space-3);
  border-collapse: collapse;
  font-size: var(--fs-small);
  color: var(--fg-body);
}
.help__card > table.faq-entry__table th,
.help__card > table.faq-entry__table td {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: top;
}
.help__card > table.faq-entry__table thead th {
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  color: var(--fg-muted);
  font-weight: var(--fw-semibold);
  background: transparent;
}
.help__card > table.faq-entry__table tbody tr:last-child td {
  border-bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
  .helptray { transition: none; }
}
@media (max-width: 767px) {
  .helptray { width: 100vw; }
}

/* =========================================================
   Slide-in panel — CSS-only :target right-side drawer.
   Used by the framework-detail snapshot rows to surface control
   version status without leaving the page. Modal-less: the
   underlying page stays interactive. Reuses the helptray width
   token + tokens-only styling (no bespoke colours).
   ========================================================= */
.slidepanel {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: var(--layout-helptray-w); max-width: 100vw;
  background: var(--bg-surface);
  border-left: 1px solid var(--border-default);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  z-index: var(--z-dialog);
  transform: translateX(100%);
  transition: transform var(--dur-base) var(--ease-out);
  visibility: hidden;
}
.slidepanel:target { transform: translateX(0); visibility: visible; }
/* JS-driven variant (funnel peek): :target cannot be cleared via
   history.replaceState, so script-opened panels toggle aria-hidden and
   this selector mirrors the :target open state. */
.slidepanel[aria-hidden="false"] { transform: translateX(0); visibility: visible; }
/* JS force-close of a :target-opened peek (control-peek outside-click / Esc):
   history.replaceState cannot reliably clear :target, so the script sets
   aria-hidden="true" and this selector — placed after :target so equal
   specificity resolves in its favour — re-asserts the hidden state. Cleared on
   the next hashchange so :target governs the freshly opened panel again. */
.slidepanel[aria-hidden="true"] { transform: translateX(100%); visibility: hidden; }
.slidepanel__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.slidepanel__brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
/* Vertical brand: stacks the chip cluster above the title (vs. the default
   horizontal). Use when the slidepanel needs to surface eyebrow + chips + title
   on three rows instead of one. */
.slidepanel__brand--stacked { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
/* Footer spacer — pushes the primary/danger button to the right of the cluster.
   Same pattern as .topbar__spacer / .filterbar__spacer. */
.slidepanel__footer-spacer { flex: 1; }
.slidepanel__chip {
  font-size: var(--fs-caption); letter-spacing: var(--ls-wide);
  text-transform: uppercase; font-weight: var(--fw-semibold);
  color: var(--fg-muted);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  background: var(--bg-subtle);
}
.slidepanel__title {
  margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold);
  color: var(--fg-primary); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.slidepanel__body {
  flex: 1 1 auto; overflow-y: auto;
  padding: var(--space-5);
}
.slidepanel__footer {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
@media (prefers-reduced-motion: reduce) {
  .slidepanel { transition: none; }
}
@media (max-width: 767px) {
  .slidepanel { width: 100vw; }
}

/* =========================================================
   Modal — CSS-only :target centered dialog with backdrop.
   Used for mutation / confirmation actions (create successor
   draft, publish, withdraw, edit details) that should sit
   above the page rather than slide in from the side. Same
   anatomy as .slidepanel (header / brand+chip / body / footer)
   so markup translates 1:1, but rendered as a centered modal.
   Click-out close via the .modal__backdrop link behind the panel.
   ========================================================= */
.modal {
  position: fixed; inset: 0;
  z-index: var(--z-dialog);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  background: rgba(15,20,25,.5);
  backdrop-filter: blur(2px);
  visibility: hidden; opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
.modal:target { visibility: visible; opacity: 1; }
.modal__backdrop {
  position: absolute; inset: 0;
  cursor: default;
}
.modal__panel {
  position: relative;
  width: 100%; max-width: 480px;
  max-height: calc(100vh - var(--space-8));
  background: var(--bg-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  transform: translateY(8px);
  transition: transform var(--dur-base) var(--ease-out);
}
.modal__panel--wide { max-width: 640px; }
.modal:target .modal__panel { transform: none; }
.modal__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.modal__brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.modal__chip {
  font-size: var(--fs-caption); letter-spacing: var(--ls-wide);
  text-transform: uppercase; font-weight: var(--fw-semibold);
  color: var(--fg-muted);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  background: var(--bg-subtle);
}
.modal__title {
  margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold);
  color: var(--fg-primary); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis;
}
.modal__body {
  flex: 1 1 auto; overflow-y: auto;
  padding: var(--space-5);
}
.modal__footer {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-subtle);
  border-bottom-left-radius: var(--radius-xl);
  border-bottom-right-radius: var(--radius-xl);
}
@media (prefers-reduced-motion: reduce) {
  .modal, .modal__panel { transition: none; }
}
@media (max-width: 767px) {
  .modal { padding: var(--space-4); }
  .modal__panel { max-height: calc(100vh - var(--space-6)); }
}

@media (max-width: 767px) {
  .shell { grid-template-columns: 1fr; }
  .shell__sidebar { position: fixed; left: -100%; width: var(--layout-sidebar-w);
    box-shadow: var(--shadow-lg); transition: left var(--dur-base) var(--ease-out); }
  .shell__sidebar.is-open { left: 0; }
  .shell__sidebar.is-open .sidebar__link span, .shell__sidebar.is-open .sidebar__count,
  .shell__sidebar.is-open .sidebar__section, .shell__sidebar.is-open .sidebar__brand span,
  .shell__sidebar.is-open .sidebar__footer > :not(.icon) { display: revert; }
  .shell__sidebar.is-open .sidebar__link { justify-content: flex-start; padding-inline: var(--space-3); }
  .shell__sidebar.is-open .sidebar__brand { justify-content: flex-start; padding-inline: var(--space-5); }
  .icon-btn.topbar__menu-btn { display: inline-flex; }
  .searchbox { min-width: 0; }
  /* Tables collapse to cards — pages add data-label per cell */
  .table thead { display: none; }
  .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
  .table tr { border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
    margin-bottom: var(--space-3); padding: var(--space-2); }
  .table td { border: 0; padding: var(--space-2); display: flex;
    justify-content: space-between; gap: var(--space-4); }
  .table td::before { content: attr(data-label); font-size: var(--fs-caption);
    text-transform: uppercase; letter-spacing: var(--ls-wide); color: var(--fg-muted);
    font-weight: var(--fw-semibold); }
}


/* Lifecycle action row (Control detail) — pairs the action with a short
   rationale next to it so disabled buttons explain themselves (UC-09 step 4,
   ADR-0003). The withdraw variant stacks a labelled reason field with its
   submit, instead of squeezing the input between unrelated buttons. */
.action-row { display: flex; align-items: flex-start; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5); }
.action-row > form { flex: 0 0 auto; }
.action-row__hint { margin: 0; flex: 1 1 280px;
  font-size: var(--fs-small); color: var(--fg-muted); }
.action-row--withdraw { padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle); }
.action-row--withdraw > form { min-width: 260px; max-width: 380px; }

/* Side-by-side draft editor (UC-09 §3) — predecessor on the left (read-only),
   editable draft on the right. Falls back to a single column on narrow widths
   so mobile editors can still work, even if the comparison is then stacked. */
.compare-grid { display: grid; gap: var(--space-5);
  grid-template-columns: 1fr 1fr; align-items: start; }
.compare-pane { display: flex; flex-direction: column; gap: var(--space-3);
  background: var(--bg-subtle); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: var(--space-4); min-width: 0; }
.compare-pane > form,
form.compare-pane { background: var(--bg-surface); }
.compare-pane__header { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); }
.compare-pane__text { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--fs-small); line-height: var(--lh-tight); }
@media (max-width: 900px) {
  .compare-grid { grid-template-columns: 1fr; }
}

/* ControlVersion diff (Phase 6l, UC-09 §3) — two-column side-by-side rendering.
   Tokens reused from the status canon (success/danger/warning) so add/delete/
   change rows pick up the global color language without a new archetype. */
.diff { table-layout: fixed; }
.diff td, .diff th { vertical-align: top; }
.diff__text { margin: 0; white-space: pre-wrap; word-break: break-word;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--fs-small); line-height: var(--lh-tight); }
.diff tr[data-diff-kind="add"]    td:nth-child(3),
.diff tr[data-diff-kind="add"]    td:nth-child(4) { background: var(--status-success-bg); }
.diff tr[data-diff-kind="delete"] td:nth-child(1),
.diff tr[data-diff-kind="delete"] td:nth-child(2) { background: var(--status-danger-bg); }
.diff tr[data-diff-kind="change"] td { background: var(--status-warning-bg); }
.diff tr[data-diff-kind="equal"]  td { color: var(--fg-muted); }

/* ============================================================
   Tree-Editor archetype (UC-17, ADR-0027)
   Mirrors docs/specs/design-framework/reference/tree-editor.html.
   Precision SaaS: per-row affordances reveal on hover/focus
   so the resting tree stays quiet. Sortable.js binds via
   [data-tree-list] and posts SUC-FE-06/07 routes on drop.
   ============================================================ */
.editor-layout { display: grid; grid-template-columns: minmax(320px, 38%) 1fr;
  gap: var(--space-5); align-items: start; }
@media (max-width: 960px) { .editor-layout { grid-template-columns: 1fr; } }

/* Two-zone tree-editor grid (F19 — ADR-0057 §1, amended 2026-07-07):
   Catalog | Structure tree. The former third zone (node detail) is a
   right-side .slidepanel peek (position:fixed → not a grid item), so the grid
   carries only the two in-flow zones. Collapses to a single column as width
   tightens; the catalog is a collapsible <details> so it never crowds the tree
   on narrow viewports. Each zone owns min-width:0 so long codes/titles wrap or
   ellipsize inside their track instead of blowing out the grid. */
.tree-editor { display: grid;
  grid-template-columns: minmax(280px, 34%) minmax(0, 1fr);
  gap: var(--space-4); align-items: start; }
.tree-editor > * { min-width: 0; }
@media (max-width: 900px) {
  .tree-editor { grid-template-columns: minmax(240px, 40%) minmax(0, 1fr); }
}
@media (max-width: 720px) { .tree-editor { grid-template-columns: 1fr; } }

.tree-pane__head { display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle); }
.tree-pane__head h2 { margin: 0; font-size: var(--fs-small); flex: 1; }
.tree-pane__body { padding: var(--space-3) var(--space-2); }

.tree-search { position: relative; flex: 1; max-width: 220px; }
.tree-search input { width: 100%; padding: 0.375rem 0.5rem 0.375rem 1.875rem;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--bg-surface); font-size: var(--fs-small); }
.tree-search .icon { position: absolute; left: 0.5rem; top: 50%;
  transform: translateY(-50%); color: var(--fg-muted); pointer-events: none; }

.tree { list-style: none; padding: 0; margin: 0; }
.tree ul[role="group"] { list-style: none; padding-left: 1.125rem; margin: 0;
  position: relative; }
.tree ul[role="group"]::before { content: ""; position: absolute;
  left: 0.5rem; top: 0.25rem; bottom: 0.25rem; width: 1px;
  background: var(--border-subtle); }

.tree__row { display: grid;
  grid-template-columns: 18px 18px 1fr auto auto;
  align-items: center; gap: 0.375rem;
  padding: 0.25rem 0.5rem; border-radius: var(--radius-md);
  min-height: 32px; position: relative;
  transition: background 80ms ease; }
.tree__row:hover { background: var(--bg-hover); }
.tree__row:focus-within { background: var(--bg-hover);
  box-shadow: 0 0 0 2px var(--ring-focus) inset; }
.tree li[aria-selected="true"] > .tree__row { background: var(--bg-brand-soft);
  box-shadow: inset 2px 0 0 var(--fg-brand); }

.tree__drag { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; color: var(--border-strong);
  cursor: grab; opacity: 0;
  transition: opacity 120ms ease, color 120ms ease; }
.tree__row:hover .tree__drag,
.tree__row:focus-within .tree__drag,
.tree li[aria-selected="true"] > .tree__row .tree__drag { opacity: 1; }
.tree__drag:hover { color: var(--fg-primary); }
.tree__drag:active { cursor: grabbing; }

/* Chevron: a functional collapse/expand <button> on parent nodes (F19 amended
   2026-07-07, second round) — reset the native button chrome, rotate the icon
   when collapsed. Leaf nodes render an inert <span> spacer (aria-hidden). JS
   toggles [hidden] on the controlled group + flips aria-expanded. */
.tree__chevron { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; color: var(--fg-muted);
  border-radius: var(--radius-sm);
  background: none; border: 0; padding: 0; cursor: pointer; }
.tree__chevron:hover { background: var(--bg-surface); color: var(--fg-primary); }
.tree__chevron:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring-focus); }
.tree__chevron[aria-hidden="true"] { visibility: hidden; cursor: default; }
.tree__chevron .icon { transition: transform var(--dur-fast) var(--ease-out); }
.tree__chevron[aria-expanded="false"] .icon { transform: rotate(-90deg); }

.tree__label { display: inline-flex; gap: 0.5rem; align-items: baseline;
  color: var(--fg-primary); text-decoration: none; min-width: 0;
  padding: 0.1875rem 0; }
.tree__label .label { color: var(--fg-muted); font-variant-numeric: tabular-nums;
  font-size: 0.75rem; letter-spacing: 0.01em; font-weight: 500;
  white-space: nowrap; }
.tree__label .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree__label:hover .title { text-decoration: underline; }

.tree__badge { font-size: 0.6875rem; padding: 0.0625rem 0.375rem; }

.tree__menu { opacity: 0; transition: opacity 120ms ease; }
.tree__row:hover .tree__menu,
.tree__row:focus-within .tree__menu,
.tree li[aria-selected="true"] > .tree__row .tree__menu { opacity: 1; }
.tree__menu summary { list-style: none; }
.tree__menu summary::-webkit-details-marker { display: none; }

/* ---- Tree-DnD (ADR-0057 §3) — progressive-enhancement drop feedback -------
   Two mutually-exclusive drop signals, keyed to the pointer's vertical
   position over the hovered row (proven by the F19 phase-04 styleguide spike,
   binding for the editor rebuild):
     • top/bottom edge band  → SIBLING at that position: a thin accent
       insertion line (.tree__drop-indicator).
     • middle band           → CHILD of that node: whole-row highlight
       (li.is-drop-target).
   Feedback is never a layout shift of sibling rows (the indicator is a
   zero-height overlay) and never persists after drop/cancel — the drag-end
   handler strips every class below. */

/* Sortable.js drag-source visuals — mirror the CatalogPanel item so a
   dragged entry reads identically in the catalog and over the tree. */
.tree__row.sortable-ghost { background: var(--bg-brand-soft); opacity: 0.6; }
.tree__row.sortable-chosen { box-shadow: 0 0 0 1px var(--fg-brand); }
.tree__row.sortable-drag { background: var(--bg-surface);
  box-shadow: var(--shadow-lg);
  border-radius: var(--radius-md); }

/* While a drag is in flight the resting hover tint is suppressed so the only
   visible feedback is the explicit sibling line / child highlight. */
.tree.is-dragging { cursor: grabbing; }
.tree.is-dragging .tree__row:hover { background: transparent; }

/* CHILD drop — the hovered node highlights; it will receive the dragged item
   as a child. */
.tree li.is-drop-target > .tree__row { background: var(--bg-brand-soft);
  box-shadow: inset 0 0 0 1px var(--fg-brand); }

/* SIBLING drop — a 2px accent insertion line at the drop position. Rendered
   as a zero-height <li> inserted into the target group <ul> before/after the
   hovered row, so the <ul>'s own padding indents it to the correct depth. The
   optional data-driven --indent (0-based level) nudges a floating variant that
   is not nested inside the target <ul>; it defaults to 0 (R2). */
.tree__drop-indicator { list-style: none; height: 0; margin: 0;
  position: relative; }
.tree__drop-indicator::before { content: ""; position: absolute;
  top: -1px; height: 2px; border-radius: var(--radius-pill);
  left: calc(var(--indent, 0) * 1.125rem + 0.5rem); right: var(--space-2);
  background: var(--fg-brand); }
.tree__drop-indicator::after { content: ""; position: absolute;
  top: -3px; width: 6px; height: 6px; border-radius: 50%;
  left: calc(var(--indent, 0) * 1.125rem + 0.125rem);
  background: var(--fg-brand); }

.tree-add { margin: 0.5rem 0.5rem 0 0.5rem;
  border-top: 1px solid var(--border-subtle); padding-top: 0.5rem; }
.tree-add > details > summary { list-style: none; cursor: pointer;
  padding: 0.375rem 0.5rem; border-radius: var(--radius-md);
  color: var(--fg-muted); display: inline-flex; align-items: center;
  gap: 0.375rem; font-size: var(--fs-small); }
.tree-add > details > summary::-webkit-details-marker { display: none; }
.tree-add > details > summary:hover { background: var(--bg-subtle);
  color: var(--fg-primary); }
.tree-add__placement { display: flex; gap: 0.5rem;
  padding: 0.25rem 0.5rem; font-size: 0.75rem; color: var(--fg-muted); }
.tree-add__placement label { display: inline-flex; gap: 0.25rem;
  align-items: center; cursor: pointer; }
.tree-add__form { display: grid; grid-template-columns: 6rem 1fr auto;
  gap: 0.375rem; padding: 0.5rem; margin-top: 0.25rem;
  background: var(--bg-subtle); border-radius: var(--radius-md); }
.tree-add__form input { padding: 0.375rem 0.5rem;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
  background: var(--bg-surface); font-size: var(--fs-small); }

.empty-tree { padding: 2rem 1rem; text-align: center;
  border: 1px dashed var(--border-subtle); border-radius: var(--radius-lg);
  background: var(--bg-subtle); color: var(--fg-muted); }
.empty-tree__icon { display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; background: var(--bg-surface);
  border-radius: 50%; color: var(--fg-muted); margin-bottom: 0.75rem; }

/* Node-detail peek breadcrumb (F19, ADR-0057 §1 amended 2026-07-07): the
   parent-label trail inside the read-only .slidepanel#node-detail. The former
   docked-pane selectors (.node-detail__title / .node-detail__meta) are retired
   — the peek reuses .slidepanel__title and standard pills/chips. */
.node-detail__crumb { display: inline-flex; gap: 0.375rem; align-items: center;
  color: var(--fg-muted); font-size: var(--fs-small); margin-bottom: 0.375rem; }
.node-detail__crumb a { color: inherit; }
.node-detail__crumb .dot { color: var(--border-strong); }

/* Read-only normative text inside the peek (F19 amended 2026-07-07, second
   round): reading-friendly paragraphs, comfortable line-height and a capped
   measure. The .slidepanel__body already scrolls, so long requirement text
   scrolls within the panel. */
.node-detail__text { color: var(--fg-primary); font-size: var(--fs-small);
  line-height: var(--lh-loose); max-width: 60ch; }
.node-detail__text p { margin: 0 0 var(--space-3); }
.node-detail__text p:last-child { margin-bottom: 0; }

.node-footer { display: flex; justify-content: space-between; gap: 0.75rem;
  padding-top: var(--space-3); border-top: 1px solid var(--border-subtle);
  margin-top: var(--space-4); }
.node-footer__danger { color: var(--fg-muted); font-size: var(--fs-small);
  text-decoration: none; display: inline-flex; align-items: center;
  gap: 0.25rem; }
.node-footer__danger:hover { color: var(--status-danger-700); }
/* ============================================================
   Tree editor — full-width toolbar above the tree
   The framework editor is a pure tree-shape editor: rich text
   editing lives on the Control detail page. So the previous
   two-pane split is a single column; .editor-layout below
   overrides the earlier grid definition when used inside
   .editor-layout--single (belt-and-braces; the template also
   uses a plain .card wrapper).
   ============================================================ */
.editor-layout--single { display: block; }

/* Toolbar above the tree — hosts "+ Add top-level control" popover.
   Right-aligned to keep the tree's left edge quiet. Sits inside
   .tree-pane__body above the <ul>. */
.tree-toolbar { display: flex; justify-content: flex-end;
  gap: var(--space-2); padding: 0 var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-3); }

/* Menu group inside a per-row <details class="menu"> — a caption-
   sized muted heading above a pair of related items ("Add sibling"
   → Attach existing / Create new). Not a native heading; purely
   presentational. */
.menu__group-label { padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--fs-caption); color: var(--fg-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
  font-weight: var(--fw-semibold); }

/* Attach modal — search results list. Rendered by the debounced
   fetch to /frameworks/:id/membership/attach-search. Each row is a
   button so keyboard focus works; .is-selected marks the chosen row
   until submit. Bounded height + internal scroll so a wide result
   set doesn't push the modal footer offscreen. */
.attach-results { border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); background: var(--bg-surface);
  max-height: 18rem; overflow-y: auto; }
.attach-results:empty { display: none; }
.attach-results p.muted { padding: var(--space-3) var(--space-4); margin: 0; }
.attach-results__list { list-style: none; padding: 0; margin: 0; }
.attach-results__item { display: grid;
  grid-template-columns: minmax(6rem, auto) 1fr auto;
  gap: var(--space-3); align-items: baseline;
  width: 100%; padding: var(--space-2) var(--space-3);
  background: transparent; border: 0; border-bottom: 1px solid var(--border-subtle);
  cursor: pointer; text-align: left; font-size: var(--fs-small);
  color: var(--fg-body); }
.attach-results__item:last-child { border-bottom: 0; }
.attach-results__item:hover { background: var(--bg-hover); }
.attach-results__item:focus-visible { outline: none;
  background: var(--bg-hover);
  box-shadow: inset 0 0 0 2px var(--ring-focus); }
.attach-results__item.is-selected { background: var(--bg-brand-soft);
  box-shadow: inset 2px 0 0 var(--fg-brand); }
.attach-results__code { font-family: var(--font-mono, ui-monospace, monospace);
  color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.attach-results__title { min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* ============================================================
   CATALOG-PANEL  (F19 — ADR-0057 §2; tree-editor v2, zone 1)
   Collapsible drag-source list of the Controls in the editor's
   scope pool that are NOT yet members of this Framework draft.
   Composes existing catalog pieces only — .pill (lifecycle status),
   .chip (current-version badge), the .searchbox anatomy (GET search,
   ADR-0020), .btn (+ New Control) and .empty-state (every Control
   attached). A native <details> drives the collapse — no JS. In
   phase 06 Sortable.js binds .catalog-panel__list as the cross-list
   drag source; the .sortable-* visuals mirror .tree__row so a
   dragged entry reads identically in the catalog and over the tree.
   Tokens only; no UUIDs rendered (code + title carry identity).
   ============================================================ */
/* Sticky + independently scrollable (F19 amended 2026-07-07, second round):
   the catalog can be very long, so it pins under the topbar with a
   viewport-bound max-height; the header/toolbar stay visible while the list
   region scrolls on its own (see .catalog-panel__list below). This keeps the
   tree in view for dropping. Sortable's auto-scroll during a drag is preserved
   because the list is the nearest scrollable ancestor of a dragged item. */
.catalog-panel { display: flex; flex-direction: column; min-width: 0;
  overflow: hidden;  /* clip the summary border against the card radius */
  position: sticky; top: calc(var(--layout-topbar-h) + var(--space-4));
  max-height: calc(100vh - var(--layout-topbar-h) - var(--space-8)); }

/* <summary> IS the header bar — clicking it collapses the body (no JS). */
.catalog-panel__collapse { display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; }
.catalog-panel__header { display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
  list-style: none; cursor: pointer; user-select: none; }
.catalog-panel__header::-webkit-details-marker { display: none; }
.catalog-panel__header:hover { background: var(--bg-hover); }
.catalog-panel__header:focus-visible { outline: none;
  box-shadow: inset 0 0 0 2px var(--ring-focus); }
.catalog-panel__chevron { color: var(--fg-muted); flex: none;
  transition: transform var(--dur-fast) var(--ease-out); }
.catalog-panel__title { flex: 1; min-width: 0; margin: 0;
  font-size: var(--fs-small); font-weight: var(--fw-semibold);
  color: var(--fg-primary); display: inline-flex; align-items: baseline;
  gap: var(--space-2); }
.catalog-panel__count { color: var(--fg-muted); font-weight: var(--fw-regular);
  font-variant-numeric: tabular-nums; }
/* collapsed: <details> without [open] → header only, chevron points right */
.catalog-panel__collapse:not([open]) .catalog-panel__header { border-bottom: 0; }
.catalog-panel__collapse:not([open]) .catalog-panel__chevron { transform: rotate(-90deg); }

.catalog-panel__body { padding: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-3);
  flex: 1 1 auto; min-height: 0; overflow: hidden; }

/* Toolbar (F19 amended 2026-07-07): GET search + New Control side by side.
   The wider two-zone catalog column has room for both on one row. */
.catalog-panel__toolbar { display: flex; align-items: center;
  gap: var(--space-2); }
.catalog-panel__search-form { flex: 1 1 auto; min-width: 0; margin: 0; }
.catalog-panel__search { width: 100%; min-width: 0; }
.catalog-panel__new { flex: none; align-self: flex-start; white-space: nowrap; }

.catalog-panel__list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-1);
  /* Own scrollbar so the long catalog scrolls while the toolbar/header stay
     put and the tree stays in view for dropping (F19 amended 2026-07-07,
     second round). The list carries its OWN viewport-relative max-height
     rather than flex-filling the panel: a <details> ancestor (the collapse)
     does not reliably distribute flex height to its children in Chromium, so a
     flex:1 chain through it silently fails to clamp. The subtracted tokens
     approximate the chrome above the list (sticky top offset + panel header +
     toolbar + paddings); it stays within the panel's own max-height so the
     panel never clips a row out of reach. A hair of inline padding keeps the
     item focus ring off the scrollbar edge. */
  max-height: calc(100vh - var(--layout-topbar-h) - var(--space-24) - var(--space-20));
  overflow-y: auto;
  padding-inline: 2px; }

/* Two-line item (F19 amended 2026-07-07): code + pills on the first line, the
   full title WRAPPING on the second — no ellipsis at desktop widths. */
.catalog-panel__item { display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "code pills" "name name";
  align-items: center;
  gap: var(--space-1) var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--bg-surface); cursor: grab;
  position: relative;  /* anchor the whole-row .catalog-panel__open overlay */
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out); }

/* Whole-row overlay link (F19 amended 2026-07-07, second round): a plain click
   opens the read-only peek (?catalog=); a drag still performs the attach DnD
   (the <li> is the Sortable draggable; draggable="false" on the anchor stops
   the browser's native link-drag). It sits above the static code/title/pills
   (which are non-interactive) but the row keeps its grab cursor. */
.catalog-panel__open { position: absolute; inset: 0; z-index: 1;
  border-radius: inherit; }
.catalog-panel__open:focus-visible { outline: none;
  box-shadow: 0 0 0 3px var(--ring-focus); }
.catalog-panel__item:hover { border-color: var(--border-strong);
  background: var(--bg-hover); }
.catalog-panel__item:focus-visible { outline: none;
  border-color: var(--border-brand); box-shadow: 0 0 0 3px var(--ring-focus); }
.catalog-panel__item:active { cursor: grabbing; }

.catalog-panel__code { grid-area: code; min-width: 0; color: var(--fg-muted);
  font-size: var(--fs-caption); font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.catalog-panel__name { grid-area: name; min-width: 0; color: var(--fg-primary);
  font-size: var(--fs-small); line-height: var(--lh-snug, 1.35);
  overflow-wrap: anywhere; }
.catalog-panel__pills { grid-area: pills; display: inline-flex;
  align-items: center; gap: var(--space-1); flex: none; justify-self: end; }

/* Client-side no-match line (F19 live filter) — hidden until the filter hides
   every row. Matches the server-rendered "no controls match" muted line. */
.catalog-panel__no-match { margin: 0; padding: var(--space-2) var(--space-1); }

/* Sortable.js drag visuals — mirror .tree__row so a dragged catalog entry
   reads the same in the catalog and over the tree drop target. */
.catalog-panel__item.sortable-ghost { background: var(--bg-brand-soft);
  border-color: var(--border-brand); opacity: 0.6; }
.catalog-panel__item.sortable-chosen { box-shadow: 0 0 0 1px var(--fg-brand); }
.catalog-panel__item.sortable-drag { background: var(--bg-surface);
  box-shadow: var(--shadow-lg); }

/* Empty — every Control already attached. Full .empty-state shape (D20) but
   with panel-tight padding (the default --space-16 is too tall for the narrow
   zone). */
.catalog-panel__empty { padding: var(--space-8) var(--space-4); }

/* ============================================================
   DROP-POPOVER  (F19 — ADR-0057 §3; tree-editor v2)
   Inline popover anchored at the drop point of a catalog->tree
   (AttachExisting) or tree-internal (MoveNode) drag; captures the
   mandatory section_label before the membership POST fires.
   Deliberately NOT a Dialog: anchored (not centered), non-blocking
   (no backdrop, page stays interactive), single field. Enter submits
   (native form submit — no-JS friendly); Esc cancels and the catalog
   entry is restored (JS drag-end handler, phase 06).

   Composes the .field catalog component for the label/control/help and
   its .has-error + .field__error for the validation-error state — no
   bespoke field or error styling here. Tokens only; the drop-point
   coordinates are the sole data-driven inline values (style="--x/--y",
   set by the JS that opens it — R2). Never persists after submit/cancel
   (removed in the same handler that strips the Tree-DnD indicators).
   ============================================================ */
.drop-popover { position: absolute; z-index: var(--z-dropdown);
  left: var(--x, 0); top: var(--y, 0);
  display: flex; flex-direction: column; gap: var(--space-3);
  min-width: 240px; max-width: 320px;
  padding: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); }

/* Optional pointer toward the drop line. Decorative (aria-hidden) — the
   popover reads correctly without it; sits just above the top edge. */
.drop-popover__arrow { position: absolute; top: calc(-1 * var(--space-1));
  left: var(--space-4); width: var(--space-2); height: var(--space-2);
  background: var(--bg-surface);
  border-left: 1px solid var(--border-default);
  border-top: 1px solid var(--border-default);
  transform: rotate(45deg); }

/* Action row — ghost Cancel then primary Attach, both flushed right, so the
   button order mirrors a form footer (F5). */
.drop-popover__actions { display: flex; justify-content: flex-end;
  gap: var(--space-2); }

/* ============================================================
   GATE-CHAIN  (F10 — workload-detail gate progression)
   Horizontal "stepper"-style chain showing every Gate of a matching
   Gate Group as an ordered stage, blended with the per-gate Run state
   (auto-passed initial · active · approved · rejected · withdrawn ·
   superseded · upcoming). Stages connect via thin chevron connectors;
   the whole chain wraps to a vertical stack on narrow viewports.

   Anatomy:
     .gate-chain               flex row container, wraps to column < 720px
     .gate-chain__stage        per-gate card; <a> when clickable, <div> else
     .gate-chain__stage--<s>   state modifier (state list above)
     .gate-chain__icon         leading status icon (auto-sized via .icon)
     .gate-chain__label        gate label (truncates with ellipsis)
     .gate-chain__status       trailing status caption (small)
     .gate-chain__connector    chevron between stages (hidden on wrap)

   States map to tokens:
     auto-passed  → neutral fill, muted fg     (calm marker, no CTA)
     active       → brand-soft fill, brand fg  (the workload owner acts here)
     approved     → success fill, success fg
     rejected     → danger fill, danger fg
     withdrawn / superseded → neutral, muted (read-only history)
     upcoming     → no fill, dashed border    (locked future stage)
   ============================================================ */
.gate-chain { display: flex; flex-wrap: wrap; align-items: stretch;
  gap: var(--space-2); list-style: none; padding: 0; margin: 0; }
.gate-chain__stage { display: inline-flex; align-items: center;
  gap: var(--space-2); padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); border: 1px solid var(--border-subtle);
  background: var(--bg-surface); color: var(--fg-body);
  font-size: var(--fs-small); text-decoration: none; min-width: 0;
  max-width: 100%; }
.gate-chain__stage:focus-visible { outline: 2px solid var(--border-brand);
  outline-offset: 2px; }
a.gate-chain__stage:hover { background: var(--bg-hover); }
.gate-chain__icon { flex: none; color: var(--fg-muted); }
.gate-chain__label { font-weight: var(--fw-semibold); color: var(--fg-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gate-chain__status { color: var(--fg-muted); font-size: var(--fs-caption);
  white-space: nowrap; }
.gate-chain__connector { display: inline-flex; align-items: center;
  color: var(--clp-neutral-300); flex: none; align-self: center; }
.gate-chain__connector .icon { width: var(--icon-sm); height: var(--icon-sm); }

/* Per-state tints. Mirror the StatusPill/Flash token canon (02-colors-and-status.md):
   ok→success, warn→warning, danger→danger, info-active→brand-soft. */
.gate-chain__stage--auto-passed { background: var(--status-neutral-bg);
  border-color: transparent; color: var(--fg-muted); }
.gate-chain__stage--auto-passed .gate-chain__label { color: var(--fg-muted);
  font-weight: var(--fw-medium); }
.gate-chain__stage--auto-passed .gate-chain__icon { color: var(--status-success); }

.gate-chain__stage--active { background: var(--bg-brand-soft);
  border-color: var(--border-brand); }
.gate-chain__stage--active .gate-chain__label { color: var(--fg-brand); }
.gate-chain__stage--active .gate-chain__icon { color: var(--fg-brand); }
.gate-chain__stage--active .gate-chain__status { color: var(--fg-brand); }

.gate-chain__stage--approved { background: var(--status-success-bg);
  border-color: transparent; }
.gate-chain__stage--approved .gate-chain__label { color: var(--status-success-700); }
.gate-chain__stage--approved .gate-chain__icon { color: var(--status-success-700); }
.gate-chain__stage--approved .gate-chain__status { color: var(--status-success-700); }

.gate-chain__stage--rejected { background: var(--status-danger-bg);
  border-color: transparent; }
.gate-chain__stage--rejected .gate-chain__label { color: var(--status-danger-700); }
.gate-chain__stage--rejected .gate-chain__icon { color: var(--status-danger-700); }
.gate-chain__stage--rejected .gate-chain__status { color: var(--status-danger-700); }

.gate-chain__stage--withdrawn,
.gate-chain__stage--superseded { background: var(--status-neutral-bg);
  border-color: transparent; color: var(--fg-muted); }
.gate-chain__stage--withdrawn .gate-chain__label,
.gate-chain__stage--superseded .gate-chain__label { color: var(--fg-muted); }

.gate-chain__stage--upcoming { background: transparent;
  border-style: dashed; border-color: var(--border-subtle); color: var(--fg-muted); }
.gate-chain__stage--upcoming .gate-chain__label { color: var(--fg-muted);
  font-weight: var(--fw-medium); }

@media (max-width: 720px) {
  .gate-chain { flex-direction: column; align-items: stretch; }
  .gate-chain__stage { width: 100%; }
  .gate-chain__connector { display: none; }
}

/* ============================================================
   APPROVERS-LIST  (gate-run detail page — F10 phase 05)
   Vertical stack, one row per approver. Each row: status icon + name + a
   trailing cluster carrying the decision pill and the decided-at caption.
   An OPTIONAL .approvers-list__comment block sits on a second row inside
   the same grid item, spanning columns 2/3, when the approver attached a
   decision rationale; this absorbs what used to be a separate Decisions
   ledger card on the gate-run detail page.
   Reuses StatusPill colours from the canon (02-colors-and-status.md);
   adds no new tokens.
   ============================================================ */
.approvers-list { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: var(--space-2); }
.approvers-list__item { display: grid;
  grid-template-columns: var(--icon-md) 1fr auto;
  align-items: center; column-gap: var(--space-3); row-gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); }
.approvers-list__item:hover { background: var(--bg-hover); }
.approvers-list__icon { display: inline-flex; align-items: center; justify-content: center;
  width: var(--icon-md); height: var(--icon-md); }
.approvers-list__icon .icon { width: var(--icon-md); height: var(--icon-md);
  color: var(--fg-muted); }
.approvers-list__item[data-decision="approved"] .approvers-list__icon .icon { color: var(--status-success-700); }
.approvers-list__item[data-decision="rejected"] .approvers-list__icon .icon { color: var(--status-danger-700); }
.approvers-list__item[data-decision="pending"]  .approvers-list__icon .icon { color: var(--status-info-700); }
.approvers-list__name { color: var(--fg-primary); font-size: var(--fs-small); }
.approvers-list__meta { justify-content: flex-end; row-gap: var(--space-1); }
/* Optional second row: the decider's free-text comment. Spans columns 2/3 so
   it sits flush under the name (the icon column stays empty, visually
   marking the comment as belonging to the row above). Rendered as a
   blockquote with a left border in the muted scale — reads as "they said
   this" without competing visually with the row's pill+caption. */
.approvers-list__comment { grid-column: 2 / -1;
  margin: 0; padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--border-default);
  color: var(--fg-body); font-size: var(--fs-small);
  background: var(--bg-subtle); border-radius: var(--radius-sm); }

/* ============================================================
   .funnel-filter — F12 Phase 04 (ADR-0054 D-4).

   The Compliance-Funnel filter toolbar is an ADR-0020 primitive with
   two-plus axes rendered as nested <details> inside the popover. The
   popover needs slightly different spacing than the topbar user menu
   (dense; readable; the reader picks multiple values per open session),
   so the funnel-specific bits live under their own BEM prefix rather
   than overloading .menu__panel utilities used by every other page.

   Kept minimal — border + padding utilities that would otherwise be
   inline styles. All widths / fonts / spacings still read from tokens.
   ============================================================ */
.funnel-filter__panel { min-width: 320px; }
.funnel-filter__section { border-top: 1px solid var(--border-subtle);
  padding: var(--space-2) 0; }
.funnel-filter__section-title { padding: var(--space-1) var(--space-3);
  font-weight: var(--fw-semibold); font-size: var(--fs-small);
  color: var(--fg-primary); }
.funnel-filter__section-body { padding: var(--space-2) var(--space-3); }
.funnel-filter__section-body--nested { padding-top: var(--space-2); }
.funnel-filter__footer { padding: var(--space-3);
  border-top: 1px solid var(--border-subtle);
  display: flex; gap: var(--space-2); justify-content: flex-end; }

/* .caption--link — underlined caption text used for compact secondary
   navigation (e.g. "Clear all" / "View as diagram" links). Reads the
   underline explicitly so the base .caption stays plain-text elsewhere. */
.caption--link { text-decoration: underline; }

/* .funnel-diagram__source — small caption row under the Sankey figure
   pointing at the JSON URL + the parallel `?view=list` fallback (F12
   Phase 04, D-2). No layout impact beyond the top-margin. */
.funnel-diagram__source { margin-top: var(--space-3); }
