/* SAIP design system — Phase 67E (liquid glass).
 *
 * Scoped to .lp-root: only the public landing page applies these tokens
 * so the existing authenticated app surfaces are untouched in 67E.
 * Phase 67F will widen the scope after the inner-app polish pass.
 *
 * No third-party CSS · no preprocessor · native CSS custom properties.
 */

:root {
  /* Brand palette */
  --saip-navy-900: #08152B;
  --saip-navy-800: #0B1A33;
  --saip-navy-700: #13294D;
  --saip-slate-600: #3F5C8A;
  --saip-slate-400: #6F88B0;
  --saip-ice-100: #F4F7FB;
  --saip-ice-50: #FBFCFE;
  --saip-ice-0: #FFFFFF;
  --saip-teal-500: #1FA8AB;
  --saip-teal-600: #147A7C;
  --saip-teal-50: #E5F6F6;
  /* F3 IDEMIA violet primitives (LOCKED charte — home.css master: violet 430099
     · peri 7382E6 · peri-lite a9b2ff · ink 0e0420 / 180a30 / 22103f · CTA
     gradient 135deg violet to peri). The semantic ps and psd token tiers below
     redirect the BRAND/ACCENT roles onto these; severity + info signals stay
     on their own ramps. (hex written without a hash so the raw-literal ratchet
     in test_semantic_token_adoption.py counts only real color values.) */
  --saip-violet-700: #35007A;
  --saip-violet-600: #430099;
  --saip-violet-500: #7382E6;
  --saip-violet-300: #A9B2FF;
  --saip-violet-50:  #EFEAFF;
  --saip-violet-ink:  #0E0420;
  --saip-violet-ink2: #1C0D38;
  --saip-violet-ink3: #231148;
  --saip-gradient-cta-violet: linear-gradient(135deg, #430099, #7382E6);
  --saip-amber-500: #D88E2F;
  --saip-amber-50: #FBEFD8;
  --saip-rose-500: #C45163;
  --saip-rose-50: #F8DDE2;
  --saip-green-500: #2BA46E;
  --saip-green-50: #DEF4E8;
  --saip-grey-700: #2B3441;
  --saip-grey-500: #5A6477;
  --saip-grey-400: #5E6B82; /* muted text — DARKENED for WCAG AA (was #76839A ≈ 3.4-3.8:1; now ≈ 4.5:1 on ice-100). W0 a11y floor G1. */
  --saip-grey-300: #C8CFDA;
  --saip-grey-200: #E2E7EE;
  --saip-grey-100: #EEF0F2;  /* light fill / hairline surface */
  --saip-grey-50:  #F8FAFC;  /* lightest surface tint */
  --saip-grey-600: #57606A;  /* muted text, one step darker than 500 (AA ≈ 6.5:1 on white) */
  --saip-white:    #FFFFFF;  /* alias of ice-0 for legacy call sites */
  --saip-navy-50:  var(--saip-grey-100);  /* lightest navy tint — alias to grey-100 (matches prior fallback) */

  /* W0 token-integrity (2026-06-28, Design Council audit) — these names were
     referenced by ~306 call sites as var(--token,#hex) fallbacks but were never
     DEFINED, so the system silently rendered the hardcoded hex (the "assembled,
     not engineered" root cause). Defining them makes the token file the actual
     single source of truth. Values match the dominant prior fallback so the
     appearance is preserved (except grey-400, darkened above for AA). */
  --saip-green-100: #E6F6EC;  /* success wash */
  --saip-green-700: #1D7A3F;  /* success text (AA ≈ 4.8:1 on white) */
  --saip-red-500:   var(--saip-rose-500);  /* semantic alias */
  --saip-red-100:   #FDE6E6;  /* danger wash */
  --saip-red-700:   #A11D1D;  /* danger text (AA ≈ 6.4:1 on white) */
  --saip-amber-600: #9A6700;  /* warning text (AA ≈ 4.9:1 on white) — W4 residual: was referenced undefined in standards_kb.css */
  --saip-amber-700: #9A6700;  /* warning text (AA ≈ 4.9:1 on white) */
  --saip-blue-100:  #E6EFFB;  /* info wash */
  --saip-blue-700:  #1C52A8;  /* info text (AA ≈ 6.9:1 on white, ≈ 6.3:1 on blue-100) */
  --saip-navy-200:  #C6DAFC;  /* light info border/fill */
  --saip-navy-300:  #A8C7FA;  /* light info border (decorative, non-text) */
  --saip-navy-400:  var(--saip-slate-600);  /* real palette navy (removes the stray #4a7fd0 blue) */
  --saip-mono:      var(--saip-font-mono);  /* alias for font-family call sites */

  /* W4B token-system closure (2026-06-29) — the safe, bounded --saip-* slice.
     Each name below was referenced as var(--token,#hex) with no :root entry,
     so the system rendered the hardcoded fallback. Defining them makes the
     token file the source of truth. Values match the dominant prior fallback
     unless an AA-safe shift is noted. */
  --saip-grey-800:  #24292F;  /* darkest grey text (AA ≈ 13:1 on white) */
  --saip-slate-700: #2F3B4C;  /* deep slate body text (AA ≈ 9.7:1 on white) */
  --saip-green-600: #1A7F37;  /* success text/fill, one step darker than 500 (AA ≈ 4.8:1 on white) */
  --saip-amber-100: #FDF0D5;  /* warning wash (between amber-50 and amber-500) */
  --saip-color-warn: var(--saip-amber-500);  /* semantic warn alias (was undefined #b66; amber is the system warn hue) */
  --saip-danger-600: var(--saip-red-700);    /* semantic danger-text alias (canonical AA red text) */
  --saip-red-600:    var(--saip-red-700);    /* semantic danger-text alias (canonical AA red text) */
  --saip-font-sans:  var(--saip-font-ui);    /* alias for legacy font-family call site */
  --saip-glass-light: rgba(255, 255, 255, 0.65);
  --saip-glass-dark: rgba(11, 26, 51, 0.55);
  --saip-glass-border: rgba(255, 255, 255, 0.18);

  /* Gradients */
  /* F3 REBRAND — auth/hero dark surface re-tinted navy -> IDEMIA violet so the
     sign-in hero reads as ONE brand with the violet chrome + landing. Radial
     spotlight kept (focal 18% 12%, behind the sign-in card); stops violet-600
     -> violet-ink3 -> violet-ink. White/light text clears AA on every stop
     (violet-600 = 12.4:1; the deeper stops higher). Hex count unchanged (3 -> 3).
     Consumed by the live .is-auth-hero .app-main + the legacy .lp-* rules. */
  --saip-gradient-hero: radial-gradient(circle at 18% 12%, #430099 0%, #231148 45%, #0E0420 100%);
  --saip-gradient-card-light: linear-gradient(135deg, rgba(255, 255, 255, 0.85) 0%, rgba(244, 247, 251, 0.55) 100%);
  --saip-gradient-card-dark: linear-gradient(135deg, rgba(31, 168, 171, 0.18) 0%, rgba(11, 26, 51, 0.55) 100%);
  /* F3 coherence — CTA gradients re-tinted teal -> IDEMIA violet so the primary
     buttons + the workspace hero read as ONE brand with the violet chrome. White
     text clears AA on BOTH stops of each gradient (contrast computed in the F3 report). */
  --saip-gradient-cta-primary: linear-gradient(135deg, #430099 0%, #6A24B8 100%);
  --saip-gradient-cta-navy: linear-gradient(135deg, #430099 0%, #1C0D38 100%);

  /* Spacing scale */
  --saip-spacing-1: 4px;
  --saip-spacing-2: 8px;
  --saip-spacing-3: 12px;
  --saip-spacing-4: 16px;
  --saip-spacing-5: 24px;
  --saip-spacing-6: 32px;
  --saip-spacing-7: 48px;
  --saip-spacing-8: 64px;
  --saip-spacing-9: 96px;

  /* Layout */
  --saip-container-max: 1180px;
  --saip-gutter: 24px;
  /* UX-GLOBAL-AUTHENTICATED-SHELL-ALIGNMENT-01 — the ONE authenticated
     application shell (.app-wrap), tokenised. The 1760px max-width is the
     Sponsor-locked AUD6835D-F19 frame (harmonised across every pillar,
     Profile Cockpit as the reference). The gutter is a single clamp shared
     by every viewport (24px >=1440px, smooth scale, 16px floor) replacing
     the former 24/20/10/16px per-breakpoint spread. The nav bleed equals
     the .app-nav link horizontal padding so the nav TEXT (left edge of the
     Dashboard "D", right edge of the Sign-out "t") lands exactly on the
     shell content edges — those two text edges are the canonical desktop
     rails every authenticated surface aligns to. Public pages (.lp-*, the
     standalone landing and the auth ensemble) keep their own shells. */
  --ps-auth-shell-max-width: 1760px;
  --ps-auth-shell-gutter: clamp(16px, 1.25vw + 6px, 24px);
  --ps-auth-shell-nav-bleed: 12px;

  /* Radii */
  --saip-radius-sm: 8px;
  --saip-radius-md: 10px;  /* step between sm (8px) and base (12px) — was undefined */
  --saip-radius: 12px;
  --saip-radius-lg: 16px;
  --saip-radius-xl: 24px;

  /* Elevation */
  --saip-shadow-sm: 0 2px 8px rgba(11, 26, 51, 0.06);
  --saip-shadow-md: 0 8px 32px rgba(11, 26, 51, 0.08);
  --saip-shadow-lg: 0 16px 48px rgba(11, 26, 51, 0.14);
  /* Enterprise redesign (HANDOFF §2.2) — soft card shadow + row-hover tint */
  --saip-shadow-card: 0 1px 4px rgba(11, 26, 51, 0.05);
  /* CLAUSE-DRAWER-SHADOW-THEMEFLIP-01 — the Clause drawer/bottom-sheet
     overlay shadows (navy-based in light; black-based in dark via --psd-*). */
  --saip-shadow-drawer: -18px 0 48px rgba(11, 26, 51, 0.22);
  --saip-shadow-sheet: 0 -18px 48px rgba(11, 26, 51, 0.28);
  --saip-row-hover: rgba(31, 168, 171, 0.045);

  /* Surface blur */
  --saip-blur-card: blur(18px);
  --saip-blur-modal: blur(28px);

  /* Typography fallback (Inter / Source Serif Pro shipped in 67F when self-hosted woff2
   * land; for 67E we use the OS sans-serif stack with a Source Serif Pro hint that
   * falls back gracefully). */
  --saip-font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Helvetica, Arial, system-ui, sans-serif;
  --saip-font-serif: "Source Serif Pro", "Source Serif 4", Georgia, "Times New Roman", serif;
  --saip-font-mono: "JetBrains Mono", Menlo, Consolas, monospace;

  /* W5 typography consistency (Sponsor 2026-06-18) — single canonical type
   * scale. Every text size site-wide snaps to one of these steps so the same
   * semantic element renders at one size across pages/sections. The values
   * are the charter sizes already in use; the scale just NAMES them so future
   * work references a token instead of inventing a raw px (the root cause of
   * the 8/9/10/10.5/11/11.5/12/12.5/13/14/16/20 spread the Sponsor saw). */
  --saip-text-2xs: 10px;   /* micro labels, count pills, field labels */
  --saip-text-xs: 11px;    /* small uppercase labels, muted captions, pills */
  --saip-text-sm: 12px;    /* secondary text, meta, breadcrumbs, helper text */
  --saip-text-base: 13px;  /* table cells, dense body, nav, buttons */
  --saip-text-md: 14px;    /* default body text (.app-root base) */
  --saip-text-lg: 15px;    /* section/card titles (.app-sec-title) */
  --saip-text-h3: 16px;    /* h3 */
  --saip-text-h2: 18px;    /* h2 */
  --saip-text-h1: 24px;    /* h1 (page title in chrome) */
  --saip-text-xl: 26px;    /* large numeric / progress percent */
  --saip-text-2xl: 34px;   /* hero stat value */
  --saip-text-3xl: 40px;   /* display hero lockup (e.g. /clause console title) */
  /* Mono / value sizes — code, hex, monospaced values only. */
  --saip-text-mono: 12.5px;
  --saip-text-mono-sm: 11px;

  /* M22-BRAND-02-R1 — brand logo sizing tokens (folded in from the former
   * second :root block so the design system has ONE token root). */
  --ps-logo-app-header: 44px;   /* enlarged brand lockup — prominent navy header mark */
  --ps-logo-app-header-mobile: 34px;
  --clause-mark-panel: 30px;    /* embedded Clause panel header */
  --clause-mark-console: 40px;  /* /clause page lead */

  /* ============================================================
     Wave-0 SEMANTIC token layer (--ps-*) — the theme-swappable tier.
     The primitives above are the raw palette; the names below name
     colours by ROLE, so every surface consumes ONE source of truth and
     dark mode (Wave 2) is just a SECOND mapping of the SAME names via
     [data-theme]. Brand chrome (navy/teal on header/nav/hero) keeps the
     primitives directly. Light values here match the current appearance
     (brief 02 · field-test 2026-07-01 · Design Council: visual-design-lead).
     ============================================================ */
  /* Surface & text */
  --ps-bg-canvas:        var(--saip-ice-100);
  --ps-bg-surface:       var(--saip-ice-0);
  --ps-bg-surface-muted: var(--saip-ice-50);
  --ps-bg-inset:         var(--saip-ice-50);
  --ps-bg-table-head:    var(--saip-ice-50);
  --ps-text-primary:     var(--saip-slate-700);
  --ps-text-heading:     var(--saip-navy-900);
  --ps-text-secondary:   var(--saip-grey-600);
  --ps-text-muted:       var(--saip-grey-400);
  --ps-border-hairline:  var(--ps-border-hairline);
  --ps-border-strong:    var(--ps-border-strong);
  /* Composer / primary-input border — the page's most important control needs
     a NON-text border >= 3:1 vs the surface (WCAG 1.4.11). --ps-border-strong
     (grey-300 ~1.57:1 on white) fails; slate-400 (#6F88B0) is ~3.66:1 on white
     / surface-muted. Dark maps onto --psd-border-input (below). Guarded by
     tests/web/test_ps_token_contrast_aa.py (test_input_border_token_visible). */
  --ps-border-input:     var(--saip-slate-400);
  /* FT-W2-DARK-SWEEP-01 — three role tokens the pillar stylesheets consumed as
     RAW primitives (recessed neutral fill · brand-navy heading/label/mono-id ·
     slate secondary text). Light values are BYTE-identical to the primitives
     they replace (grey-100 · navy-700 · slate-600) so no light-mode colour
     shifts; the dark map (below) repaints all three. Dark pairings are
     WCAG-AA computed + guarded (tests/web/test_ps_token_contrast_aa.py). */
  --ps-bg-fill:            var(--saip-grey-100);
  --ps-text-heading-brand: var(--saip-navy-700);
  --ps-text-slate:         var(--saip-slate-600);
  --ps-link:             var(--saip-violet-600); /* F3: IDEMIA violet-600 ≈12.4:1 on white (text-safe) — was teal-600 */
  --ps-accent:           var(--saip-violet-600); /* F3: brand accent → IDEMIA violet — was teal-500 */
  /* Brand-accent SOFT chip (upload dropzone icon / brand badges) — a violet
     echo of the --ps-sev-* wash/fg pattern so it FLIPS with the theme instead
     of sitting on the info/teal ramp. Light = violet-50 wash + violet-600 glyph;
     the dark map (below) repaints it recessed-violet-ink + periwinkle-lite.
     Info stays teal (--ps-sev-info-*); this is the BRAND accent, not info. */
  --ps-accent-soft-bg:   var(--saip-violet-50);
  --ps-accent-soft-fg:   var(--saip-violet-600);
  --ps-shadow-card:      var(--saip-shadow-card);
  --ps-shadow-drawer:    var(--saip-shadow-drawer);
  --ps-shadow-sheet:     var(--saip-shadow-sheet);
  --ps-focus-ring:       var(--saip-violet-600); /* F3: violet-600 ≈12.4:1 vs white (≥3:1 non-text) — was teal-600 5.12:1 */
  /* Severity — ONE canonical set (HANDOFF §2.7). fg · bg · bd
     FT-DC-PILL-CONTRAST-AA-01 (Design Council T9 a11y M6): the fg darks below
     are the WCAG-AA text ramp for 11px bold uppercase pill text (needs 4.5:1
     on the wash). Values reuse the ramp the product already shipped in the
     PE workspace badges (.pe-badge-*), which now CONSUME these tokens.
     Computed ratios (guarded by tests/web/test_ps_token_contrast_aa.py):
       critical on rose-50   ≈ 6.21:1 (was rose-500  ≈ 3.49:1)
       major    on amber-50  ≈ 5.09:1 (was amber-500 ≈ 2.36:1)
       minor    on amber-50  ≈ 4.94:1 (was old olive ≈ 2.52:1)
       pass     on green-50  ≈ 6.26:1 (was green-500 ≈ 2.74:1)
       info     teal-600 on teal-50  ≈ 4.59:1 (verified — kept)
       neutral  grey-500 on grey-200 ≈ 4.80:1 (verified — kept) */
  --ps-sev-critical-fg: #9E1C23;               --ps-sev-critical-bg: var(--saip-rose-50);  --ps-sev-critical-bd: #f0a8a0;
  --ps-sev-critical-solid-bg: #B03A4C;         /* solid pill bg — white text ≈ 5.91:1 (was rose-500 ≈ 4.46:1, borderline) */
  --ps-sev-major-fg:    #92580A;               --ps-sev-major-bg:    var(--saip-amber-50); --ps-sev-major-bd:    #f0c987;
  --ps-sev-minor-fg:    #7A6608;               --ps-sev-minor-bg:    var(--saip-amber-50); --ps-sev-minor-bd:    #ead7af;
  --ps-sev-pass-fg:     #156436;               --ps-sev-pass-bg:     var(--saip-green-50); --ps-sev-pass-bd:     #bcdcc4;
  --ps-sev-info-fg:     var(--saip-teal-600);  --ps-sev-info-bg:     var(--saip-teal-50);  --ps-sev-info-bd:     #b6dcdd;
  --ps-sev-neutral-fg:  var(--saip-grey-500);  --ps-sev-neutral-bg:  var(--saip-grey-200); --ps-sev-neutral-bd:  var(--saip-grey-300);
  /* REVUE3P-01.a — DIFF semantic tier (Profile Delta). A DEDICATED tier, mapped
     INDEPENDENTLY of severity so the structural diff surface never borrows the
     Critical/Major/Pass hues by accident, and so it FLIPS with the theme (the
     raw --saip-*-500/-50 primitives it replaces had no dark override → broken
     contrast in dark). added=green, removed=rose, changed=amber — the universal
     diff convention. fg/bg/bd triads reuse the proven, AA-verified severity
     numerics but under diff-owned names (decoupled source of truth). */
  --ps-diff-added-fg:   #156436;  --ps-diff-added-bg:   var(--saip-green-50);  --ps-diff-added-bd:   #bcdcc4;
  --ps-diff-removed-fg: #9E1C23;  --ps-diff-removed-bg: var(--saip-rose-50);   --ps-diff-removed-bd: #f0a8a0;
  --ps-diff-changed-fg: #92580A;  --ps-diff-changed-bg: var(--saip-amber-50);  --ps-diff-changed-bd: #f0c987;
  /* Action-danger (destructive buttons) — AA on white; folds .app-btn-danger
     and the PE workspace .pe-ws-btn-danger onto ONE source of truth.
       fg on white ≈ 6.62:1 · white on bg ≈ 5.44:1 · white on bg-hover ≈ 6.62:1 */
  --ps-danger-fg:       #A93226;
  --ps-danger-bg:       #C0392B;
  --ps-danger-bd:       #A93226;
  --ps-danger-bg-hover: #A93226;
  /* Brand chrome — identical in light & dark (navy is already dark) */
  --ps-chrome-bg:      var(--saip-gradient-cta-violet); /* F3: IDEMIA violet gradient header/chrome — was cta-navy */
  --ps-chrome-fg:      var(--saip-ice-0);
  --ps-chrome-fg-muted:var(--saip-slate-400);
  /* UXCOMP-WAHOU-01 — chrome interaction states + dialog backdrop, promoted
     from raw rgba() literals (chrome is theme-invariant, so is the scrim). */
  --ps-chrome-hover:   rgba(255, 255, 255, 0.06);
  --ps-chrome-active:  rgba(255, 255, 255, 0.08);
  --ps-backdrop:       rgba(15, 22, 33, 0.45);

  /* ============================================================
     FT-W2-APPEARANCE-PREF-01 — DARK-value primitives (--psd-*).
     Defined once here (raw hex lives in ONE place) and consumed by the
     two dark mapping blocks below, so the explicit-dark and system-dark
     maps can never drift value-wise. Navy-950-family canvas, surfaces one
     step lighter, ice/slate-light text — consistent with the navy/teal
     brand. Every text-bearing pair is WCAG-AA computed (guarded by the
     dark parametrisation in tests/web/test_ps_token_contrast_aa.py):
       text-primary on surface   #D7E1EF on #12233B = 11.97:1
       text-secondary on surface #A6B5CA on #12233B = 7.59:1
       text-muted on surface     #94A5BD on #12233B = 6.30:1 (canvas 7.25:1)
       link on surface           #5BC6C9 on #12233B = 7.80:1
       sev-critical fg/bg        #F2A6AC on #3D1A20 = 7.92:1
       sev-major    fg/bg        #ECB25E on #3A2A12 = 7.30:1
       sev-minor    fg/bg        #D9C35F on #34290F = 8.11:1
       sev-pass     fg/bg        #7ED0A2 on #10301F = 7.81:1
       sev-info     fg/bg        #64C9CC on #0E2C2D = 7.60:1
       sev-neutral  fg/bg        #B9C5D6 on #2A3850 = 6.75:1
       danger-fg on surface      #F0938B on #12233B = 6.96:1
       danger-bd vs surface      #E06055 on #12233B = 4.49:1 (non-text, >=3:1)
     Solid pill / danger-button backgrounds keep the light-mode values
     (white-on-solid ratios already AA: 5.91:1 / 5.44:1 / 6.62:1). */
  --psd-bg-canvas:        #0E0420;  /* F3 violet ink canvas */
  --psd-bg-surface:       #1C0D38;  /* F3 violet-ink surface */
  --psd-bg-surface-muted: #160A2C;  /* F3 violet-ink muted */
  --psd-bg-inset:         #160A2C;  /* F3 violet-ink inset */
  --psd-bg-table-head:    #231148;  /* F3 violet table head */
  --psd-text-primary:     #D7E1EF;
  --psd-text-heading:     #F4F7FB;
  --psd-text-secondary:   #A6B5CA;
  --psd-text-muted:       #94A5BD;
  --psd-border-hairline:  #263850;
  --psd-border-strong:    #3A5070;
  /* composer/primary-input border (non-text >= 3:1 vs the dark surfaces):
       #6E86A8 on #12233B = 4.19:1 · on #0E1C30 = 4.54:1 (both >= 3:1) */
  --psd-border-input:     #6E86A8;
  /* FT-W2-DARK-SWEEP-01 dark values (computed vs the dark surfaces):
       bg-fill        recessed fill (text-primary on it = 10.6:1,
                      text-secondary on it = 6.7:1)
       heading-brand  bright navy-tint on the dark surface = 11.5:1
       text-slate reuses --psd-text-secondary (7.59:1 on surface) — the
       light-mode slate-blue distinction is not preserved in dark (both
       secondary tiers converge to one legible light-grey). */
  --psd-bg-fill:            #281952;  /* F3 recessed violet fill */
  --psd-text-heading-brand: #CFC2FF;  /* F3 violet-tint heading */
  --psd-link:             #A9B2FF;  /* F3 periwinkle-lite link */
  --psd-focus-ring:       #A9B2FF;  /* F3 periwinkle-lite focus */
  --psd-shadow-card:      0 1px 4px rgba(0, 0, 0, 0.55);
  --psd-shadow-drawer:    -18px 0 48px rgba(0, 0, 0, 0.6);
  --psd-shadow-sheet:     0 -18px 48px rgba(0, 0, 0, 0.62);
  --psd-sev-critical-fg:  #F2A6AC;
  --psd-sev-critical-bg:  #3D1A20;
  --psd-sev-critical-bd:  #7C3038;
  --psd-sev-major-fg:     #ECB25E;
  --psd-sev-major-bg:     #3A2A12;
  --psd-sev-major-bd:     #7A5A28;
  --psd-sev-minor-fg:     #D9C35F;
  --psd-sev-minor-bg:     #34290F;
  --psd-sev-minor-bd:     #6E5E28;
  --psd-sev-pass-fg:      #7ED0A2;
  --psd-sev-pass-bg:      #10301F;
  --psd-sev-pass-bd:      #2C5E42;
  --psd-sev-info-fg:      #64C9CC;
  --psd-sev-info-bg:      #0E2C2D;
  --psd-sev-info-bd:      #2A5C5E;
  --psd-sev-neutral-fg:   #B9C5D6;
  --psd-sev-neutral-bg:   #2A3850;
  --psd-sev-neutral-bd:   #45577A;
  /* REVUE3P-01.a — DIFF tier dark values (mirror of the sev dark triads). */
  --psd-diff-added-fg:    #7ED0A2;  --psd-diff-added-bg:    #10301F;  --psd-diff-added-bd:    #2C5E42;
  --psd-diff-removed-fg:  #F2A6AC;  --psd-diff-removed-bg:  #3D1A20;  --psd-diff-removed-bd:  #7C3038;
  --psd-diff-changed-fg:  #ECB25E;  --psd-diff-changed-bg:  #3A2A12;  --psd-diff-changed-bd:  #7A5A28;
  --psd-danger-fg:        #F0938B;
  --psd-danger-bd:        #E06055;
}

/* ============================================================
   FT-W2-APPEARANCE-PREF-01 — appearance modes (light / dark / system).
   base.html server-stamps <html data-theme="light|dark|system"> from the
   allowlist-validated saip_theme cookie (default system) — first paint is
   already correct, no head script. The dark SEMANTIC map applies under
   BOTH selector groups: explicit [data-theme="dark"], and
   [data-theme="system"] when the OS prefers dark. Brand chrome
   (--ps-chrome-*) is deliberately NOT remapped — the navy header/nav is
   identical in both modes per the charter. Pages that do not extend
   base.html (public landing, .lp-root/.hp-root) carry no data-theme and
   always render the light map. `color-scheme` keeps native widgets /
   scrollbars in step (design note FT-W2-APPEARANCE-PREF-01 section 5).
   ============================================================ */
:root { color-scheme: light; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="system"] { color-scheme: light dark; }

:root[data-theme="dark"] {
  --ps-bg-canvas:        var(--psd-bg-canvas);
  --ps-bg-surface:       var(--psd-bg-surface);
  --ps-bg-surface-muted: var(--psd-bg-surface-muted);
  --ps-bg-inset:         var(--psd-bg-inset);
  --ps-bg-table-head:    var(--psd-bg-table-head);
  --ps-text-primary:     var(--psd-text-primary);
  --ps-text-heading:     var(--psd-text-heading);
  --ps-text-secondary:   var(--psd-text-secondary);
  --ps-text-muted:       var(--psd-text-muted);
  --ps-border-hairline:  var(--psd-border-hairline);
  --ps-border-strong:    var(--psd-border-strong);
  --ps-border-input:     var(--psd-border-input);
  --ps-bg-fill:            var(--psd-bg-fill);
  --ps-text-heading-brand: var(--psd-text-heading-brand);
  --ps-text-slate:         var(--psd-text-secondary);
  --ps-link:             var(--psd-link);
  --ps-focus-ring:       var(--psd-focus-ring);
  --ps-accent-soft-bg:   var(--psd-bg-fill);
  --ps-accent-soft-fg:   var(--psd-link);
  --ps-shadow-card:      var(--psd-shadow-card);
  --ps-shadow-drawer:    var(--psd-shadow-drawer);
  --ps-shadow-sheet:     var(--psd-shadow-sheet);
  --ps-sev-critical-fg:  var(--psd-sev-critical-fg);
  --ps-sev-critical-bg:  var(--psd-sev-critical-bg);
  --ps-sev-critical-bd:  var(--psd-sev-critical-bd);
  --ps-sev-major-fg:     var(--psd-sev-major-fg);
  --ps-sev-major-bg:     var(--psd-sev-major-bg);
  --ps-sev-major-bd:     var(--psd-sev-major-bd);
  --ps-sev-minor-fg:     var(--psd-sev-minor-fg);
  --ps-sev-minor-bg:     var(--psd-sev-minor-bg);
  --ps-sev-minor-bd:     var(--psd-sev-minor-bd);
  --ps-sev-pass-fg:      var(--psd-sev-pass-fg);
  --ps-sev-pass-bg:      var(--psd-sev-pass-bg);
  --ps-sev-pass-bd:      var(--psd-sev-pass-bd);
  --ps-sev-info-fg:      var(--psd-sev-info-fg);
  --ps-sev-info-bg:      var(--psd-sev-info-bg);
  --ps-sev-info-bd:      var(--psd-sev-info-bd);
  --ps-sev-neutral-fg:   var(--psd-sev-neutral-fg);
  --ps-sev-neutral-bg:   var(--psd-sev-neutral-bg);
  --ps-sev-neutral-bd:   var(--psd-sev-neutral-bd);
  --ps-diff-added-fg:    var(--psd-diff-added-fg);
  --ps-diff-added-bg:    var(--psd-diff-added-bg);
  --ps-diff-added-bd:    var(--psd-diff-added-bd);
  --ps-diff-removed-fg:  var(--psd-diff-removed-fg);
  --ps-diff-removed-bg:  var(--psd-diff-removed-bg);
  --ps-diff-removed-bd:  var(--psd-diff-removed-bd);
  --ps-diff-changed-fg:  var(--psd-diff-changed-fg);
  --ps-diff-changed-bg:  var(--psd-diff-changed-bg);
  --ps-diff-changed-bd:  var(--psd-diff-changed-bd);
  --ps-danger-fg:        var(--psd-danger-fg);
  --ps-danger-bd:        var(--psd-danger-bd);
}

@media (prefers-color-scheme: dark) {
  :root[data-theme="system"] {
    --ps-bg-canvas:        var(--psd-bg-canvas);
    --ps-bg-surface:       var(--psd-bg-surface);
    --ps-bg-surface-muted: var(--psd-bg-surface-muted);
    --ps-bg-inset:         var(--psd-bg-inset);
    --ps-bg-table-head:    var(--psd-bg-table-head);
    --ps-text-primary:     var(--psd-text-primary);
    --ps-text-heading:     var(--psd-text-heading);
    --ps-text-secondary:   var(--psd-text-secondary);
    --ps-text-muted:       var(--psd-text-muted);
    --ps-border-hairline:  var(--psd-border-hairline);
    --ps-border-strong:    var(--psd-border-strong);
    --ps-border-input:     var(--psd-border-input);
    --ps-bg-fill:            var(--psd-bg-fill);
    --ps-text-heading-brand: var(--psd-text-heading-brand);
    --ps-text-slate:         var(--psd-text-secondary);
    --ps-link:             var(--psd-link);
    --ps-focus-ring:       var(--psd-focus-ring);
    --ps-accent-soft-bg:   var(--psd-bg-fill);
    --ps-accent-soft-fg:   var(--psd-link);
    --ps-shadow-card:      var(--psd-shadow-card);
    --ps-shadow-drawer:    var(--psd-shadow-drawer);
    --ps-shadow-sheet:     var(--psd-shadow-sheet);
    --ps-sev-critical-fg:  var(--psd-sev-critical-fg);
    --ps-sev-critical-bg:  var(--psd-sev-critical-bg);
    --ps-sev-critical-bd:  var(--psd-sev-critical-bd);
    --ps-sev-major-fg:     var(--psd-sev-major-fg);
    --ps-sev-major-bg:     var(--psd-sev-major-bg);
    --ps-sev-major-bd:     var(--psd-sev-major-bd);
    --ps-sev-minor-fg:     var(--psd-sev-minor-fg);
    --ps-sev-minor-bg:     var(--psd-sev-minor-bg);
    --ps-sev-minor-bd:     var(--psd-sev-minor-bd);
    --ps-sev-pass-fg:      var(--psd-sev-pass-fg);
    --ps-sev-pass-bg:      var(--psd-sev-pass-bg);
    --ps-sev-pass-bd:      var(--psd-sev-pass-bd);
    --ps-sev-info-fg:      var(--psd-sev-info-fg);
    --ps-sev-info-bg:      var(--psd-sev-info-bg);
    --ps-sev-info-bd:      var(--psd-sev-info-bd);
    --ps-sev-neutral-fg:   var(--psd-sev-neutral-fg);
    --ps-sev-neutral-bg:   var(--psd-sev-neutral-bg);
    --ps-sev-neutral-bd:   var(--psd-sev-neutral-bd);
    --ps-diff-added-fg:    var(--psd-diff-added-fg);
    --ps-diff-added-bg:    var(--psd-diff-added-bg);
    --ps-diff-added-bd:    var(--psd-diff-added-bd);
    --ps-diff-removed-fg:  var(--psd-diff-removed-fg);
    --ps-diff-removed-bg:  var(--psd-diff-removed-bg);
    --ps-diff-removed-bd:  var(--psd-diff-removed-bd);
    --ps-diff-changed-fg:  var(--psd-diff-changed-fg);
    --ps-diff-changed-bg:  var(--psd-diff-changed-bg);
    --ps-diff-changed-bd:  var(--psd-diff-changed-bd);
    --ps-danger-fg:        var(--psd-danger-fg);
    --ps-danger-bd:        var(--psd-danger-bd);
  }
}

/* ============================================================
   Public landing layout — scope: .lp-root
   ============================================================ */

.lp-root {
  --lp-text-on-dark: var(--saip-ice-0);
  --lp-text-muted-on-dark: var(--saip-slate-400);
  --lp-text-on-light: var(--saip-grey-700);
  --lp-text-muted-on-light: var(--saip-grey-500);
  background: var(--saip-ice-100);
  color: var(--lp-text-on-light);
  font-family: var(--saip-font-ui);
  line-height: 1.6;
}

.lp-root *,
.lp-root *::before,
.lp-root *::after {
  box-sizing: border-box;
}

.lp-root .lp-container {
  max-width: var(--saip-container-max);
  margin: 0 auto;
  padding: 0 var(--saip-gutter);
}

.lp-root a {
  color: var(--saip-teal-600);
  text-decoration: none;
}

.lp-root a:hover {
  text-decoration: underline;
}

.lp-root .lp-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--saip-navy-800);
  color: var(--saip-ice-0);
  padding: 8px 12px;
  z-index: 99;
}

.lp-root .lp-skip-link:focus {
  left: 8px;
  top: 8px;
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}

/* ============================================================
   Header / nav (public)
   ============================================================ */

.lp-header {
  background: rgba(11, 26, 51, 0.92);
  color: var(--lp-text-on-dark);
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(18px);
          backdrop-filter: blur(18px);
}

.lp-header .lp-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--saip-spacing-4);
  padding: var(--saip-spacing-4) 0;
}

.lp-wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--saip-spacing-3);
  color: var(--lp-text-on-dark);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.005em;
}

.lp-wordmark .lp-wordmark-glyph {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  background: var(--saip-gradient-cta-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--saip-ice-0);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -0.02em;
}

.lp-nav {
  display: flex;
  align-items: center;
  gap: var(--saip-spacing-4);
  flex-wrap: wrap;
}

.lp-nav a {
  color: var(--saip-ice-0);
  font-size: 14px;
  font-weight: 500;
  padding: 8px 10px;
  border-radius: var(--saip-radius-sm);
}

.lp-nav a:hover {
  background: rgba(255, 255, 255, 0.08);
  text-decoration: none;
}

.lp-nav .lp-nav-cta-secondary,
.lp-nav .lp-nav-cta-primary {
  padding: 8px 14px;
  font-weight: 600;
}

.lp-nav .lp-nav-cta-secondary {
  border: 1px solid var(--saip-glass-border);
}

.lp-nav .lp-nav-cta-primary {
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
}

/* ============================================================
   Buttons
   ============================================================ */

.lp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--saip-spacing-2);
  padding: 12px 22px;
  border-radius: var(--saip-radius);
  font-family: var(--saip-font-ui);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: transform 200ms ease-out, box-shadow 200ms ease-out, background 200ms ease-out;
}

.lp-btn-primary {
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
}

.lp-btn-primary:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--saip-shadow-md);
}

.lp-btn-navy {
  background: var(--saip-gradient-cta-navy);
  color: var(--saip-ice-0);
}

.lp-btn-navy:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--saip-shadow-md);
}

.lp-btn-secondary {
  background: transparent;
  border-color: var(--ps-text-muted);
  color: var(--ps-text-primary);
}

.lp-btn-secondary:hover {
  background: var(--ps-bg-surface-muted);
  text-decoration: none;
}

.lp-btn-on-dark {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--saip-glass-border);
  color: var(--saip-ice-0);
}

.lp-btn-on-dark:hover {
  background: rgba(255, 255, 255, 0.14);
  text-decoration: none;
}

.lp-btn:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 3px;
}

/* ============================================================
   Section base
   ============================================================ */

.lp-section {
  padding: var(--saip-spacing-9) 0;
}

.lp-section-light {
  background: var(--saip-ice-100);
  color: var(--lp-text-on-light);
}

.lp-section-dark {
  background: var(--saip-gradient-hero);
  color: var(--lp-text-on-dark);
}

.lp-section-slate {
  background:
    linear-gradient(180deg, rgba(11, 26, 51, 0.94) 0%, rgba(19, 41, 77, 0.94) 100%),
    radial-gradient(circle at 80% 0%, rgba(31, 168, 171, 0.18) 0%, transparent 60%);
  color: var(--lp-text-on-dark);
}

.lp-eyebrow {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--saip-teal-500);
  margin-bottom: var(--saip-spacing-4);
  display: block;
}

.lp-section-light .lp-eyebrow {
  color: var(--saip-teal-600);
}

.lp-section-header h2,
.lp-h2 {
  font-family: var(--saip-font-ui);
  font-size: 32px;
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0 0 var(--saip-spacing-4) 0;
}

.lp-section-header p,
.lp-lead {
  font-size: 17px;
  line-height: 1.6;
  max-width: 720px;
  margin: 0 0 var(--saip-spacing-6) 0;
  color: inherit;
  opacity: 0.92;
}

.lp-section-light .lp-lead {
  color: var(--ps-text-primary);
  opacity: 1;
}

/* ============================================================
   Hero
   ============================================================ */

.lp-hero {
  background: var(--saip-gradient-hero);
  color: var(--lp-text-on-dark);
  padding: var(--saip-spacing-9) 0 var(--saip-spacing-9) 0;
  position: relative;
  overflow: hidden;
}

.lp-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 80% 85%, rgba(31, 168, 171, 0.22) 0%, transparent 55%),
    radial-gradient(circle at 95% 5%, rgba(63, 92, 138, 0.30) 0%, transparent 50%);
  pointer-events: none;
}

.lp-hero-inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--saip-spacing-8);
  align-items: center;
}

.lp-hero-text {
  max-width: 640px;
}

.lp-hero h1 {
  font-family: var(--saip-font-serif);
  font-size: 56px;
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 var(--saip-spacing-5) 0;
  color: var(--saip-ice-0);
}

.lp-hero .lp-subhead {
  font-size: 19px;
  line-height: 1.6;
  color: var(--ps-text-muted);
  margin: 0 0 var(--saip-spacing-6) 0;
  max-width: 580px;
}

.lp-hero .lp-cta-row {
  display: flex;
  gap: var(--saip-spacing-3);
  flex-wrap: wrap;
  align-items: center;
}

.lp-hero .lp-hero-footnote {
  margin-top: var(--saip-spacing-6);
  font-size: 12px;
  color: var(--ps-text-muted);
  line-height: 1.5;
}

.lp-trust-strip {
  margin-top: var(--saip-spacing-7);
  padding-top: var(--saip-spacing-5);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  flex-wrap: wrap;
  gap: var(--saip-spacing-3) var(--saip-spacing-6);
  font-size: 13px;
  color: var(--ps-text-muted);
}

.lp-trust-strip .lp-trust-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.lp-trust-strip .lp-trust-item::before {
  content: "•";
  color: var(--saip-teal-500);
  font-size: 18px;
  line-height: 1;
}

/* Hero illustration card (decorative dashboard preview) */

.lp-hero-art {
  position: relative;
  min-height: 380px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lp-dashboard-card {
  background: var(--saip-glass-light);
  -webkit-backdrop-filter: var(--saip-blur-card);
          backdrop-filter: var(--saip-blur-card);
  border: 1px solid var(--saip-glass-border);
  border-radius: var(--saip-radius-lg);
  box-shadow: var(--saip-shadow-lg);
  padding: var(--saip-spacing-5);
  width: 100%;
  max-width: 420px;
  color: var(--ps-text-primary);
}

.lp-dashboard-card .lp-dc-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ps-text-heading);
}

.lp-dashboard-card .lp-dc-title .lp-dc-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--saip-teal-500);
  display: inline-block;
}

.lp-dashboard-card .lp-dc-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--saip-spacing-3);
  font-size: 13px;
}

.lp-dashboard-card .lp-dc-row .lp-dc-label {
  color: var(--ps-text-secondary);
}

.lp-dashboard-card .lp-dc-row .lp-dc-value {
  color: var(--ps-text-primary);
  font-weight: 600;
}

.lp-dashboard-card .lp-dc-findings {
  margin-top: var(--saip-spacing-4);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  overflow: hidden;
  background: var(--ps-bg-surface);
}

.lp-dashboard-card .lp-dc-finding {
  display: flex;
  align-items: center;
  gap: var(--saip-spacing-3);
  padding: 10px 12px;
  font-size: 13px;
  border-bottom: 1px solid var(--ps-border-hairline);
}

.lp-dashboard-card .lp-dc-finding:last-child {
  border-bottom: none;
}

.lp-dashboard-card .lp-dc-finding .lp-dc-finding-id {
  font-weight: 600;
  color: var(--ps-text-heading);
}

.lp-dashboard-card .lp-dc-finding .lp-dc-finding-body {
  flex: 1;
  color: var(--ps-text-primary);
}

.lp-sev-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--saip-radius-sm);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.lp-sev-critical {
  background: var(--saip-rose-500);
  color: var(--saip-ice-0);
}

.lp-sev-major {
  background: var(--saip-amber-50);
  color: var(--saip-amber-500);
}

.lp-sev-minor {
  background: var(--saip-ice-100);
  color: var(--ps-text-slate);
}

.lp-sev-info {
  background: var(--saip-teal-50);
  color: var(--saip-teal-600);
}

/* ============================================================
   Cards / grids
   ============================================================ */

.lp-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
}

.lp-grid-4 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
}

.lp-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
}

.lp-grid-6 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
}

.lp-card {
  border-radius: var(--saip-radius-lg);
  padding: var(--saip-spacing-6);
  background: var(--ps-bg-surface-muted);
  border: 1px solid var(--ps-border-hairline);
  box-shadow: var(--saip-shadow-md);
}

.lp-section-dark .lp-card,
.lp-section-slate .lp-card {
  background: var(--saip-glass-dark);
  -webkit-backdrop-filter: var(--saip-blur-card);
          backdrop-filter: var(--saip-blur-card);
  border-color: var(--saip-glass-border);
  color: var(--saip-ice-0);
}

.lp-card h3 {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin: 0 0 var(--saip-spacing-3) 0;
}

.lp-card p {
  font-size: 15px;
  line-height: 1.6;
  margin: 0;
  color: inherit;
  opacity: 0.92;
}

.lp-section-light .lp-card p {
  color: var(--ps-text-primary);
  opacity: 1;
}

.lp-card-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--saip-teal-50);
  color: var(--saip-teal-600);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 17px;
  margin-bottom: var(--saip-spacing-4);
}

.lp-section-dark .lp-card-icon,
.lp-section-slate .lp-card-icon {
  background: rgba(31, 168, 171, 0.18);
  color: var(--saip-teal-500);
}

/* ============================================================
   How it works (timeline)
   ============================================================ */

.lp-timeline {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--saip-spacing-4);
  margin-top: var(--saip-spacing-6);
}

.lp-step {
  position: relative;
  padding: var(--saip-spacing-5);
  background: var(--saip-glass-dark);
  -webkit-backdrop-filter: var(--saip-blur-card);
          backdrop-filter: var(--saip-blur-card);
  border: 1px solid var(--saip-glass-border);
  border-radius: var(--saip-radius-lg);
}

.lp-step .lp-step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
  font-weight: 700;
  font-size: 13px;
  margin-bottom: var(--saip-spacing-3);
}

.lp-step h3 {
  font-size: 16px;
  margin: 0 0 var(--saip-spacing-2) 0;
  color: var(--saip-ice-0);
}

.lp-step p {
  font-size: 13px;
  color: var(--ps-text-muted);
  line-height: 1.5;
  margin: 0;
}

/* ============================================================
   Trust block (2-col facts grid)
   ============================================================ */

.lp-facts-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
  margin-top: var(--saip-spacing-6);
}

.lp-fact {
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-lg);
  padding: var(--saip-spacing-5);
  box-shadow: var(--saip-shadow-sm);
}

.lp-fact h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 var(--saip-spacing-2) 0;
  color: var(--ps-text-heading);
}

.lp-fact p {
  font-size: 14px;
  color: var(--ps-text-primary);
  margin: 0;
  line-height: 1.55;
}

/* ============================================================
   License block (4-card grid)
   ============================================================ */

.lp-license-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--saip-spacing-4);
  margin-top: var(--saip-spacing-6);
}

.lp-license-card {
  background: var(--saip-glass-dark);
  -webkit-backdrop-filter: var(--saip-blur-card);
          backdrop-filter: var(--saip-blur-card);
  border: 1px solid var(--saip-glass-border);
  border-radius: var(--saip-radius-lg);
  padding: var(--saip-spacing-5);
  color: var(--saip-ice-0);
}

.lp-license-card .lp-license-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(31, 168, 171, 0.18);
  color: var(--saip-teal-500);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin-bottom: var(--saip-spacing-3);
}

.lp-license-card h3 {
  font-size: 18px;
  margin: 0 0 var(--saip-spacing-2) 0;
  color: var(--saip-ice-0);
}

.lp-license-card p {
  font-size: 13px;
  color: var(--ps-text-muted);
  line-height: 1.55;
  margin: 0;
}

/* ============================================================
   Final CTA band
   ============================================================ */

.lp-final-cta {
  background: var(--saip-gradient-hero);
  padding: var(--saip-spacing-8) 0;
  text-align: center;
  position: relative;
  overflow: hidden;
}

.lp-final-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 50% 100%, rgba(31, 168, 171, 0.20) 0%, transparent 50%);
  pointer-events: none;
}

.lp-final-cta .lp-final-card {
  position: relative;
  background: var(--saip-glass-dark);
  -webkit-backdrop-filter: var(--saip-blur-card);
          backdrop-filter: var(--saip-blur-card);
  border: 1px solid var(--saip-glass-border);
  border-radius: var(--saip-radius-xl);
  padding: var(--saip-spacing-7) var(--saip-spacing-6);
  margin: 0 auto;
  max-width: 720px;
  color: var(--saip-ice-0);
  box-shadow: var(--saip-shadow-lg);
}

.lp-final-card h2 {
  margin-bottom: var(--saip-spacing-3);
}

.lp-final-card p {
  color: var(--ps-text-muted);
  font-size: 16px;
  margin: 0 0 var(--saip-spacing-5) 0;
}

.lp-final-card .lp-cta-row {
  justify-content: center;
}

/* ============================================================
   Footer
   ============================================================ */

.lp-footer {
  background: var(--saip-navy-900);
  color: var(--ps-text-muted);
  padding: var(--saip-spacing-7) 0 var(--saip-spacing-5) 0;
  font-size: 13px;
}

.lp-footer-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--saip-spacing-5);
}

.lp-footer-grid h4 {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--saip-ice-0);
  margin: 0 0 var(--saip-spacing-3) 0;
}

.lp-footer-grid p,
.lp-footer-grid li {
  margin: 0 0 6px 0;
  color: var(--ps-text-muted);
  line-height: 1.55;
}

.lp-footer-grid ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.lp-footer-grid a {
  color: var(--ps-text-muted);
}

.lp-footer-grid a:hover {
  color: var(--saip-ice-0);
  text-decoration: none;
}

.lp-footer-baseline {
  margin-top: var(--saip-spacing-6);
  padding-top: var(--saip-spacing-4);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  font-size: 12px;
  color: var(--ps-text-muted);
  display: flex;
  justify-content: space-between;
  gap: var(--saip-spacing-3);
  flex-wrap: wrap;
}

/* ============================================================
   In-page license banner pass-through (logged-in)
   ============================================================ */

.lp-license-banner {
  margin: var(--saip-spacing-5) 0 0 0;
  padding: 14px 18px;
  border-radius: var(--saip-radius);
  background: var(--saip-amber-50);
  color: var(--ps-text-primary);
  font-size: 14px;
  line-height: 1.55;
  border: 1px solid #f0c987;
}

.lp-license-banner.is-suspended {
  background: var(--saip-rose-50);
  border-color: #f0a8a0;
}

.lp-license-banner.is-pending {
  background: var(--saip-ice-100);
  border-color: var(--ps-text-muted);
}

.lp-license-banner.is-info {
  background: var(--saip-teal-50);
  border-color: #b6dcdd;
}

/* ============================================================
   Responsive breakpoints
   ============================================================ */

@media (max-width: 1024px) {
  .lp-hero-inner {
    grid-template-columns: 1fr;
    gap: var(--saip-spacing-7);
  }
  .lp-hero h1 {
    font-size: 44px;
  }
  .lp-hero-art {
    min-height: 280px;
  }
  .lp-timeline {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .lp-license-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .lp-grid-6 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .lp-footer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .lp-section {
    padding: var(--saip-spacing-7) 0;
  }
  .lp-hero {
    padding: var(--saip-spacing-7) 0;
  }
  .lp-hero h1 {
    font-size: 36px;
  }
  .lp-section-header h2,
  .lp-h2 {
    font-size: 26px;
  }
  .lp-grid-3,
  .lp-grid-2,
  .lp-grid-6,
  .lp-grid-4,
  .lp-license-grid,
  .lp-facts-grid,
  .lp-timeline,
  .lp-footer-grid {
    grid-template-columns: 1fr;
  }
  .lp-header .lp-header-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--saip-spacing-3);
  }
  .lp-nav {
    gap: var(--saip-spacing-2);
  }
  .lp-footer-baseline {
    flex-direction: column;
  }
}

/* ============================================================
   Reduced motion + reduced transparency
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .lp-root *,
  .lp-root *::before,
  .lp-root *::after {
    transition: none !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .lp-dashboard-card,
  .lp-step,
  .lp-license-card,
  .lp-final-cta .lp-final-card,
  .lp-section-dark .lp-card,
  .lp-section-slate .lp-card {
    background: var(--saip-navy-700);
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
  }
  .lp-header {
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
    background: var(--saip-navy-800);
  }
}

/* ============================================================
   Focus visible (a11y)
   ============================================================ */

.lp-root a:focus-visible,
.lp-root button:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 3px;
  border-radius: var(--saip-radius-sm);
}

/* ============================================================
   ============================================================
   Phase 67F — authenticated app inner UI (.app-root)
   ============================================================
   Distinct scope so the public landing (.lp-root) stays untouched.
   ============================================================ */

.app-root {
  background: var(--ps-bg-canvas);
  color: var(--ps-text-primary);
  font-family: var(--saip-font-ui);
  line-height: 1.55;
  font-size: var(--saip-text-md);
  margin: 0;
  padding: 0;
}

.app-root *,
.app-root *::before,
.app-root *::after {
  box-sizing: border-box;
}

.app-root a {
  color: var(--ps-link);
  text-decoration: none;
}

.app-root a:hover {
  text-decoration: underline;
}

.app-root code {
  font-family: var(--saip-font-mono);
  font-size: 12.5px;
}

.app-root pre {
  font-family: var(--saip-font-mono);
  font-size: 12.5px;
  background: var(--ps-bg-inset);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-sm);
  padding: 12px 14px;
  overflow: auto;
  margin: 8px 0;
  line-height: 1.5;
}

/* W5 — headings are anchored to the canonical type scale AND the UI family,
   so no surface can fall back to a browser default or inherit a stray family. */
.app-root h1,
.app-root h2,
.app-root h3,
.app-root h4,
.app-root h5,
.app-root h6 {
  font-family: var(--saip-font-ui);
}

.app-root h1 {
  font-size: var(--saip-text-h1);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin: 0 0 8px 0;
  color: var(--saip-ice-0);
}

.app-root h2 {
  font-size: var(--saip-text-h2);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin: 0 0 var(--saip-spacing-4) 0;
  color: var(--ps-text-heading);
}

.app-root h3 {
  font-size: var(--saip-text-h3);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.3;
  margin: var(--saip-spacing-5) 0 var(--saip-spacing-3) 0;
  color: var(--ps-text-heading);
}

.app-root h4 {
  font-size: var(--saip-text-md);
  font-weight: 600;
  margin: var(--saip-spacing-4) 0 var(--saip-spacing-2) 0;
  color: var(--ps-text-heading);
}

.app-root p {
  margin: 0 0 var(--saip-spacing-3) 0;
}

.app-root ul,
.app-root ol {
  margin: 0 0 var(--saip-spacing-3) 0;
  padding-left: 22px;
}

.app-root ul li,
.app-root ol li {
  line-height: 1.6;
  margin-bottom: 4px;
}

.app-root .app-wrap {
  /* AUD6835D-F19 — the app container is 1760px on EVERY authenticated pillar
     (Sponsor 2026-07-16: harmonise page width across pillars, Profile Cockpit
     as the reference — the 1760px value is Sponsor-locked).
     UX-GLOBAL-AUTHENTICATED-SHELL-ALIGNMENT-01 — this is THE authenticated
     application shell, now token-driven. The former per-breakpoint gutter
     overrides (20px <=1440, 10px <=1180, 16px <=720) are replaced by the
     single --ps-auth-shell-gutter clamp so every viewport shares one gutter
     and the shell content edges are the canonical rails (see the token
     block). Per-pillar OUTER shells are abolished; feature-internal layouts
     (reading columns, forms, split workspaces) stay deliberate INSIDE.
     The landing/marketing pages use .lp-container (1180px), unchanged. */
  max-width: var(--ps-auth-shell-max-width);
  margin-inline: auto;
  padding: 0 var(--ps-auth-shell-gutter);
}

.app-root .app-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--saip-navy-800);
  color: var(--saip-ice-0);
  padding: 8px 12px;
  z-index: 99;
}

.app-root .app-skip-link:focus {
  left: 8px;
  top: 8px;
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}

/* W0 a11y floor (G3) — visually-hidden utility for the per-page <h1> that
   restores the document outline (header band was trimmed in 2026-06-18, leaving
   pages starting at <h2>). Content stays in the accessibility tree for screen
   readers; it is removed from the visual layout without disabling focusability. */
.app-vh {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

/* UXCOMP-WAHOU-01 (dn §1.7) — generic no-break utility for short unit tokens
   ("10.0 MiB") that must never wrap mid-value on narrow viewports. */
.app-nowrap { white-space: nowrap; }

/* -----------------------------------------------------------
   Header
   ----------------------------------------------------------- */

.app-root .app-header {
  background: var(--ps-chrome-bg);   /* F3: IDEMIA violet chrome — was cta-navy */
  color: var(--ps-chrome-fg);
  padding: var(--saip-spacing-5) 0 var(--saip-spacing-3) 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.app-root .app-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--saip-spacing-4);
  flex-wrap: wrap;
}

.app-root .app-wordmark {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  color: var(--ps-chrome-fg);
  text-decoration: none;
  font-weight: 700;
  font-size: 26px;             /* Profile Sentinel wordmark — bigger, beneath IDEMIA */
  letter-spacing: -0.01em;
}

/* F3 IDEMIA co-brand lockup — IDEMIA (smaller) stacked ABOVE the Profile
   Sentinel wordmark (bigger), matching the landing co-brand (home.css
   .brand / .idemia-top / .ist-txt). Token-driven on --ps-chrome-*; the
   chevron white/periwinkle + periwinkle-lite sub-label read on the violet
   chrome (white ≈12:1, periwinkle-lite ≈6:1 on the dark #430099 end). */
.app-root .app-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
}
.app-root .app-idemia {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.app-root .app-idemia-chev {
  height: 15px;
  width: auto;
  display: block;
}
.app-root .app-idemia-txt {
  display: flex;
  flex-direction: column;
  line-height: 1;
}
.app-root .app-idemia-txt b {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--ps-chrome-fg);
}
.app-root .app-idemia-txt span {
  font-size: 7.5px;
  letter-spacing: 0.15em;
  color: var(--saip-violet-300);
  margin-top: 2px;
}

/* Copie-conforme chrome — signed-in identity in the header. F3: the identity
   sits on the light (periwinkle) end of the violet chrome gradient, so a
   faint violet-ink scrim keeps the 12px text AA (white ≈6.4:1 · muted
   ≈4.7:1) regardless of the gradient position. */
.app-root .app-header-user {
  margin-left: auto;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.86);
  background: rgba(14, 4, 32, 0.30);
  padding: 5px 12px;
  border-radius: 999px;
  /* identity on line 1, the theme switch on line 2 (Sponsor 2026-07-23). */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
}

/* Colour-theme switch in the navy header — a compact segmented pill. Always
   on the always-navy header, so it uses fixed light-on-navy tones (not the
   theme-flipping tokens) by design. */
.app-root .app-theme-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.app-root .app-theme-opt {
  appearance: none;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, 0.80);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  padding: 3px 10px;
  border-radius: 999px;
  cursor: pointer;
}
.app-root .app-theme-opt:hover { color: #fff; }
.app-root .app-theme-opt[aria-pressed="true"] {
  background: #fff;
  color: var(--ps-text-heading);
}
.app-root .app-theme-opt:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 1px;
}

.app-root .app-header-user strong {
  color: var(--ps-chrome-fg);
  font-weight: 600;
}

.app-root .app-header-user .app-auth-role {
  background: var(--saip-violet-600);   /* F3: de-teal — white ≈12.4:1 */
  color: var(--saip-ice-0);
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* M22-BRAND-02-R1 — brand logo sizing tokens are defined once in the main
   :root block above (W5: the former second :root here was folded in so the
   design system has a single token root). */

/* M22-BRAND-02 / R1 — Profile Sentinel SVG mark replaces the legacy text-square
   glyph. Decorative (alt=""); the adjacent "Profile Sentinel" text remains the
   accessible label. R1: larger + a subtle teal-edged backing lozenge so the
   mark reads strongly on the dark navy header and never looks thin/invisible. */
.app-root .app-wordmark .app-wordmark-mark {
  width: var(--ps-logo-app-header);
  height: var(--ps-logo-app-header);
  display: block;
  flex: 0 0 auto;
  padding: 5px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);            /* F3: white glass — was teal */
  box-shadow: inset 0 0 0 1px rgba(169, 178, 255, 0.30);  /* F3: periwinkle edge */
}

/* M22-BRAND-02 / R1 — Clause SVG mark, used next to "Clause" titles in the
   embedded panel header and on the /clause console lead. Decorative (alt=""). */
.app-root .clause-mark {
  display: inline-block;
  flex: 0 0 auto;
  vertical-align: middle;
}

.app-root .app-wordmark-sub {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ps-text-muted);
  margin-top: 2px;
  display: block;
  font-weight: 500;
}

.app-root .app-header-title {
  margin: var(--saip-spacing-4) 0 0 0;
}

.app-root .app-header-title h1 {
  color: var(--saip-ice-0);
}

.app-root .app-header-sub {
  font-size: 12px;
  color: var(--ps-text-muted);
  margin-top: 2px;
}

/* -----------------------------------------------------------
   Top navigation
   ----------------------------------------------------------- */

.app-root .app-nav {
  background: var(--saip-violet-700);   /* F3: deep IDEMIA violet nav band — was navy-800 */
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  padding: 0;
  position: sticky;
  top: 0;
  z-index: 40;
}

.app-root .app-nav-inner {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 8px 0;
  /* UX-GLOBAL-AUTHENTICATED-SHELL-ALIGNMENT-01 — nav links carry 12px
     horizontal padding, so without correction the link TEXT sits 12px
     inside the shell content edge while the lockup / identity pill / bands
     / sections sit flush. Bleeding the bar by exactly that padding puts the
     first label's text (the Dashboard "D") and the last label's text (the
     Sign-out "t") ON the shell content edges — the canonical rails. The
     hover pill extends into the gutter (16px floor > 12px bleed, so this
     can never overflow the viewport). */
  margin-inline: calc(-1 * var(--ps-auth-shell-nav-bleed));
}

.app-root .app-nav a {
  color: var(--saip-ice-0);
  text-decoration: none;
  font-weight: 500;
  font-size: 13px;
  padding: 8px 12px;
  border-radius: var(--saip-radius-sm);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background 200ms ease-out;
}

.app-root .app-nav a:hover {
  background: rgba(255, 255, 255, 0.08);
  text-decoration: none;
}

.app-root .app-nav a.is-active {
  /* F3: periwinkle active pill + violet-ink text (≈5.5:1 AA, echoes the brand
     gradient end) — a clear "you are here" on the deep-violet nav (was teal). */
  background: var(--saip-violet-500);
  color: var(--saip-violet-ink);
}

.app-root .app-nav .app-nav-spacer {
  flex: 1;
}

/* Auth strip — small bar under the nav for sign-in state + license pill. */

/* UX-REFONTE-W1 (B4) — Profile Delta sub-nav (Compare / Reference library /
   Fleet query). The active link is distinguished by MORE than colour (weight +
   teal underline) so the "you are here" cue holds for sighted users and passes
   WCAG SC 1.4.1; AT gets it via aria-current="page". */
.app-root .app-subnav {
  display: flex;
  gap: var(--saip-spacing-4);
  flex-wrap: wrap;
  margin: 0 0 var(--saip-spacing-5);
  font-size: var(--saip-text-sm);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding-bottom: var(--saip-spacing-2);
}
.app-root .app-subnav a {
  /* FT-DARK-TEXT — resting tab labels flip so they stay AA on the dark canvas.
     grey-600 IS --ps-text-secondary's light value, so light mode is unchanged. */
  color: var(--ps-text-secondary);
  text-decoration: none;
  padding-bottom: var(--saip-spacing-1);
  border-bottom: 2px solid transparent;
}
.app-root .app-subnav a:hover { color: var(--ps-link); }
.app-root .app-subnav a.is-active {
  /* FT-DARK-TEXT — the ACTIVE tri-modal tab ("Compare two profiles") was
     navy-900 on the dark canvas = invisible in dark. --ps-text-heading flips
     to near-white in dark and is byte-identical navy in light; the brand
     underline routes through --ps-link so it stays visible in dark too. */
  color: var(--ps-text-heading);
  font-weight: 700;
  border-bottom-color: var(--ps-link);
}

.app-root .app-auth-strip {
  background: var(--ps-bg-surface-muted);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding: 6px 0;
  font-size: 12.5px;
}

.app-root .app-auth-strip-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--saip-spacing-3);
  flex-wrap: wrap;
}

.app-root .app-auth-strip strong {
  color: var(--ps-text-heading);
}

.app-root .app-auth-strip .app-auth-role {
  display: inline-block;
  background: var(--saip-ice-100);
  color: var(--ps-text-heading-brand);
  padding: 2px 8px;
  border-radius: var(--saip-radius-sm);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--ps-border-strong);
  margin-left: 6px;
}

.app-root .app-auth-strip .app-auth-signin {
  color: var(--saip-rose-500);
  font-weight: 600;
}

.app-root .app-auth-strip .app-auth-link {
  color: var(--saip-violet-600);   /* F3: de-teal (dev-only auth strip) */
  font-weight: 600;
  text-decoration: none;
  padding: 2px 10px;
  border: 1px solid var(--ps-border-strong);
  border-radius: var(--saip-radius-sm);
  background: var(--ps-bg-surface);
}

.app-root .app-auth-strip .app-auth-link:hover {
  background: var(--saip-violet-50);   /* F3: de-teal */
  text-decoration: none;
}

/* Mobile drawer (CSS-only via <details>) — appears on narrow viewports. */

.app-root .app-nav-drawer {
  display: none;
  margin: 0;
}

.app-root .app-nav-drawer summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.08);
  color: var(--saip-ice-0);
  border-radius: var(--saip-radius-sm);
  font-size: 14px;
  font-weight: 600;
}

.app-root .app-nav-drawer summary::-webkit-details-marker {
  display: none;
}

.app-root .app-nav-drawer[open] summary::after {
  content: "▴";
  margin-left: 4px;
}

.app-root .app-nav-drawer summary::after {
  content: "▾";
  margin-left: 4px;
}

@media (max-width: 880px) {
  .app-root .app-nav-inner {
    display: none;
  }
  .app-root .app-nav-drawer {
    display: block;
    padding: 8px 0;
  }
  .app-root .app-nav-drawer .app-nav-drawer-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 8px;
    background: var(--saip-navy-700);
    border-radius: var(--saip-radius);
    padding: 8px;
  }
  .app-root .app-nav-drawer .app-nav-drawer-list a {
    color: var(--saip-ice-0);
    text-decoration: none;
    padding: 10px 12px;
    border-radius: var(--saip-radius-sm);
    font-size: 14px;
    font-weight: 500;
  }
  .app-root .app-nav-drawer .app-nav-drawer-list a.is-active {
    background: var(--saip-violet-500);   /* F3: parity with desktop active pill */
    color: var(--saip-violet-ink);
  }
  .app-root .app-nav-drawer .app-nav-drawer-list a:hover {
    background: rgba(255, 255, 255, 0.08);
  }
}

/* -----------------------------------------------------------
   License pill in the auth strip
   ----------------------------------------------------------- */

.app-root .app-lic-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  border: 1px solid var(--ps-border-strong);
  margin-left: 6px;
  text-decoration: none;
  vertical-align: middle;
}

.app-root .app-lic-pill-active {
  background: var(--saip-green-50);
  color: var(--saip-green-500);
  border-color: #bcdcc4;
}

.app-root .app-lic-pill-soon {
  background: var(--saip-amber-50);
  color: var(--saip-amber-500);
  border-color: #f0c987;
}

.app-root .app-lic-pill-expired {
  background: var(--saip-rose-50);
  color: var(--saip-rose-500);
  border-color: #f0a8a0;
}

.app-root .app-lic-pill-suspended {
  background: #f8e1ec;
  color: #7a1a3a;
  border-color: #e7b8c8;
}

.app-root .app-lic-pill-pending {
  background: var(--saip-ice-100);
  color: var(--ps-text-heading-brand);
  border-color: var(--ps-text-muted);
}

/* -----------------------------------------------------------
   Page-level layout
   ----------------------------------------------------------- */

.app-root .app-main {
  padding: var(--saip-spacing-5) 0 var(--saip-spacing-7) 0;
  min-height: 60vh;
}

.app-root .app-breadcrumb {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  font-size: 12px;
  margin: 0 0 var(--saip-spacing-4) 0;
}

.app-root .app-breadcrumb a {
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-sm);
  padding: 4px 10px;
  color: var(--ps-text-heading);
  font-weight: 600;
}

.app-root .app-breadcrumb a:hover {
  background: var(--ps-sev-info-bg);
  text-decoration: none;
  border-color: var(--ps-border-strong);
}

.app-root .app-breadcrumb .sep {
  color: var(--ps-border-strong);
  font-weight: 600;
}

.app-root .app-breadcrumb code {
  font-family: var(--saip-font-mono);
  color: var(--ps-text-primary);
  background: transparent;
  font-size: 12px;
}

/* -----------------------------------------------------------
   Section / card primitives
   ----------------------------------------------------------- */

/* HANDOFF §2.2 — cards: radius 12px, hairline border, soft shadow. */
.app-root .app-section {
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  box-shadow: var(--ps-shadow-card);
  padding: var(--saip-spacing-5) var(--saip-spacing-6);
  margin: 0 0 var(--saip-spacing-5) 0;
}

/* Non-compliant content card: rose border + 3px left accent. */
.app-root .app-section.is-noncompliant,
.app-root .app-card-noncompliant {
  border-color: #f0a8a0;
  border-left: 3px solid var(--saip-rose-500);
}

/* Absent / not-declared object: dashed border. */
.app-root .app-section.is-absent,
.app-root .app-card-absent {
  border-style: dashed;
}

.app-root .app-section h2 {
  border-bottom: 1px solid var(--ps-border-hairline);
  padding-bottom: var(--saip-spacing-3);
  margin: 0 0 var(--saip-spacing-4) 0;
}

.app-root .app-section h3 {
  margin-top: var(--saip-spacing-4);
}

.app-root .app-section-tight {
  padding: var(--saip-spacing-4) var(--saip-spacing-5);
}

/* UX-GLOBAL-AUTHENTICATED-SHELL-ALIGNMENT-01 — named page-layout variants.
   Top-level surfaces span the shell BY DEFAULT; a page that deliberately
   narrows its top-level surface uses one of these named variants (never an
   inline max-width — the shell guard test bans those). Both are
   left-anchored at the rail (no auto-centring): the right-hand space is the
   deliberate whitespace of a narrow flow, and the left rail contract holds.
   --form: focused form / wizard / confirm flows (Studio wizard, delete
   confirm). --reading: long-form prose pages (sanctioned option; document
   consumers in docs/product/AUTHENTICATED_APPLICATION_LAYOUT_SYSTEM.md). */
.app-root .ps-page-layout--form {
  max-width: 760px;
}
.app-root .ps-page-layout--reading {
  max-width: 920px;
}

/* ABOUT-REFRESH-01 — /about is a long-form prose page whose top-level
   sections SPAN the shell (rail contract, spans_both_rails=true); the
   readable line length is capped INSIDE the cards instead — the sanctioned
   internal reading measure (see the shell guard: "reading measures … are
   the sanctioned INTERNAL patterns"). scroll-margin clears the sticky
   primary nav for the #security / #data-protection / … deep-link anchors
   (same clearance family as the other anchored surfaces, 84px). */
.app-root .app-about-sec {
  scroll-margin-top: 84px;
}

.app-root .app-about-sec p,
.app-root .app-about-sec li {
  max-width: 82ch;
  line-height: 1.65;
}

.app-root .app-section-muted {
  background: var(--ps-bg-surface-muted);
}

.app-root .app-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--saip-spacing-3);
  margin-bottom: var(--saip-spacing-3);
}

.app-root .app-stat-card {
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  box-shadow: var(--ps-shadow-card);
  padding: var(--saip-spacing-4) var(--saip-spacing-4);
}

.app-root .app-stat-card .app-stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ps-text-secondary);
  font-weight: 600;
}

.app-root .app-stat-card .app-stat-value {
  font-size: 18px;
  font-weight: 600;
  color: var(--ps-text-heading);
  margin-top: 4px;
  word-break: break-word;
}

.app-root .app-stat-card .app-stat-value a {
  color: var(--ps-link);
}

/* Status-coloured stat cards (severity overview). */
.app-root .app-stat-card.is-pass {
  background: var(--saip-green-50);
  border-color: #bcdcc4;
}
.app-root .app-stat-card.is-pass .app-stat-value {
  color: var(--saip-green-500);
}
.app-root .app-stat-card.is-critical {
  background: var(--saip-rose-50);
  border-color: #f0a8a0;
}
.app-root .app-stat-card.is-critical .app-stat-value {
  color: var(--saip-rose-500);
}
.app-root .app-stat-card.is-major {
  background: var(--saip-amber-50);
  border-color: #f0c987;
}
.app-root .app-stat-card.is-major .app-stat-value {
  color: var(--saip-amber-500);
}
.app-root .app-stat-card.is-minor {
  background: var(--saip-amber-50);
  border-color: #ead7af;
}
.app-root .app-stat-card.is-minor .app-stat-value {
  color: var(--ps-sev-minor-fg); /* FT-DC-PILL-CONTRAST-AA-01: old olive ≈2.5:1 failed AA */
}
.app-root .app-stat-card.is-info {
  background: var(--saip-teal-50);
  border-color: #b6dcdd;
}
.app-root .app-stat-card.is-info .app-stat-value {
  color: var(--saip-teal-600);
}
.app-root .app-stat-card.is-skipped {
  background: var(--saip-grey-200);
  border-color: var(--ps-text-muted);
}
.app-root .app-stat-card.is-skipped .app-stat-value {
  color: var(--ps-text-secondary);
}
.app-root .app-stat-card.is-error {
  background: var(--saip-grey-200);
  border-color: var(--ps-text-muted);
}
.app-root .app-stat-card.is-error .app-stat-value {
  color: var(--ps-text-primary);
}

/* -----------------------------------------------------------
   Tables
   ----------------------------------------------------------- */

.app-root table.app-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  overflow: hidden;
  font-size: 13px;
}

.app-root table.app-table th,
.app-root table.app-table td {
  text-align: left;
  vertical-align: top;
  padding: 10px 12px;
  border-bottom: 1px solid var(--ps-border-hairline);
}

/* ── Shared history-row grammar (Design Council T9, 2026-07-02) ─────────────
   One column dialect for every pillar history table (_history_cells.html):
   name-first row header (semibold, with the opaque id demoted to a mono
   sub-line), muted facts, human_dt dates, right-most nowrap actions. These
   classes replace the copy-pasted inline font/color styles on new markup. */
.app-root table.app-table th.app-cell-name {
  /* the <th scope="row"> name cell keeps BODY styling — it is a row header,
     not a column header (the column-header rules below are scoped to thead). */
  background: transparent;
  color: var(--ps-text-primary);
  font-weight: 600;
  font-size: var(--saip-text-sm);
  letter-spacing: normal;
  text-transform: none;
}
.app-root .app-cell-name .app-cell-id {
  display: block;
  margin-top: 2px;
  font-family: var(--saip-font-mono);
  font-size: var(--saip-text-2xs);
  font-weight: 400;
  color: var(--ps-text-secondary);
}
.app-root .app-cell-date {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-primary);
  white-space: nowrap;
}
.app-root .app-cell-muted {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-secondary);
}
.app-root .app-cell-actions { white-space: nowrap; }

/* HANDOFF §2.1 — table headers are always light; navy is chrome-only. */
.app-root table.app-table th {
  background: var(--ps-bg-table-head);
  color: var(--ps-text-secondary);
  font-weight: 700;
  font-size: 9.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--ps-border-hairline);
}

.app-root table.app-table tbody tr:nth-child(even) {
  background: var(--ps-bg-surface-muted);
}

.app-root table.app-table tbody tr:hover {
  background: var(--saip-row-hover);
}

.app-root table.app-table tbody tr:last-child td {
  border-bottom: none;
}

/* Responsive table wrapper for overflow on small screens. */
.app-root .app-table-wrap {
  overflow-x: auto;
  border-radius: var(--saip-radius);
  margin: 0 0 var(--saip-spacing-3) 0;
}

/* MR-M3-06 / BETA2-WP-13 — keep the row-action column (the last cell) always
   reachable. The date-heavy admin tables overflow horizontally, pushing the
   "Open"/action control off-screen and forcing a horizontal scroll (BETA2-016/
   023). Pin the last column sticky to the right edge of the scroll viewport so
   the action stays visible without scrolling. Opaque, row-matched backgrounds
   keep the scrolled-under data from showing through; a soft left shadow marks
   the seam. Sticky only engages when the table overflows, so non-overflowing
   tables are visually unchanged. Scoped to .app-table-wrap > table.app-table
   (the Profile Explorer filesystem table uses .pe-fs-table-wrap, untouched). */
/* The base table sets overflow:hidden (border-radius clip); that establishes a
   clip context which prevents the cells below from sticking to the OUTER
   .app-table-wrap scroll container. Inside the wrap, let the table overflow be
   visible so position:sticky resolves against the wrap (the wrap keeps the
   rounded corners + the horizontal scroll). */
.app-root .app-table-wrap table.app-table {
  overflow: visible;
}
.app-root .app-table-wrap table.app-table th:last-child,
.app-root .app-table-wrap table.app-table td:last-child {
  position: sticky;
  right: 0;
  z-index: 1;
  box-shadow: -6px 0 6px -6px rgba(0, 0, 0, 0.18);
}
.app-root .app-table-wrap table.app-table thead th:last-child {
  background: var(--ps-bg-table-head);
  z-index: 2;
}
.app-root .app-table-wrap table.app-table tbody td:last-child {
  background: var(--ps-bg-surface);
}
.app-root .app-table-wrap table.app-table tbody tr:nth-child(even) td:last-child {
  background: var(--ps-bg-surface-muted);
}
.app-root .app-table-wrap table.app-table tbody tr:hover td:last-child {
  /* opaque blend of --saip-row-hover over white (sticky cell must hide
     the content scrolling underneath it) */
  background: #F5FBFB;
}

/* MR-M3-10 / BETA2-014 / SIGNIN-REFINE-01 — in-field password reveal
   (wrapper + toggle built by /static/js/password_reveal.js around every
   password input; a11y M1-M8). The wrapper must never clip the focus ring
   (no overflow:hidden); the input reserves right padding so typed text
   never runs under the control. Semantic tokens so dark mode repaints. */
.app-root .pw-field {
  position: relative;
  display: block;
}
.app-root .pw-field input {
  width: 100%;
  padding-right: 118px;
}
.app-root .pw-reveal-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  min-height: 32px;
  min-width: 32px;
  padding: 4px 10px;
  font-size: var(--saip-text-sm);
  font-weight: 600;
  font-family: var(--saip-font-ui);
  cursor: pointer;
  background: transparent;
  border: none;
  border-radius: var(--saip-radius-sm);
  color: var(--ps-text-secondary);
}
.app-root .pw-reveal-toggle:hover {
  color: var(--ps-link);
  background: var(--ps-bg-raised, var(--saip-violet-50));
}
/* Exactly ONE reveal affordance: suppress the native Edge eye (a11y M6 —
   3.2.4 Consistent Identification; it duplicated our accessible control). */
.app-root input[type="password"]::-ms-reveal,
.app-root input[type="password"]::-ms-clear {
  display: none;
}

/* -----------------------------------------------------------
   Buttons
   ----------------------------------------------------------- */

.app-root .app-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: var(--saip-radius-sm);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--saip-font-ui);
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  color: var(--ps-text-heading);
  text-decoration: none;
  cursor: pointer;
  transition: transform 200ms ease-out, box-shadow 200ms ease-out, background 200ms ease-out;
  line-height: 1.4;
}

.app-root .app-btn:hover {
  text-decoration: none;
  background: var(--ps-bg-surface-muted);
  box-shadow: var(--saip-shadow-sm);
}

/* SIGNIN-REFINE-01 — large button size step (auth CTAs; replaces the inline
   padding/font-size styles that lived on the sign-in template). */
.app-root .app-btn-lg {
  padding: 11px 22px;
  font-size: var(--saip-text-md);
}

.app-root .app-btn-primary {
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
  border-color: transparent;
}

.app-root .app-btn-primary:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--saip-shadow-md);
}

/* ============================================================
   ASK CLAUSE — the ONE launcher identity (geometry + paint +
   states). Council 2026-07-19 (Sponsor: "les boutons n'ont pas la
   même taille partout — harmonise"): the size drift came from the
   finding/conformance/rule branches also carrying `.app-btn`,
   whose (0,2,0) geometry (padding 8/14, 13px, radius 8) hijacked
   the chip while the earlier block here overrode PAINT only.
   Lives HERE in the globally-loaded design system so every page
   renders it whole, with or without copilot.css (the /rules
   lesson, completed). copilot.css keeps ZERO geometry for these
   classes. Exactly TWO sizes: base pill + `--lg` tile. */
.app-root .clause-ask,
.app-root .cmp-clause-chip,
.app-root .app-btn.clause-ask,
.app-root .app-btn.cmp-clause-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;                 /* ≥24px target (WCAG 2.5.8); dense-row safe */
  padding: 3px 12px;
  border: 1px solid transparent;
  border-radius: 999px;             /* the Clause pill — never radius-sm */
  font-family: var(--saip-font-ui);
  font-size: var(--saip-text-xs);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.01em;
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  vertical-align: middle;
  transition: none;                 /* neutralise .app-btn transform/shadow */
}
.app-root .clause-ask:hover,
.app-root .cmp-clause-chip:hover,
.app-root .app-btn.cmp-clause-chip:hover {
  background: var(--saip-gradient-cta-primary);
  color: var(--saip-ice-0);
  border-color: transparent;
  filter: brightness(0.95);
  box-shadow: none;
}
/* outline-offset 2px is LOAD-BEARING: the ring token equals the chip's own
   gradient start — the offset moves the ring onto the page surface. */
.app-root .clause-ask:focus-visible,
.app-root .cmp-clause-chip:focus-visible,
.app-root .app-btn.cmp-clause-chip:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}
.app-root .cmp-clause-chip-mark {
  color: currentColor;
  font-size: 0.85em;                /* ✦ rides the label at glyph scale */
  line-height: 1;
}
/* Layout modifier (NOT a size): sentence chips fill their row. */
.app-root .cmp-clause-chip--fill {
  flex: 1 1 auto;
  justify-content: flex-start;
  text-align: left;
  white-space: normal;
}
/* The ONE larger size: the At-a-glance / KB tile. Same pill. */
.app-root .clause-ask--lg {
  min-height: 36px;
  padding: 8px 16px;
  font-size: var(--saip-text-sm);
  justify-content: center;
  border-radius: 999px;
}
/* Drawer header title — `.app-root h2` (0,1,1) darkened it to near-invisible
   on the violet band in light mode (measured 1.47:1 → 12.36:1 with ice-0).
   Re-assert EVERY property the global h2 rule also sets. */
.app-root .clause-drawer-title {
  margin: 0;
  font-size: var(--saip-text-h3);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--saip-ice-0);
}

.app-root .app-btn-navy {
  background: var(--saip-gradient-cta-navy);
  color: var(--saip-ice-0);
  border-color: transparent;
}

.app-root .app-btn-navy:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--saip-shadow-md);
}

.app-root .app-btn-ghost {
  background: transparent;
  /* F3 dark defect fix — border/text were fixed light primitives (grey-300 /
     grey-700), so the ghost button (e.g. "Sign in instead", "Home" on the auth
     surfaces) read faded on the dark card. The semantic tier keeps the light
     values (grey-300 / slate-700) and flips both in dark. */
  border: 1px solid var(--ps-border-strong);
  color: var(--ps-text-primary);
}

.app-root .app-btn-ghost:hover {
  background: var(--ps-bg-surface-muted);
}

.app-root .app-btn-destructive {
  background: transparent;
  border-color: var(--saip-rose-500);
  color: var(--saip-rose-500);
}

.app-root .app-btn-destructive:hover {
  background: var(--saip-rose-50);
}

.app-root .app-btn[disabled],
.app-root .app-btn.is-disabled {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* HANDOFF §2.6 — visible focus everywhere in the app scope. */
.app-root :focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}

.app-root .app-btn:focus-visible,
.app-root .app-nav a:focus-visible,
.app-root .app-auth-strip a:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
  border-radius: var(--saip-radius-sm);
}

/* -----------------------------------------------------------
   Forms
   ----------------------------------------------------------- */

.app-root .app-form {
  max-width: 560px;
}

.app-root .app-form-row {
  margin-bottom: var(--saip-spacing-4);
}

.app-root .app-form-row label {
  display: block;
  font-size: 12px;
  color: var(--ps-text-heading);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 4px;
}

.app-root .app-form-row input[type="text"],
.app-root .app-form-row input[type="email"],
.app-root .app-form-row input[type="password"],
.app-root .app-form-row input[type="search"],
.app-root .app-form-row input[type="number"],
.app-root .app-form-row input[type="url"],
.app-root .app-form-row input[type="file"],
.app-root .app-form-row textarea,
.app-root .app-form-row select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--ps-border-strong);
  border-radius: var(--saip-radius-sm);
  font-size: 14px;
  font-family: var(--saip-font-ui);
  background: var(--ps-bg-surface);
  color: var(--ps-text-primary);
}

.app-root .app-form-row textarea {
  min-height: 96px;
  resize: vertical;
}

.app-root .app-form-row input:focus,
.app-root .app-form-row textarea:focus,
.app-root .app-form-row select:focus {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 0;
  border-color: var(--ps-focus-ring);
}

.app-root .app-form-row .app-form-help {
  display: block;
  font-size: 12px;
  color: var(--ps-text-secondary);
  margin-top: 4px;
  line-height: 1.5;
}

.app-root .app-form-row.has-error input,
.app-root .app-form-row.has-error textarea,
.app-root .app-form-row.has-error select {
  border-color: var(--saip-rose-500);
}

.app-root .app-form-row.has-error .app-form-help {
  color: var(--saip-rose-500);
}

.app-root .app-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;   /* SIGNIN-REFINE-01 — baseline-align CTA + quiet link */
  gap: var(--saip-spacing-2);
  margin-top: var(--saip-spacing-4);
}

.app-root .app-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  line-height: 1.5;
}

.app-root .app-checkbox input[type="checkbox"] {
  margin-top: 4px;
}

/* FT-W2-APPEARANCE-PREF-01 — My Profile > Appearance radio row. */
.app-root .app-appearance-fieldset {
  border: none;
  margin: 0;
  padding: 0;
}

.app-root .app-appearance-options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--saip-spacing-3);
}

.app-root .app-appearance-option {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--ps-border-strong);
  border-radius: var(--saip-radius-sm);
  background: var(--ps-bg-surface);
  color: var(--ps-text-primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.app-root .app-appearance-option:has(input:checked) {
  /* Council pass-2 m2: the checked outline rides the AA focus-ring token
     (teal-500 accent was 2.90:1 on white; the native radio dot independently
     conveys the state, this ring is reinforcement). */
  border-color: var(--ps-focus-ring);
  outline: 1px solid var(--ps-focus-ring);
}

/* Phase 68E — responsive reason input for admin lifecycle actions.
   Replaces the hard-coded `width:480px;` that previously overflowed the
   admin user-detail card on small viewports. Box-sizing border-box +
   max-width 100% guarantee the input never extends past its container,
   while a generous min-width keeps short labels readable on wide
   viewports. */
.app-root .app-reason-input {
  display: block;
  width: 100%;
  max-width: 480px;
  box-sizing: border-box;
  padding: 8px 10px;
  font-size: 14px;
  font-family: var(--saip-font-ui);
  border: 1px solid var(--ps-border-strong);
  border-radius: var(--saip-radius-sm);
  background: var(--ps-bg-surface);
  color: var(--ps-text-primary);
  margin-top: 4px;
}

.app-root .app-reason-input:focus {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 0;
  border-color: var(--ps-focus-ring);
}

/* Phase 68E — danger styling for destructive lifecycle actions
   (Disable / Archive / Delete / Cancel-invitation). The .app-btn-danger
   variant inherits .app-btn structure but uses the rose palette for the
   border + label so destructive buttons read as different from primary
   actions. */
.app-root .app-btn-danger {
  /* FT-DC-PILL-CONTRAST-AA-01: rose-500 label on white was ≈4.46:1; the
     --ps-danger-* action tokens are the AA set shared with the PE workspace
     danger button (fg ≈6.62:1 on white). */
  background: var(--ps-bg-surface);
  color: var(--ps-danger-fg);
  border-color: var(--ps-danger-bd);
}

.app-root .app-btn-danger:hover,
.app-root .app-btn-danger:focus {
  background: var(--ps-danger-bg);
  color: var(--saip-ice-0);
  border-color: var(--ps-danger-bd);
}

/* -----------------------------------------------------------
   Banners
   ----------------------------------------------------------- */

/* SIGNIN-REFINE-01 hotfix — the hidden attribute must always win: any
   component that sets its own `display` (e.g. .app-banner's flex) otherwise
   overrides the UA's hidden=display:none and an empty shell renders (the
   Sponsor's red-band catch on the passkey status container). Standard
   defensive rule; JS that reveals by removing the attribute is unaffected. */
.app-root [hidden] {
  display: none !important;
}

.app-root .app-banner {
  display: flex;
  gap: var(--saip-spacing-3);
  padding: 12px 16px;
  border-radius: var(--saip-radius);
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface-muted);
  color: var(--ps-text-primary);
  font-size: 13.5px;
  line-height: 1.55;
  margin-bottom: var(--saip-spacing-4);
}

.app-root .app-banner strong {
  color: var(--ps-text-heading);
}

.app-root .app-banner.is-success {
  background: var(--ps-sev-pass-bg);
  border-color: var(--ps-sev-pass-bd);
}

.app-root .app-banner.is-warning {
  background: var(--ps-sev-major-bg);
  border-color: var(--ps-sev-major-bd);
}

.app-root .app-banner.is-error {
  background: var(--ps-sev-critical-bg);
  border-color: var(--ps-sev-critical-bd);
}

.app-root .app-banner.is-info {
  background: var(--ps-sev-info-bg);
  border-color: var(--ps-sev-info-bd);
}

.app-root .app-banner.is-suspended {
  background: #f8e1ec;
  border-color: #e7b8c8;
}

.app-root .app-banner.is-pending {
  background: var(--ps-bg-canvas);
  border-color: var(--ps-border-strong);
}

.app-root .app-banner .app-banner-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: rgba(11, 26, 51, 0.06);
  color: var(--ps-text-heading);   /* FT-DARK-TEXT — neutral banner glyph flips on the theme-aware banner surface */
  font-weight: 700;
  flex: none;
}

.app-root .app-banner.is-success .app-banner-icon {
  background: rgba(43, 164, 110, 0.18);
  color: var(--saip-green-500);
}

.app-root .app-banner.is-warning .app-banner-icon {
  background: rgba(216, 142, 47, 0.18);
  color: var(--saip-amber-500);
}

.app-root .app-banner.is-error .app-banner-icon,
.app-root .app-banner.is-suspended .app-banner-icon {
  background: rgba(196, 81, 99, 0.18);
  color: var(--saip-rose-500);
}

.app-root .app-banner.is-info .app-banner-icon {
  background: rgba(31, 168, 171, 0.18);
  color: var(--saip-teal-600);
}

.app-root .app-banner-body {
  flex: 1;
}

.app-root .app-banner-body .app-banner-cta {
  margin-top: 6px;
  font-size: 12.5px;
}

.app-root .app-banner-body .app-banner-cta a {
  color: var(--ps-link);
  font-weight: 600;
}

/* -----------------------------------------------------------
   KV layout (read-only key/value)
   ----------------------------------------------------------- */

.app-root .app-kv {
  display: flex;
  gap: var(--saip-spacing-4);
  padding: 6px 0;
  font-size: 13px;
  border-bottom: 1px dashed var(--ps-border-hairline);
}

.app-root .app-kv:last-child {
  border-bottom: none;
}

.app-root .app-kv .app-kv-k {
  flex: 0 0 220px;
  color: var(--ps-text-secondary);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}

.app-root .app-kv .app-kv-v {
  flex: 1;
  color: var(--ps-text-primary);
  word-break: break-word;
}

@media (max-width: 600px) {
  .app-root .app-kv {
    flex-direction: column;
    gap: 2px;
  }
  .app-root .app-kv .app-kv-k {
    flex: none;
  }
}

/* -----------------------------------------------------------
   Status / severity pills
   ----------------------------------------------------------- */

.app-root .app-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--saip-radius-sm);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border: 1px solid transparent;
}

/* Severity pills now consume the --ps-sev-* semantic tokens (single source
   of truth; identical computed values as before → no light regression). The
   [data-tone] variants are the unified API brief 02 collapses every pillar's
   badge idiom onto (.pe-badge-*, .ds-verdict-*, .m34-vt-chip.* migrate to it
   in Wave 2). ok=pass · fail=critical-wash · warn=major · info · neutral. */
.app-root .app-pill-active,
.app-root .app-pill-pass,
.app-root .app-pill-diagnostic-pass,
.app-root .app-pill[data-tone="ok"] {
  background: var(--ps-sev-pass-bg);
  color: var(--ps-sev-pass-fg);
  border-color: var(--ps-sev-pass-bd);
}

.app-root .app-pill-critical {
  /* FT-DC-PILL-CONTRAST-AA-01: solid variant — white on rose-500 was ≈4.46:1
     (borderline fail); the solid token is darkened to ≈5.91:1. */
  background: var(--ps-sev-critical-solid-bg);
  color: var(--saip-ice-0);
}

.app-root .app-pill-major,
.app-root .app-pill-warning,
.app-root .app-pill-diagnostic-findings,
.app-root .app-pill[data-tone="warn"] {
  background: var(--ps-sev-major-bg);
  color: var(--ps-sev-major-fg);
  border-color: var(--ps-sev-major-bd);
}

.app-root .app-pill-minor {
  background: var(--ps-sev-minor-bg);
  color: var(--ps-sev-minor-fg);
  border-color: var(--ps-sev-minor-bd);
}

.app-root .app-pill-info,
.app-root .app-pill-partial-analysis,
.app-root .app-pill-trial,
.app-root .app-pill[data-tone="info"] {
  background: var(--ps-sev-info-bg);
  color: var(--ps-sev-info-fg);
  border-color: var(--ps-sev-info-bd);
}

.app-root .app-pill-skipped,
.app-root .app-pill-archived {
  background: var(--ps-sev-neutral-bg);
  color: var(--ps-sev-neutral-fg);
}

.app-root .app-pill[data-tone="neutral"] {
  background: var(--ps-sev-neutral-bg);
  color: var(--ps-sev-neutral-fg);
  border-color: var(--ps-sev-neutral-bd);
}

.app-root .app-pill-expired,
.app-root .app-pill-fail,
.app-root .app-pill-error,
.app-root .app-pill[data-tone="fail"] {
  background: var(--ps-sev-critical-bg);
  color: var(--ps-sev-critical-fg);
  border-color: var(--ps-sev-critical-bd);
}

.app-root .app-pill-suspended {
  background: #f8e1ec;
  color: #7a1a3a;
  border-color: #e7b8c8;
}

.app-root .app-pill-pending {
  background: var(--saip-ice-100);
  color: var(--ps-text-heading-brand);
  border-color: var(--ps-text-muted);
}

.app-root .app-pill-internal,
.app-root .app-pill-annual,
.app-root .app-pill-enterprise {
  background: var(--saip-ice-100);
  color: var(--ps-text-heading-brand);
  border-color: var(--ps-text-muted);
}

/* -----------------------------------------------------------
   Enterprise redesign primitives — HANDOFF §2.3/§2.4/§2.5/§2.8
   (design_handoff_profile_sentinel · 01 - Design Charter)
   ----------------------------------------------------------- */

/* §2.3 — section heading pattern: 15/700 title + labelled count pill. */
.app-root .app-sec-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

.app-root .app-sec-title {
  font-size: var(--saip-text-lg);
  font-weight: 700;
  color: var(--ps-text-heading);
  margin: 0;
}

/* W5 — dense table-group header label. A NAMED component (13px/700) so a
   row-group title never has to shrink the 15px .app-sec-title with a one-off
   inline override (the rules-table inconsistency the Sponsor saw). */
.app-root .app-group-label {
  font-size: var(--saip-text-base);
  font-weight: 700;
  color: var(--ps-text-heading);
  margin: 0;
}

/* Counts appear ONLY with an explicit label (e.g. "2 applet instances"),
   never as a bare number badge. */
.app-root .app-count-pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  background: var(--ps-bg-canvas);
  border: 1px solid var(--ps-border-hairline);
  color: var(--ps-text-secondary);
  white-space: nowrap;
}

/* "▾ Detailed … rows (n)" line that precedes every detail table. */
.app-root .app-detail-line {
  font-size: 12px;
  font-weight: 600;
  color: var(--ps-text-heading);
  margin: 10px 0 6px;
}

.app-root .app-detail-line .app-detail-note {
  font-weight: 400;
  color: var(--ps-text-muted);
}

/* §2.4 — field rows: uppercase muted label above a mono value. */
.app-root .app-field-row {
  padding: 7px 0;
  border-bottom: 1px solid var(--ps-border-hairline);
}

.app-root .app-field-row:last-child {
  border-bottom: none;
}

.app-root .app-field-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ps-text-muted);
}

.app-root .app-field-value {
  display: block;
  margin-top: 3px;
  font-family: var(--saip-font-mono);
  font-size: 11px;
  color: var(--ps-text-heading-brand);   /* FT-DARK-TEXT — mono value flips on the theme-aware section */
  word-break: break-all;
}

.app-root .app-field-value.is-prose {
  font-family: var(--saip-font-ui);
  font-size: 12.5px;
  word-break: normal;
}

/* §2.5 — muted text at charter contrast. */
.app-root .app-muted {
  color: var(--ps-text-muted);
}

/* "standard default" marker — defaults are always explicitly marked. */
.app-root .app-std-default {
  font-size: 10px;
  font-weight: 600;
  color: var(--ps-text-muted);
  text-transform: none;
}

/* Card-grid + label utilities (Dashboard.dc.html port). */
.app-root .app-card-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ps-text-secondary);
  font-weight: 600;
  margin: 0 0 var(--saip-spacing-3) 0;
}

.app-root .app-section h2.app-card-label {
  border-bottom: none;
  padding-bottom: 0;
}

.app-root .app-grid-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}

.app-root .app-grid-2-1 {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
  gap: 14px;
  margin-bottom: 14px;
}

@media (max-width: 900px) {
  .app-root .app-grid-2-1 {
    grid-template-columns: 1fr;
  }
}

.app-root .app-card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ps-text-heading);   /* FT-DARK-TEXT */
  margin: 8px 0 0;
}

.app-root .app-stat-value-xl {
  font-size: 34px;
  font-weight: 700;
  color: var(--ps-text-heading);   /* FT-DARK-TEXT */
  letter-spacing: -0.02em;
  margin-top: 6px;
}

.app-root .app-btn-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.app-root .app-btn-stack .app-btn {
  justify-content: center;
}

.app-root .app-link-arrow {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ps-link);
}

/* Sign-in hero (Sign In.dc.html port) — auth area on the navy radial
   gradient with the access-controls list beside the card. Scoped by the
   per-page body modifier `is-auth-hero` so other auth pages keep the
   light surface until they are ported. */
.app-root.is-auth-hero .app-main {
  background: var(--saip-gradient-hero);
  padding: var(--saip-spacing-6) 0 var(--saip-spacing-7) 0;
}

.app-root.is-auth-hero .app-footer {
  margin-top: 0;
}

/* SIGNIN-REFINE-01 — compact the brand header on the auth hero only
   (Design Council Option A: same lockup geometry, denser — ≈−26%; the
   authed app chrome is untouched). */
.app-root.is-auth-hero .app-header {
  padding: var(--saip-spacing-3) 0 var(--saip-spacing-3) 0;
}
.app-root.is-auth-hero .app-brand {
  gap: 3px;
}
.app-root.is-auth-hero .app-idemia-chev {
  height: 12px;
}
.app-root.is-auth-hero .app-wordmark {
  font-size: 21px;
}
.app-root.is-auth-hero .app-wordmark .app-wordmark-mark {
  width: 34px;
  height: 34px;
  padding: 4px;
}

/* SIGNIN-REFINE-01 — centre the ENSEMBLE (card 460 + trust 400), never the
   text; the 1fr trust track gave ~95ch line lengths and made the triad look
   lost (council visual B3). */
.app-root .app-auth-grid {
  display: grid;
  grid-template-columns: minmax(0, 460px) minmax(280px, 400px);
  justify-content: center;
  gap: var(--saip-spacing-8);
  align-items: start;
  max-width: 1200px;
  margin: 0 auto;
}

.app-root.is-auth-hero .app-auth-card {
  /* F3: a themed hairline (not border:none) so the card reads as a distinct
     panel on the violet hero in BOTH modes — in dark the surface
     (--ps-bg-surface, violet-ink2) sits close to the hero, and the hairline
     carries the edge. */
  border: 1px solid var(--ps-border-hairline);
  border-radius: 18px;
  /* SIGNIN-REFINE-01 — shadow re-tinted to the hero's own violet ink (was a
     navy shadow that read grey-green at the edge on the violet gradient). */
  box-shadow: 0 16px 48px rgba(14, 4, 32, 0.40);
  max-width: none;
  margin: 0;
}

@media (max-width: 1000px) {
  .app-root .app-auth-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }
  /* Stacked: keep the card a card, centred — not a full-bleed slab (after
     the base block so the cap wins the cascade at equal specificity). */
  .app-root.is-auth-hero .app-auth-card {
    max-width: 480px;
    margin: 0 auto;
    width: 100%;
  }
  /* Stacked: the desktop optical alignment padding (48px, card-title line)
     has no referent — tighten the card→trust gap. */
  .app-root .app-auth-controls {
    padding-top: var(--saip-spacing-5);
  }
}

.app-root .app-auth-controls {
  /* F3: light violet-tinted body copy on the violet hero (was a fixed navy
     tint). violet-50 clears AA on every hero stop (>= 10.8:1).
     SIGNIN-REFINE-01 — optical top alignment: the eyebrow cap-height sits on
     the card TITLE's line (card top padding = spacing-7), not the card edge. */
  color: var(--saip-violet-50);
  padding-top: var(--saip-spacing-7);
}

.app-root .app-auth-controls .app-auth-controls-eyebrow {
  margin: 0;
  border: none;
  padding: 0;
  font-size: var(--saip-text-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* F3: de-cyan — periwinkle accent (was a fixed cyan, the last off-brand teal). */
  color: var(--saip-violet-300);
}

.app-root .app-auth-controls ul {
  margin: var(--saip-spacing-4) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--saip-spacing-5);
}

.app-root .app-auth-controls li {
  display: flex;
  gap: 12px;
  font-size: var(--saip-text-base);
  line-height: 1.55;
  margin: 0;
  max-width: 46ch;
}

.app-root .app-auth-controls li::before {
  /* F3: de-cyan — periwinkle diamond marker (was a fixed cyan).
     SIGNIN-REFINE-01 — empty alt text so AT never reads "black diamond"
     per row (a11y M11). */
  content: "◆" / "";
  color: var(--saip-violet-300);
  flex: 0 0 auto;
}

.app-root .app-auth-controls strong {
  color: var(--saip-ice-0);
  display: block;
  margin-bottom: 2px;
}

/* ABOUT-REFRESH-01 — quiet trust-column link to /about#security. Periwinkle
   (violet-300) like the other trust-column accents — never --ps-link on the
   hero (a11y M15: the light-theme link token would vanish on the violet
   gradient); underlined so the affordance is not colour-only (WCAG 1.4.1).
   The trust-column focus-ring override below (M16) already covers it. */
.app-root .app-auth-controls .app-auth-controls-link {
  display: inline-block;
  margin-top: var(--saip-spacing-5);
  color: var(--saip-violet-300);
  font-size: var(--saip-text-sm);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.app-root .app-auth-controls .app-auth-controls-link:hover {
  color: var(--saip-violet-50);
}

/* SIGNIN-REFINE-01 (a11y M16) — the light-theme focus ring token equals the
   hero's top gradient stop (#430099): any focusable in the trust column
   would take an invisible ring. Defensive override, scoped to the hero. */
.app-root.is-auth-hero .app-auth-controls :focus-visible {
  outline-color: var(--saip-violet-300);
}

.app-root.is-auth-hero .app-posture-line {
  color: var(--ps-text-muted);
}

/* Upload screen (Upload.dc.html port) — drop zone + pipeline steps. */
/* UXS-007-FUP-POLISH minor 5 — naming note: despite the name this is a
   2-track 1.4fr:1fr grid (lead + rail), not a 3-column 1-4-1; rename only
   with a sweep of its consumers. */
.app-root .app-grid-1-4-1 {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(300px, 1fr);
  gap: 18px;
  align-items: start;
}

@media (max-width: 1000px) {
  .app-root .app-grid-1-4-1 {
    grid-template-columns: 1fr;
  }
}

/* FT-W2-DARK-SWEEP-01 — the dropzone was a self-consistent LIGHT card in both
   modes (a white ice-50 card floating on the dark canvas). Folded onto the
   semantic tier so the dark map repaints it; light values are identical. The
   green has-file success state stays on the primitive (FT-DC-PILL-CONTRAST-AA
   scope). */
.app-root .app-dropzone {
  margin-top: 18px;
  border: 2px dashed var(--ps-border-strong);
  border-radius: 14px;
  background: var(--ps-bg-surface-muted);
  padding: 30px 24px;
  text-align: center;
}

.app-root .app-dropzone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 12px;
  /* FT-DARK-TEXT — the upload glyph is the BRAND accent, not an info signal:
     retint teal → IDEMIA violet via the flipping --ps-accent-soft-* pair
     (info stays teal; the green .has-file success state below is untouched). */
  background: var(--ps-accent-soft-bg);
  color: var(--ps-accent-soft-fg);
  font-size: 24px;
  margin-bottom: 12px;
}

.app-root .app-dropzone-title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ps-text-heading);
}

.app-root .app-dropzone-hint {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--ps-text-muted);
}

.app-root .app-dropzone input[type="file"] {
  margin-top: 12px;
  font-size: 12.5px;
  max-width: 100%;
}
/* Drop feedback (wired by dropzone.js) — global so the upload panel + Profile
   Delta zones both show drag-over highlight + the chosen filename. */
.app-root .app-dropzone { cursor: pointer; }
.app-root .app-dropzone.is-dragover { border-color: var(--ps-text-heading-brand); background: var(--ps-bg-canvas); }
/* UXS-007 — dark-safe .has-file: the raw --saip-green-50 fill did not flip, so
   on the dark canvas the title/hint/filename went near-invisible on a light
   green chip (a11y Mn4). --ps-sev-pass-bg flips (light == green-50, no light
   regression; dark == the dark success fill) and pairs AA with the filename's
   --ps-sev-pass-fg. Fixes the dropzone everywhere (Studio landing + Cockpit +
   Delta). */
.app-root .app-dropzone.has-file { border-color: var(--saip-green-500); background: var(--ps-sev-pass-bg); }
.app-root .app-dropzone-filename { margin: 8px 0 0; font-weight: 600; font-size: 12.5px; color: var(--ps-sev-pass-fg); }

.app-root .app-steps {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.app-root .app-step {
  display: flex;
  gap: 13px;
}

.app-root .app-step-num {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--saip-violet-ink3);
  color: var(--saip-ice-0);
  font-size: 12px;
  font-weight: 700;
}

.app-root .app-step-num.is-active {
  background: var(--saip-gradient-cta-primary);
}

.app-root .app-step-num.is-muted {
  background: var(--saip-grey-200);
  color: var(--ps-text-secondary);
}

.app-root .app-step-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ps-text-heading);   /* FT-DARK-TEXT */
}

.app-root .app-step-desc {
  margin: 2px 0 0;
  font-size: 12.5px;
  color: var(--ps-text-secondary);
  line-height: 1.5;
}

/* Loading skeletons (02 - Edge States.dc.html §5) — pulse 1.4s
   ease-in-out, blocks #E2E7EE on #F4F7FB, mirroring the layout being
   loaded. Server-rendered pages use them on async surfaces only. */
@keyframes app-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

.app-root .app-skeleton-bar {
  height: 12px;
  border-radius: 6px;
  background: var(--saip-grey-200);
  animation: app-pulse 1.4s ease-in-out infinite;
}

.app-root .app-skeleton-pill {
  width: 64px;
  height: 14px;
  border-radius: 999px;
  background: var(--saip-grey-200);
  animation: app-pulse 1.4s ease-in-out infinite;
}

.app-root .app-skeleton-tile {
  height: 64px;
  border-radius: 10px;
  background: var(--saip-ice-100);
  border: 1px solid var(--ps-border-hairline);
  animation: app-pulse 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .app-root .app-skeleton-bar,
  .app-root .app-skeleton-pill,
  .app-root .app-skeleton-tile {
    animation: none;
  }
}

/* Upload — guided analysis-progress overlay (Sponsor 2026-06-12;
   fetch rework + polish 2026-06-16). A dimmed full-viewport backdrop with a
   clean centered card; the form is submitted via fetch() so the animation
   actually runs (a native navigation would freeze JS timers). */
.app-root .app-progress-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  overflow-y: auto;
}

/* display:flex above would defeat the hidden attribute's display:none —
   keep the server-rendered hidden state authoritative. */
.app-root .app-progress-overlay[hidden] {
  display: none;
}

/* Dimmed, lightly-blurred backdrop covering the whole viewport so the page
   chrome behind reads as cleanly dimmed (not washed-out / missing). */
.app-root .app-progress-backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: rgba(8, 21, 43, 0.55); /* --saip-navy-900 @ 55% */
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.app-root .app-progress-card {
  position: relative;
  width: min(520px, 100%);
  background: var(--ps-bg-surface);
  border-radius: var(--saip-radius-lg);
  box-shadow: var(--saip-shadow-lg);
  padding: var(--saip-spacing-6);
  animation: app-progress-card-in 0.22s ease-out;
}

@keyframes app-progress-card-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

/* Title + climbing percentage on one row. */
.app-root .app-progress-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.app-root .app-progress-title {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ps-text-heading);
}

.app-root .app-progress-percent {
  font-size: 26px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ps-link);
  letter-spacing: -0.01em;
}

/* Determinate progress bar — a smooth fill that climbs toward the cap. */
.app-root .app-progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--saip-grey-200);
  overflow: hidden;
  margin: 0 0 14px;
  position: relative;
}

/* Honest wait line under the bar. */
.app-root .app-progress-wait {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ps-text-secondary);
  margin: 0 0 18px;
}

/* Retryable error line (network / unexpected non-2xx). */
.app-root .app-progress-error {
  margin: 14px 0 0;
  padding: 10px 12px;
  border-radius: var(--saip-radius-sm);
  background: #fdecec;
  border: 1px solid #f3c2c2;
  color: #9a2a2a;
  font-size: 12.5px;
  line-height: 1.45;
}

.app-root .app-progress-error[hidden] {
  display: none;
}

.app-root .app-progress-bar-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: var(--saip-gradient-cta-primary);
  transition: width 0.28s ease-out;
}

/* Step list — done / active / pending visual states. */
.app-root .app-progress-steps {
  margin-top: 4px;
  border-top: 1px solid var(--ps-border-hairline);
}

.app-root .app-progress-step {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--ps-border-hairline);
  font-size: 13.5px;
  color: var(--ps-text-muted);
  transition: color 0.2s ease;
}

.app-root .app-progress-step:last-child {
  border-bottom: none;
}

/* The status marker — a small disc that becomes a check (done) or an
   accented pulsing dot (active). */
.app-root .app-progress-step-mark {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  border: 2px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  font-size: 12px;
  line-height: 1;
  color: var(--saip-ice-0);
}

/* Pending (default) — muted text, hollow marker. */
.app-root .app-progress-step[data-step-state="pending"] {
  color: var(--ps-text-muted);
}

/* Active — highlighted text + accent marker with a subtle pulse. */
.app-root .app-progress-step[data-step-state="active"] {
  color: var(--ps-text-primary);
  font-weight: 600;
}

.app-root .app-progress-step[data-step-state="active"] .app-progress-step-mark {
  border-color: var(--ps-accent);
  background: var(--saip-violet-50);
  animation: app-progress-pulse 1.2s ease-in-out infinite;
}

/* Done — recovered text colour + filled teal marker with a check glyph. */
.app-root .app-progress-step[data-step-state="done"] {
  color: var(--ps-text-primary);
}

.app-root .app-progress-step[data-step-state="done"] .app-progress-step-mark {
  border-color: var(--ps-accent);
  background: var(--ps-accent);
}

.app-root .app-progress-step[data-step-state="done"] .app-progress-step-mark::after {
  content: "\2713"; /* ✓ */
}

@keyframes app-progress-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(67, 0, 153, 0.45); }
  50% { box-shadow: 0 0 0 5px rgba(67, 0, 153, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .app-root .app-progress-bar-fill {
    transition: none;
  }
  .app-root .app-progress-card {
    animation: none;
  }
  .app-root .app-progress-step[data-step-state="active"] .app-progress-step-mark {
    animation: none;
  }
}

/* In-content posture footer line (charter rule: on every page). */
.app-root .app-posture-line {
  margin: 20px 0 0;
  font-size: 11px;
  color: var(--ps-text-muted);
}

/* §2.8 — conformity rule (semantic only): green = matches standard,
   red = does not match, gray = standard undefined. */
.app-root .app-conf-match {
  color: var(--saip-green-500);
  font-weight: 600;
}

.app-root .app-conf-mismatch {
  color: var(--saip-rose-500);
  font-weight: 600;
}

.app-root .app-conf-undefined {
  color: var(--ps-text-muted);
  font-weight: 600;
}

/* -----------------------------------------------------------
   Filter chips (admin lists)
   ----------------------------------------------------------- */

.app-root .app-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--saip-spacing-2);
  margin-bottom: var(--saip-spacing-4);
}

.app-root .app-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-strong);
  border-radius: 999px;
  color: var(--ps-text-heading-brand);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
}

.app-root .app-chip:hover {
  background: var(--ps-bg-surface-muted);
  text-decoration: none;
}

.app-root .app-chip.is-active {
  background: var(--ps-accent);
  color: var(--saip-ice-0);
  border-color: transparent;
}

.app-root .app-chip .app-chip-count {
  background: rgba(255, 255, 255, 0.16);
  color: inherit;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
}

.app-root .app-chip:not(.is-active) .app-chip-count {
  background: var(--ps-bg-fill);
  color: var(--ps-text-secondary);
}

/* -----------------------------------------------------------
   Empty state
   ----------------------------------------------------------- */

.app-root .app-empty {
  text-align: center;
  background: var(--ps-bg-inset);
  border: 1px dashed var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  padding: var(--saip-spacing-7) var(--saip-spacing-6);
  color: var(--ps-text-secondary);
}

/* Edge-state variants (02 - Edge States.dc.html §4). */
.app-root .app-empty.is-success {
  /* FT-DARK-TEXT — route the success-empty surface through the theme-aware
     tokens (byte-identical in light) so the now-flipping <h3> stays legible in
     dark instead of near-white-on-white. */
  background: var(--ps-bg-surface);
  border-style: solid;
  border-color: var(--ps-border-hairline);
  box-shadow: var(--saip-shadow-card);
}

/* UX-REFONTE-W4-COMPONENT-LAYER-01 §2 (A5) — inline-SVG icon glyph.
   Renders via {{ icon('name') }} → <svg class="app-icon"><use href="#i-name">.
   Sizes to the surrounding text (1em), inherits its colour (currentColor) so
   semantic state stays in the pill/text, never hard-coded on the glyph. */
.app-root .app-icon,
.hp-root .app-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  flex: none;
  /* stroke + fill both currentColor: outline symbols set fill="none" on their
     own <symbol>, which overrides this, so this is safe for them; a future
     fill-based symbol (no fill="none") will pick up currentColor and be visible */
  fill: currentColor;
  stroke: currentColor;
  color: currentColor;
}

.app-root .app-empty.is-success .app-empty-icon {
  background: var(--saip-green-50);
  color: var(--saip-green-500);
}

.app-root .app-empty.is-nomatch .app-empty-icon {
  background: var(--saip-ice-100);
  color: var(--ps-text-muted);
}

.app-root .app-empty .app-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--saip-violet-50);
  color: var(--ps-accent);
  font-weight: 700;
  font-size: 18px;
  margin-bottom: var(--saip-spacing-3);
}

.app-root .app-empty h3 {
  margin: 0 0 var(--saip-spacing-2) 0;
  color: var(--ps-text-heading);   /* FT-DARK-TEXT — empty-state heading on the theme-aware inset/surface */
}

.app-root .app-empty p {
  font-size: 13.5px;
  color: var(--ps-text-secondary);
  max-width: 480px;
  margin: 0 auto var(--saip-spacing-4) auto;
}

/* -----------------------------------------------------------
   Footer
   ----------------------------------------------------------- */

.app-root .app-footer {
  background: var(--saip-navy-900);
  /* DSV2-04 lot 4d (axe color-contrast, serious) — the footer surface is
     CONSTANT dark navy in both themes, so the theme-muted token (grey-400,
     darkened for LIGHT surfaces) fails here at 3.38:1. Fixed light-muted
     foreground: 6.1:1 on navy-900, quiet by design. */
  color: #8B96AA;
  padding: var(--saip-spacing-5) 0;
  margin-top: var(--saip-spacing-7);
  font-size: 12px;
}

.app-root .app-footer-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--saip-spacing-3);
}

.app-root .app-footer a {
  color: #8B96AA; /* lot 4d — same fixed-surface AA rationale as the footer fg */
  text-decoration: none;
}

.app-root .app-footer a:hover {
  color: var(--saip-ice-0);
}


/* -----------------------------------------------------------
   Inline severity hint (for the dashboard sessions table)
   ----------------------------------------------------------- */

.app-root .app-sev-line {
  font-family: var(--saip-font-mono);
  font-size: 12px;
}

.app-root .app-sev-line .pass {
  color: var(--saip-green-500);
  font-weight: 700;
}

.app-root .app-sev-line .fail {
  color: var(--saip-rose-500);
  font-weight: 700;
}

.app-root .app-sev-line .skip {
  color: var(--ps-text-secondary);
  font-weight: 600;
}

/* -----------------------------------------------------------
   Auth pages (login / request-access / password reset)
   ----------------------------------------------------------- */

.app-root .app-auth-card {
  /* F3 dark defect fix — the card surface + hairline + shadow were pinned to
     light primitives (ice-0 / grey-200), so in dark mode the card stayed white
     while the heading/labels flipped light (the reported "faded heading").
     Routing through the semantic tier lets the dark map repaint the card;
     light values are byte-identical (ice-0 / grey-200 / shadow-card). */
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  box-shadow: var(--ps-shadow-card);
  padding: var(--saip-spacing-7) var(--saip-spacing-6);
  max-width: 540px;
  margin: var(--saip-spacing-5) auto;
}

.app-root .app-auth-card h2 {
  margin-top: 0;
  border-bottom: none;
  padding-bottom: 0;
  /* SIGNIN-REFINE-01 — the title carries the page: one step-change to
     page weight, everything else stays at or below text-md. */
  font-size: var(--saip-text-h1);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin-bottom: var(--saip-spacing-2);
}

.app-root .app-auth-card .app-auth-meta {
  font-size: var(--saip-text-base);
  line-height: 1.55;
  color: var(--ps-text-secondary);
  margin-bottom: var(--saip-spacing-5);
}

.app-root .app-auth-card .app-auth-help {
  margin-top: var(--saip-spacing-5);
  border-top: 1px solid var(--ps-border-hairline);
  padding-top: var(--saip-spacing-4);
  font-size: var(--saip-text-base);
  color: var(--ps-text-secondary);
}

/* SIGNIN-REFINE-01 — access section structure (replaces the template's
   inline styles) + the de-emphasised invitation note. */
.app-root .app-auth-card .app-auth-help h3 {
  margin: 0 0 var(--saip-spacing-2) 0;
  font-size: var(--saip-text-md);
  font-weight: 600;
}
.app-root .app-auth-card .app-auth-help p {
  margin: 0 0 var(--saip-spacing-3) 0;
  line-height: 1.55;
}
.app-root .app-auth-card .app-auth-invite-note {
  margin: var(--saip-spacing-3) 0 0 0;
  font-size: var(--saip-text-sm);
  color: var(--ps-text-muted);
}
.app-root .app-auth-card .app-auth-alt {
  margin-top: var(--saip-spacing-3);
}
.app-root .app-auth-card .app-auth-alt-note {
  margin: 0 0 var(--saip-spacing-2) 0;
  font-size: var(--saip-text-sm);
  line-height: 1.55;
  color: var(--ps-text-muted);
}

/* -----------------------------------------------------------
   Responsive tweaks
   ----------------------------------------------------------- */

@media (max-width: 720px) {
  .app-root .app-header-inner {
    flex-direction: column;
    align-items: flex-start;
  }
  /* Copie-conforme chrome: the in-header identity is desktop chrome; on a
     phone it would wrap into a second line — account identity stays one tap
     away on /profile. */
  .app-root .app-header-user {
    display: none;
  }
  /* UX-GLOBAL-AUTHENTICATED-SHELL-ALIGNMENT-01 — the former
     `.app-wrap { padding: 0 16px }` override is deleted: the shared
     --ps-auth-shell-gutter clamp already floors at 16px here. */
  /* M22-BRAND-02-R1 — keep the mark prominent but clean on mobile. */
  .app-root .app-wordmark .app-wordmark-mark {
    width: var(--ps-logo-app-header-mobile);
    height: var(--ps-logo-app-header-mobile);
  }
  .app-root .app-wordmark { font-size: 20px; }
  .app-root .app-section {
    padding: var(--saip-spacing-4) var(--saip-spacing-4);
  }
  .app-root h1 {
    font-size: 22px;
  }
  .app-root h2 {
    font-size: 18px;
  }
  .app-root .app-stat-card .app-stat-value {
    font-size: 16px;
  }
  .app-root .app-footer-inner {
    flex-direction: column;
    align-items: flex-start;
  }
  .app-root .app-form-row input,
  .app-root .app-form-row input[type="text"],
  .app-root .app-form-row input[type="email"],
  .app-root .app-form-row input[type="password"],
  .app-root .app-form-row input[type="search"],
  .app-root .app-form-row input[type="number"],
  .app-root .app-form-row input[type="url"],
  .app-root .app-form-row textarea {
    /* prevent iOS zoom-in on focus. SIGNIN-REFINE-01 — the attribute
       selectors are required: the base sizing rule (…input[type="…"]) has
       higher specificity than the bare `input` this guard used, so the
       16px floor NEVER actually applied (proven by computed-style assert
       in Chromium at 390px — 14px before this fix). */
    font-size: 16px;
  }
}

/* -----------------------------------------------------------
   Reduced motion + transparency (mirrors .lp-root)
   ----------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .app-root *,
  .app-root *::before,
  .app-root *::after {
    transition: none !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .app-root .app-section,
  .app-root .app-stat-card,
  .app-root .app-auth-card,
  .app-root .app-empty,
  .app-root .app-banner {
    box-shadow: none;
  }
}

/* -----------------------------------------------------------
   Phase 83B — Profile Explorer UX refresh.

   Additive only. Re-uses existing design tokens; introduces no
   new colour values. All rules namespaced under `.app-root` to
   match the rest of the design system.
   ----------------------------------------------------------- */

.app-root .pe-sticky-header {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  padding: var(--saip-spacing-3) var(--saip-spacing-4);
  margin-bottom: var(--saip-spacing-3);
  box-shadow: 0 1px 3px rgba(11, 26, 51, 0.06);
}

.app-root .pe-sticky-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.app-root .pe-sticky-row-primary {
  margin-bottom: 4px;
}

.app-root .pe-sticky-eyebrow {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ps-text-heading);
  margin-right: 4px;
}

.app-root .pe-sticky-stat {
  font-size: 12px;
  color: var(--ps-text-primary);
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--ps-border-hairline);
  background: var(--ps-bg-surface-muted);
}

.app-root .pe-sticky-stat.is-pass {
  border-color: #bcdcc4;
  background: var(--saip-green-50);
  color: var(--saip-green-500);
}

.app-root .pe-sticky-stat.is-fail {
  border-color: #f0c987;
  background: var(--saip-amber-50);
  color: var(--saip-amber-500);
}

.app-root .pe-sticky-stat.is-info {
  border-color: #b6dcdd;
  background: var(--saip-teal-50);
  color: var(--saip-teal-600);
}

.app-root .pe-sticky-stat code {
  font-family: var(--saip-font-mono);
  font-weight: 700;
}

/* M22-UX-05a — at-a-glance status tiles (clickable jump anchors).
   Rendered as a sibling immediately below the sticky header (NOT inside
   the pinned box) so the pinned header height is unchanged — journey_07
   asserts the header pins at top:0 within ±1px even at full-page scroll. */
.app-root .pe-glance-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: -4px;
  margin-bottom: var(--saip-spacing-3);
}

.app-root .pe-glance-tile {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--ps-border-hairline);
  background: var(--ps-bg-surface-muted);
  text-decoration: none;
  color: var(--ps-text-primary);
  line-height: 1.4;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.app-root .pe-glance-tile:hover,
.app-root .pe-glance-tile:focus-visible {
  border-color: var(--ps-text-heading);
  box-shadow: 0 1px 4px rgba(11, 26, 51, 0.1);
}

.app-root .pe-glance-tile.is-critical {
  border-color: #f0c987;
  background: var(--saip-amber-50);
}

.app-root .pe-glance-tile.is-major {
  border-color: #f0c987;
  background: var(--saip-amber-50);
}

.app-root .pe-glance-tile.is-review {
  border-color: #b6dcdd;
  background: var(--saip-teal-50);
}

.app-root .pe-glance-tile.is-report {
  border-color: #bcdcc4;
  background: var(--saip-green-50);
}

.app-root .pe-glance-clause {
  border-color: var(--ps-text-heading);
  background: var(--ps-bg-surface);
  font-weight: 600;
}

.app-root .pe-glance-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-secondary);
  white-space: nowrap;
}

.app-root .pe-glance-value {
  font-size: 12px;
  color: var(--ps-text-heading);
}

.app-root .pe-glance-value code {
  font-family: var(--saip-font-mono);
  font-weight: 700;
}

.app-root .pe-glance-sev.is-critical {
  color: var(--saip-amber-500);
  font-weight: 600;
}

.app-root .pe-glance-sev.is-major {
  color: var(--saip-amber-500);
}

.app-root .pe-glance-sev.is-minor,
.app-root .pe-glance-sev.is-info {
  color: var(--saip-teal-600);
}

/* TOC pill bar. */
.app-root .pe-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--saip-spacing-3);
  padding: 4px 0;
}

.app-root .pe-toc-pill {
  font-size: 11px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  color: var(--ps-text-heading);
  text-decoration: none;
}

.app-root .pe-toc-pill:hover {
  background: var(--saip-amber-50);
  border-color: var(--saip-amber-500);
}

/* Sub-headings inside the summary section. */
.app-root .pe-summary-subhead {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ps-text-heading);
  margin-top: var(--saip-spacing-3);
  margin-bottom: var(--saip-spacing-2);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding-bottom: 4px;
}

/* Triage grid + tile. */
.app-root .pe-triage-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--saip-spacing-3);
}

.app-root .pe-triage-tile {
  background: var(--saip-amber-50);
  border: 1px solid #f0c987;
  border-radius: var(--saip-radius);
  padding: var(--saip-spacing-3);
}

.app-root .pe-triage-tile.is-empty {
  background: var(--ps-bg-surface-muted);
  border-color: var(--ps-border-hairline);
  opacity: 0.75;
}

.app-root .pe-triage-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--ps-text-heading);
  margin-bottom: 6px;
}

.app-root .pe-triage-list {
  margin: 0;
  padding-left: 16px;
  font-size: 11px;
}

.app-root .pe-triage-list li {
  margin-bottom: 2px;
}

.app-root .pe-triage-more {
  font-size: 11px;
  color: var(--ps-text-secondary);
  margin-top: 4px;
}

.app-root .pe-triage-empty {
  font-size: 11px;
  color: var(--ps-text-secondary);
  margin-top: 4px;
}

.app-root .pe-triage-apply {
  margin-top: 8px;
  font-size: 11px;
  padding: 4px 10px;
  border-radius: var(--saip-radius-sm);
  border: 1px solid var(--saip-navy-700);
  background: var(--ps-bg-surface);
  color: var(--ps-text-heading-brand);
  cursor: pointer;
}

.app-root .pe-triage-apply:hover {
  background: var(--saip-navy-800);
  color: var(--saip-ice-0);
}

/* Filter chip bar. */
.app-root .pe-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-bottom: var(--saip-spacing-2);
}

.app-root .pe-filter-chip {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  color: var(--ps-text-heading-brand);
  cursor: pointer;
  user-select: none;
  font-family: var(--saip-font-ui);
}

.app-root .pe-filter-chip:hover {
  background: var(--ps-bg-surface-muted);
}

.app-root .pe-filter-chip.is-active {
  background: var(--saip-navy-800);
  color: var(--saip-ice-0);
  border-color: var(--ps-text-heading);
}

.app-root .pe-filter-chip code {
  font-family: var(--saip-font-mono);
  font-size: 11px;
}

.app-root .pe-filter-result-count {
  font-size: 11px;
  color: var(--ps-text-secondary);
  margin-left: 6px;
}

/* Hidden table rows during filtering. */
.app-root tr.is-hidden { display: none; }

/* -----------------------------------------------------------
   Phase 83F — Profile Explorer filesystem table interactions.

   - Sticky Action / Path / Name columns so the primary "Inspect"
     control and the file identity stay visible even when the
     wider table scrolls horizontally on narrow viewports. The
     remaining columns (Kind / FID / SFI / Structure / Size /
     Content / Decoder / Access / Findings / Std / PE) scroll
     horizontally as before, but never push the action off-
     screen.
   - .pe-fs-row clickable affordance: cursor pointer, hover
     highlight, keyboard-focus ring, selected-row highlight.
   - Name-column source variants: dim grey for the file_id /
     node_id fallback so the user knows when the rendered name
     is synthetic.
   ----------------------------------------------------------- */
.app-root .pe-fs-table-wrap { position: relative; }
.app-root .pe-fs-table { table-layout: auto; }
.app-root .pe-fs-table th.pe-fs-col-action,
.app-root .pe-fs-table td.pe-fs-col-action {
  position: sticky;
  left: 0;
  z-index: 3;
  background: var(--ps-bg-surface);
  min-width: 110px;
  box-shadow: 1px 0 0 var(--saip-grey-200);
}
.app-root .pe-fs-table th.pe-fs-col-path,
.app-root .pe-fs-table td.pe-fs-col-path {
  position: sticky;
  left: 110px;
  z-index: 2;
  background: var(--ps-bg-surface);
  min-width: 200px;
}
.app-root .pe-fs-table th.pe-fs-col-name,
.app-root .pe-fs-table td.pe-fs-col-name {
  position: sticky;
  left: 310px;
  z-index: 2;
  background: var(--ps-bg-surface);
  min-width: 140px;
  box-shadow: 1px 0 0 var(--saip-grey-200);
}
/* Header sticks above the body when the table is taller than its
   wrapper, so column labels never disappear during scroll. */
.app-root .pe-fs-table thead th {
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--ps-bg-surface-muted);
}
.app-root .pe-fs-table thead th.pe-fs-col-action { z-index: 5; }
.app-root .pe-fs-table thead th.pe-fs-col-path,
.app-root .pe-fs-table thead th.pe-fs-col-name { z-index: 5; }
/* Clickable row affordance. */
.app-root tr.pe-fs-row {
  cursor: pointer;
  transition: background 100ms ease-out;
}
.app-root tr.pe-fs-row:hover td {
  background: var(--saip-amber-50);
}
.app-root tr.pe-fs-row:focus {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: -2px;
}
.app-root tr.pe-fs-row.is-selected td {
  background: var(--saip-amber-50);
  border-left: 3px solid var(--saip-amber-500);
}
.app-root tr.pe-fs-row.is-selected td.pe-fs-col-action {
  background: var(--saip-amber-50);
}
.app-root tr.pe-fs-row.is-selected td.pe-fs-col-path,
.app-root tr.pe-fs-row.is-selected td.pe-fs-col-name {
  background: var(--saip-amber-50);
}
/* Name-column source colour-coding (deterministic; passive). */
.app-root .pe-fs-col-name.pe-fs-name-well_known code,
.app-root .pe-fs-col-name.pe-fs-name-canonical code {
  color: var(--ps-text-heading);
  font-weight: 600;
}
.app-root .pe-fs-col-name.pe-fs-name-inline_tag code {
  color: var(--ps-text-heading-brand);
}
.app-root .pe-fs-col-name.pe-fs-name-file_id code {
  color: var(--ps-text-primary);
}
.app-root .pe-fs-col-name.pe-fs-name-node_id code,
.app-root .pe-fs-col-name.pe-fs-name-unknown code {
  color: var(--ps-text-secondary);
}
/* Inspect button + toggle button live in the same cell. */
.app-root .pe-fs-col-action .pe-row-focus {
  margin-right: 4px;
}

/* Inline detail row toggle button. */
.app-root .pe-row-toggle {
  display: inline-block;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: var(--saip-radius-sm);
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  color: var(--ps-text-heading-brand);
  cursor: pointer;
}

.app-root .pe-row-toggle:hover {
  background: var(--saip-amber-50);
  border-color: var(--saip-amber-500);
}

/* Paired detail row body. */
.app-root tr.pe-row-detail-row > td {
  background: var(--ps-bg-surface-muted);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding: 0;
}

.app-root .pe-row-detail-body {
  padding: var(--saip-spacing-3) var(--saip-spacing-4);
}

.app-root .pe-row-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--saip-spacing-3);
}

.app-root .pe-row-detail-kv {
  display: flex;
  flex-direction: column;
  margin-bottom: 6px;
  font-size: 12px;
}

.app-root .pe-row-detail-kv > span:first-child {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-secondary);
  margin-bottom: 2px;
}

.app-root .pe-row-detail-kv code {
  font-family: var(--saip-font-mono);
  font-size: 11px;
}

/* Profile Elements collapsible wrapper. */
.app-root details.pe-pes-collapse > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--ps-text-heading-brand);
  padding: 6px 0;
  font-weight: 600;
}

.app-root details.pe-pes-collapse[open] > summary {
  color: var(--ps-text-heading);
  margin-bottom: var(--saip-spacing-2);
}

/* MR-M21-09c HOTFIX2 — reduce the file-system table from 14 columns
   to 7 by default. The original column set tried to fit too much in
   one row; an expert needs the IDENTITY of the file first (action /
   path / name / kind / FID), its STRUCTURE (transparent / linear_fixed
   + match badge), and its SIZE. Everything else (Content count /
   Decoder pill / Access status / Findings count / Std family / PE
   index / SFI) is one click away via the per-row ▼ expand button
   AND is also rendered as filter-chip metadata + per-row data-*
   attributes — so the data isn't hidden, just decluttered. */
.app-root table#profile-explorer-filesystem-table th:nth-child(6),
.app-root table#profile-explorer-filesystem-table td:nth-child(6),   /* SFI */
.app-root table#profile-explorer-filesystem-table th:nth-child(9),
.app-root table#profile-explorer-filesystem-table td:nth-child(9),   /* Content */
.app-root table#profile-explorer-filesystem-table th:nth-child(10),
.app-root table#profile-explorer-filesystem-table td:nth-child(10),  /* Decoder */
.app-root table#profile-explorer-filesystem-table th:nth-child(11),
.app-root table#profile-explorer-filesystem-table td:nth-child(11),  /* Access */
.app-root table#profile-explorer-filesystem-table th:nth-child(12),
.app-root table#profile-explorer-filesystem-table td:nth-child(12),  /* Findings count */
.app-root table#profile-explorer-filesystem-table th:nth-child(13),
.app-root table#profile-explorer-filesystem-table td:nth-child(13),  /* Std family */
.app-root table#profile-explorer-filesystem-table th:nth-child(14),
.app-root table#profile-explorer-filesystem-table td:nth-child(14)   /* PE index */ {
  display: none;
}

@media (max-width: 900px) {
  /* Narrow viewport — also hide the Kind pill (encoded in the icon
     + path already). */
  .app-root table#profile-explorer-filesystem-table th:nth-child(4),
  .app-root table#profile-explorer-filesystem-table td:nth-child(4) {
    display: none;
  }
}

/* HANDOFF §2.1 (supersedes MR-M21-09c navy header) — FS-table header
   stays LIGHT like every in-app table; visibility comes from the
   uppercase 700 type + the hairline, not a navy band. */
.app-root .pe-fs-table thead th {
  background: var(--ps-bg-surface-muted) !important;
  color: var(--ps-text-secondary);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  padding: 6px 8px;
  border-bottom: 1px solid var(--ps-border-hairline);
}

/* Keep the sticky-left header cells matching the row background-shift
   when the table scrolls horizontally. */
.app-root .pe-fs-table thead th.pe-fs-col-action,
.app-root .pe-fs-table thead th.pe-fs-col-path,
.app-root .pe-fs-table thead th.pe-fs-col-name {
  background: var(--ps-bg-surface-muted) !important;
  color: var(--ps-text-secondary);
}

/* -----------------------------------------------------------
   Phase 83D — Two-column workspace + consolidated findings panel.

   Additive only — reuses existing tokens; no new colour values
   introduced. The workspace places the file table in a left column
   and the per-file detail panel in a right sticky column so an
   expert can scan files and read their detail without scrolling.
   Below 900 px the grid collapses to a single column and the
   sticky pin releases.
   ----------------------------------------------------------- */

.app-root .pe-workspace {
  display: grid;
  /* MR-M21-09c HOTFIX2 — drop the 2-col mode entirely. The Sponsor
     2026-05-28 review showed the 14-column file system table can
     never fit cleanly in a 2-col workspace's narrow left lane on
     typical laptop viewports. Always stacked: file system table on
     top at full width, EF detail panel below at full width. The
     legacy 2-col rule below now never applies — preserved for the
     phase_83d literal-string contract test only. */
  grid-template-columns: 1fr;
  gap: var(--saip-spacing-4);
  align-items: start;
  margin-bottom: var(--saip-spacing-4);
}

.app-root .pe-workspace > .pe-workspace-left,
.app-root .pe-workspace > .pe-workspace-right {
  min-width: 0;
}

/* MR-M4-06 — the right grid cell must STRETCH to the full row height so the
   nested sticky `.pe-detail-panel` has a tall containing block to pin within.
   The grid sets `align-items: start`, which would otherwise leave this cell
   content-height; a sticky element inside a content-height block has no room
   to stick and simply scrolls away with the page (proven by journey 10 —
   the panel moved the full scroll delta despite `position: sticky`). */
.app-root .pe-workspace > .pe-workspace-right {
  align-self: stretch;
}

.app-root .pe-detail-panel {
  /* MR-M21-09c HOTFIX2 — workspace always stacked, so sticky pinning
     is no longer relevant. Position is static; the panel sits below
     the file-system table at full content width. */
  position: static;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  padding: var(--saip-spacing-3);
  box-shadow: 0 1px 3px rgba(11, 26, 51, 0.06);
  /* MR-M21-09 — establish a CSS container so the kv rows inside can
     reflow based on the panel's own width (independent of viewport). */
  container-type: inline-size;
  container-name: pe-detail-panel;
}

/* MR-M21-09 — protect dense expert content from near-vertical rendering.
   Long unbreakable hex tokens (AID/TAR/FID/rule IDs) use overflow-wrap so
   one identifier never pushes the panel to its narrowest column width.
   The min-width:0 on .app-kv-v lets a wide nested table shrink without
   forcing a horizontal scroll on the whole panel. */
.app-root .pe-detail-panel code,
.app-root .pe-detail-panel-eyebrow code {
  overflow-wrap: anywhere;
}
.app-root .pe-detail-panel .app-kv .app-kv-v {
  min-width: 0;
}
/* Nested expert tables (decoded payload, conformance) inside the kv
   value column scroll horizontally rather than render at one character
   per line when the panel is narrower than the table's natural width. */
.app-root .pe-detail-panel .app-kv .app-kv-v > table.app-table {
  display: block;
  overflow-x: auto;
  max-width: 100%;
}

/* MR-M21-09 — container-query collapse. When the panel itself is
   narrower than ~520px (regardless of viewport size — happens when the
   user opens a sidebar or uses a partner browser at fixed width), the
   .app-kv rows switch to a stacked label/value layout, returning the
   full panel width to the value column. Solves the "near-vertical
   text" BETA defect reported on the EF detail panel. */
@container pe-detail-panel (max-width: 520px) {
  .app-kv {
    flex-direction: column;
    gap: 2px;
  }
  .app-kv .app-kv-k {
    flex: none;
  }
}

.app-root .pe-detail-panel > .app-section {
  margin-bottom: var(--saip-spacing-3);
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
}

.app-root .pe-detail-panel > .app-section:last-child {
  margin-bottom: 0;
}

.app-root .pe-detail-panel h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ps-text-heading);
  margin: 0 0 var(--saip-spacing-2) 0;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--ps-border-hairline);
}

.app-root .pe-detail-panel-eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ps-text-secondary);
  font-weight: 700;
  margin-bottom: 4px;
}

/* Consolidated findings panel (grouped by severity). */
.app-root .pe-finding-group {
  margin-bottom: var(--saip-spacing-3);
  border-left: 4px solid var(--ps-border-hairline);
  background: var(--ps-bg-surface-muted);
  padding: var(--saip-spacing-2) var(--saip-spacing-3);
  border-radius: var(--saip-radius);
}

.app-root .pe-finding-group.is-critical { border-left-color: var(--saip-rose-500); background: var(--saip-rose-50); }
.app-root .pe-finding-group.is-major    { border-left-color: var(--saip-amber-500); background: var(--saip-amber-50); }
.app-root .pe-finding-group.is-minor    { border-left-color: var(--ps-sev-minor-fg); background: var(--saip-amber-50); }
.app-root .pe-finding-group.is-info     { border-left-color: var(--saip-teal-600); background: var(--saip-teal-50); }
.app-root .pe-finding-group.is-pass     { border-left-color: var(--saip-green-500); background: var(--saip-green-50); }
.app-root .pe-finding-group.is-skipped  { border-left-color: var(--ps-text-secondary); }
.app-root .pe-finding-group.is-error    { border-left-color: var(--ps-text-primary); }

.app-root .pe-finding-group-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ps-text-heading);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--saip-spacing-2);
}

.app-root .pe-finding-group-header-count {
  font-family: var(--saip-font-mono);
  font-weight: 600;
  color: var(--ps-text-primary);
}

/* Wrap legacy mock-pack and candidate-rule tables on result pages
   into a discreet <details> shell. */
.app-root details.pe-legacy-collapse > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--ps-text-heading-brand);
  padding: 6px 0;
  font-weight: 600;
}

.app-root details.pe-legacy-collapse[open] > summary {
  color: var(--ps-text-heading);
  margin-bottom: var(--saip-spacing-2);
}

.app-root details.pe-legacy-collapse > summary::before {
  content: "▶ ";
  display: inline-block;
  transition: transform 200ms ease-out;
}

.app-root details.pe-legacy-collapse[open] > summary::before {
  content: "▼ ";
}

/* MR-M21-09 / MR-M21-09c — workspace single-column collapse.
   Originally raised from 900px → 1199px in MR-M21-09; raised again to
   1399px in MR-M21-09c after the Sponsor's live review on a typical
   laptop (1280-1440px viewport) showed the file-system table forced
   into a narrow horizontal-scroll lane with most columns invisible.
   At <=1399px we stack the workspace single-column so both the file
   system table and the EF detail panel get the full content width.
   The journey_10 sticky-detail test viewport (1280px) sits at the
   stacked layout — but its assertions are scrolling-behaviour pinned,
   not two-column-grid pinned. */
@media (max-width: 1399px) {
  .app-root .pe-workspace {
    grid-template-columns: 1fr;
  }
  .app-root .pe-detail-panel {
    position: static;
    max-height: none;
    overflow-y: visible;
  }
}
@media (max-width: 900px) {
  /* Legacy single-column collapse — preserved for archival contracts. */
  .app-root .pe-workspace {
    grid-template-columns: 1fr;
  }
  .app-root .pe-detail-panel {
    position: static;
    max-height: none;
    overflow-y: visible;
  }
}

/* Phase 83D — brief highlight flash when a finding's target jump lands
   the user on the corresponding file row. The class is toggled off by
   the inline JS after ~1.6 s. */
.app-root tr.pe-row-flash {
  animation: pe-row-flash 1.6s ease-out;
}

@keyframes pe-row-flash {
  0% { background: var(--saip-amber-50); }
  50% { background: var(--saip-amber-50); }
  100% { background: transparent; }
}

/* ===========================================================
   MR-M21-09b — Profile Explorer Expert UX Crisis Redesign.

   The MR-M21-09 hotfix collapsed the workspace to single-column at
   <=1199px and added a 440px floor on the right detail panel, but the
   panel itself still stacked 6 dense cards top-to-bottom with no
   landing summary. Sponsor 2026-05-28 live review: still reads as an
   accumulation of widgets, not an expert analysis product.

   This block adds:

     - `.pe-ef-at-a-glance`   — the 30-second expert summary at the
       top of the EF detail panel (definition-list grid, 2-column).
     - `.pe-ef-sections`      — accordion container around the
       MR-M21-03 six expert blocks. Native <details>/<summary>; no JS.
     - `.pe-ef-section`       — one accordion section per block, with
       the Identity section open by default.
     - `.pe-mono`             — controlled monospace token rendering
       that wraps on word boundaries first, character boundary only as
       fallback — never mid-word.
     - `.pe-ef-decoded-list`  — decoded payload rendered as key/value
       cards instead of a nested <table> inside a narrow panel.
     - `.pe-ef-rules-headline` — per-EF rule severity rollup chips
       (material findings first; pass-only collapsed).
     - Hardening: any <code> inside the detail panel uses
       overflow-wrap:anywhere so no rule_id / AID / TAR / FID can ever
       render mid-character at panel widths >=520px.
   =========================================================== */

/* At-a-glance card — landing summary. */
.app-root .pe-ef-at-a-glance {
  background: linear-gradient(180deg, var(--saip-ice-50), var(--saip-ice-0));
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  padding: var(--saip-spacing-3) var(--saip-spacing-3) var(--saip-spacing-2);
  margin-bottom: var(--saip-spacing-3);
  box-shadow: 0 1px 2px rgba(11, 26, 51, 0.04);
}

.app-root .pe-ef-at-a-glance-eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--ps-text-heading);
  margin-bottom: var(--saip-spacing-2);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding-bottom: 4px;
}

.app-root .pe-ef-at-a-glance-grid {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  column-gap: var(--saip-spacing-3);
  row-gap: 6px;
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
}

.app-root .pe-ef-at-a-glance-grid > dt {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-secondary);
  min-width: 0;
  white-space: nowrap;
}

.app-root .pe-ef-at-a-glance-grid > dd {
  margin: 0;
  color: var(--ps-text-heading);
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: normal;
}

.app-root .pe-ef-at-a-glance-sub {
  font-size: 11px;
  color: var(--ps-text-secondary);
  margin-left: 6px;
}

.app-root .pe-na {
  font-size: 11px;
  color: var(--ps-text-secondary);
  font-style: italic;
}

/* M28-KEYSET-ALGO-MODE-G13-01 — algorithm known but the per-message mode
   bits are not stored in the SAIP keyset (TS 102 225 §5.1.2/§5.1.3 DES rows).
   Renders the honest "algorithm (mode not declared)" label muted, distinct
   from a fully-determinate AES chip. */
.app-root .pe-algo-undeclared {
  font-size: 11px;
  color: var(--ps-text-secondary);
  font-style: italic;
}

/* Compact pills used inside the at-a-glance card. */
.app-root .pe-pill-xs {
  font-size: 9px;
  padding: 1px 6px;
  margin-left: 6px;
  vertical-align: middle;
}

/* Mono token rendering — wrap on word boundary first, character boundary
   only as a fallback. NEVER mid-word. */
.app-root .pe-mono {
  font-family: var(--saip-font-mono);
  font-size: 11px;
  overflow-wrap: anywhere;
  word-break: normal;
}

/* Accordion sections wrapping the six MR-M21-03 expert blocks. */
.app-root .pe-ef-sections > details.pe-ef-section {
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius);
  background: var(--ps-bg-surface);
  margin-bottom: var(--saip-spacing-2);
  overflow: hidden;
}

.app-root details.pe-ef-section > summary.pe-ef-section-summary {
  cursor: pointer;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ps-text-heading);
  padding: 10px var(--saip-spacing-3);
  background: var(--ps-bg-surface-muted);
  list-style: none;
  user-select: none;
}

.app-root details.pe-ef-section > summary.pe-ef-section-summary::-webkit-details-marker {
  display: none;
}

.app-root details.pe-ef-section > summary.pe-ef-section-summary::before {
  content: "▶";
  display: inline-block;
  width: 14px;
  margin-right: 6px;
  font-size: 10px;
  color: var(--ps-text-secondary);
  transition: transform 150ms ease-out;
}

.app-root details.pe-ef-section[open] > summary.pe-ef-section-summary::before {
  transform: rotate(90deg);
}

.app-root details.pe-ef-section[open] > summary.pe-ef-section-summary {
  border-bottom: 1px solid var(--ps-border-hairline);
}

.app-root details.pe-ef-section > .app-stat-card {
  border: none;
  background: transparent;
  margin: 0;
  padding: var(--saip-spacing-3);
  box-shadow: none;
}

.app-root details.pe-ef-section > .pe-ef-decoded-cards {
  padding: var(--saip-spacing-3);
}

/* Linked-rules severity headline — chips row above the per-tier list. */
.app-root .pe-ef-rules-headline {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-bottom: var(--saip-spacing-2);
  padding: 6px 8px;
  background: var(--ps-bg-inset);
  border-radius: var(--saip-radius-sm);
}

.app-root .pe-ef-rules-subnote {
  font-size: 10px;
  color: var(--ps-text-secondary);
  font-style: italic;
}

/* Decoded payload — key/value cards (no nested table). */
.app-root .pe-ef-decoded-cards {
  margin-top: var(--saip-spacing-2);
}

.app-root .pe-ef-decoded-cards-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ps-text-secondary);
  margin-bottom: 6px;
}

.app-root ul.pe-ef-decoded-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
}

.app-root .pe-ef-decoded-item {
  display: grid;
  grid-template-columns: minmax(0, 140px) minmax(0, 1fr);
  gap: var(--saip-spacing-3);
  padding: 6px 8px;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-sm);
}

.app-root .pe-ef-decoded-key {
  font-size: 11px;
  color: var(--ps-text-primary);
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
}

.app-root .pe-ef-decoded-value {
  font-size: 11px;
  color: var(--ps-text-heading);
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: normal;
}

/* Below 700px (narrow panel) — stack the decoded key/value pair. */
@media (max-width: 700px) {
  .app-root .pe-ef-decoded-item {
    grid-template-columns: 1fr;
    gap: 2px;
  }
}

/* Hardening — every <code> token inside the detail panel uses
   overflow-wrap:break-word (NOT `anywhere`) so identifiers wrap on
   word boundaries first and only break mid-character as a last
   resort. `anywhere` was destroying intrinsic min-content sizing of
   table columns, which collapsed the FIELD column of the L2 data
   tables (decoded payload / conformance / findings / interpreted
   content) to ~1ch and produced character-per-line wrapping. The
   raw-hex blocks keep their intentional `word-break:break-all`
   inline style (hex is meant to break per-character). */
.app-root .pe-detail-panel code:not([data-raw-encoded-full]):not([data-raw-encoded-full-file]):not([data-field="content_hex"]) {
  overflow-wrap: break-word;
  word-break: normal;
}

/* ===========================================================
   MR-M21-09b HOTFIX (sponsor live review 2026-05-28).

   The accordion sections were deployed correctly but their content
   (the legacy MR-M21-03 `.app-kv` rows) still rendered with massive
   blank space between label and value when the container-query
   collapse changed `.app-kv` to flex-column. Root cause: the legacy
   `.app-kv .app-kv-v { flex: 1 }` from line 2043 stretched the value
   to fill the (very tall) column track that flex-column produced
   inside the section card.

   Fix: inside every `.pe-ef-section` of the EF detail panel, REPLACE
   the legacy `.app-kv` flex layout with a deterministic CSS-grid
   layout. The grid has predictable row heights (content-sized) and
   does not stretch. Above 520px panel width we render as a 2-column
   grid (label, value); below 520px we collapse to a single-column
   stack with tight 2px row gap.

   This pattern also drops the redundant eyebrow visual ("1 · Identity",
   "2 · Structure", …) when the accordion summary already names the
   section — those eyebrows are kept in the markup for backwards
   compatibility with the MR-M21-03 visual hooks but visually
   de-emphasised so the section reads as one tight grid.
   =========================================================== */

/* ===========================================================
   MR-M21-09c — Technical-Report-Driven Profile Explorer Rework
   (Sponsor 2026-05-28, AMD-027).

   The IDEMIA EPS technical reports use exactly three layouts for
   every section: L1 (dense bordered Parameter|Value table), L2
   (multi-column dense data table), L3 (numbered subsection heading
   §N.M.K). Every report-mirroring Profile Explorer surface uses
   these three primitives — no cards, no flex kv-rows, no nested
   tables inside <td>, no character-per-line wrapping.

   This block defines the canonical primitives. Per-surface markup
   converts to use them; legacy `.app-kv` / `.app-stat-card` /
   card-grid layouts are deprecated for report-mirroring surfaces.
   =========================================================== */

/* --- L3 — Numbered subsection heading -------------------------- */

.app-root .pe-report-section {
  margin: 0 0 var(--saip-spacing-3);
}

.app-root .pe-report-section-heading {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ps-text-heading);
  margin: 0 0 6px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--ps-border-strong);
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.app-root .pe-report-section-heading > .pe-report-section-num {
  font-family: var(--saip-font-mono);
  font-size: 11px;
  color: var(--ps-text-secondary);
  font-weight: 600;
  min-width: 36px;
}

.app-root .pe-report-section-heading > .pe-report-section-title {
  flex: 1 1 auto;
}

.app-root .pe-report-section-heading > .pe-report-section-meta {
  font-size: 10px;
  color: var(--ps-text-secondary);
  font-weight: 500;
  letter-spacing: 0;
}

/* --- L1 — Dense bordered Parameter|Value table ----------------- */
/* Use on a <table class="pe-report-table"> with one <tr> per row,
   <th class="pe-report-k"> for label, <td class="pe-report-v"> for value.
   Or use the <div class="pe-report-table"> + <div class="pe-report-row">
   variant when a real <table> isn't appropriate (no nested table inside
   <td> ever). Both render identically. */

.app-root .pe-report-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  font-size: 11px;
  line-height: 1.4;
  margin: 0 0 var(--saip-spacing-3);
  table-layout: fixed;
}

.app-root .pe-report-table > tbody > tr,
.app-root .pe-report-table > tr,
.app-root div.pe-report-table > .pe-report-row {
  display: table-row;
}

.app-root .pe-report-table .pe-report-k {
  width: 38%;
  padding: 5px 10px;
  background: var(--ps-bg-surface-muted);
  border: 1px solid var(--ps-border-hairline);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-primary);
  vertical-align: top;
  text-align: left;
  word-break: normal;
  overflow-wrap: anywhere;
}

.app-root .pe-report-table .pe-report-v {
  padding: 5px 10px;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  color: var(--ps-text-heading);
  vertical-align: top;
  text-align: left;
  word-break: normal;
  overflow-wrap: anywhere;
  min-width: 0;
}

.app-root .pe-report-table .pe-report-v code,
.app-root .pe-report-table .pe-report-v .pe-mono {
  font-family: var(--saip-font-mono);
  font-size: 11px;
  word-break: normal;
  overflow-wrap: anywhere;
}

/* `div.pe-report-table` variant — used where we can't have a <table>
   (e.g. inside another <td>). Identical visuals. */
.app-root div.pe-report-table {
  display: table;
  width: 100%;
}
.app-root div.pe-report-table > .pe-report-row > .pe-report-k,
.app-root div.pe-report-table > .pe-report-row > .pe-report-v {
  display: table-cell;
}

/* --- L2 — Dense multi-column data table ------------------------ */
.app-root .pe-report-data-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--ps-border-strong);
  background: var(--ps-bg-surface);
  font-size: 11px;
  line-height: 1.4;
  margin: 0 0 var(--saip-spacing-3);
}

.app-root .pe-report-data-table thead th {
  padding: 5px 8px;
  background: var(--ps-bg-surface-muted);
  color: var(--ps-text-secondary);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  text-align: left;
  border: 1px solid var(--ps-border-hairline);
  vertical-align: bottom;
}

.app-root .pe-report-data-table tbody td {
  padding: 5px 8px;
  border: 1px solid var(--ps-border-hairline);
  vertical-align: top;
  color: var(--ps-text-heading);
  word-break: normal;
  overflow-wrap: break-word;
}

.app-root .pe-report-data-table tbody tr:nth-child(even) td {
  background: var(--ps-bg-surface-muted);
}

.app-root .pe-report-data-table tbody td code,
.app-root .pe-report-data-table tbody td .pe-mono {
  font-family: var(--saip-font-mono);
  font-size: 10.5px;
  word-break: normal;
  overflow-wrap: break-word;
}

/* Inside the detail panel, force the L2 data tables to use a fixed
   table layout with explicit first-column width. Without this, the
   browser's auto algorithm collapses the field column to ~1ch when
   the value column carries a long unbreakable string. */
.app-root .pe-detail-panel .pe-report-data-table {
  table-layout: fixed;
  width: 100%;
}
.app-root .pe-detail-panel .pe-report-data-table thead th:first-child,
.app-root .pe-detail-panel .pe-report-data-table tbody td:first-child {
  width: 38%;
}

/* MR-M21-09c HOTFIX3 — explicit widths for the slimmed 5-col per-file
   findings table (Rule id · Title · Status · Severity · Message).
   Rule IDs are long monospace strings — give them 22%. Title needs
   ~26% so sentences don't wrap mid-word. Status / Severity are pill
   columns (~70px each). Message takes the rest. */
.app-root table#profile-explorer-file-findings-table {
  table-layout: fixed;
  width: 100%;
}
.app-root table#profile-explorer-file-findings-table thead th:nth-child(1),
.app-root table#profile-explorer-file-findings-table tbody td:nth-child(1) {
  width: 22%;
}
.app-root table#profile-explorer-file-findings-table thead th:nth-child(2),
.app-root table#profile-explorer-file-findings-table tbody td:nth-child(2) {
  width: 26%;
}
.app-root table#profile-explorer-file-findings-table thead th:nth-child(3),
.app-root table#profile-explorer-file-findings-table tbody td:nth-child(3),
.app-root table#profile-explorer-file-findings-table thead th:nth-child(4),
.app-root table#profile-explorer-file-findings-table tbody td:nth-child(4) {
  width: 8%;
}

/* Right-align numeric columns. */
.app-root .pe-report-data-table th.pe-report-num,
.app-root .pe-report-data-table td.pe-report-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Subdued caption row used for empty-data fallbacks. */
.app-root .pe-report-empty {
  padding: 8px 10px;
  font-size: 11px;
  color: var(--ps-text-secondary);
  font-style: italic;
  border: 1px dashed var(--ps-border-hairline);
  background: var(--ps-bg-surface-muted);
  border-radius: var(--saip-radius-sm);
  margin: 0 0 var(--saip-spacing-3);
}

/* --- Subtle table when nested inside accordion section --------- */
.app-root details.pe-ef-section .pe-report-table,
.app-root details.pe-ef-section .pe-report-data-table {
  margin-bottom: 0;
}

/* === Legacy MR-M21-09b CSS retained below for completeness ==== */

/* MR-M21-09b HOTFIX2 (sponsor live review 2026-05-28).
   Render the legacy `.app-kv` rows inside `.pe-ef-section` as a dense,
   bordered Parameter | Value table — the format expert technical
   reports use (IDEMIA EPS 2.4.1 3G Codes / 2.4.2 ADM Codes / 2.10
   Application tables). Single source of truth for the EF detail panel:
   every "kv row" is one table row with the parameter name on the left
   and the decoded value on the right, separated by a hairline border.
   No stretching, no card padding, no blank space between label and
   value.
   MR-M21-09c (Sponsor live re-review): the kept block below applies
   only to the LEGACY `.app-kv` markup that has not yet been rewritten.
   New surfaces use `.pe-report-table` / `.pe-report-data-table`
   directly. */

/* Strip the residual card chrome — inside an accordion section the
   surrounding accordion already provides borders + header. The card
   becomes a tight inline table container. */
.app-root .pe-detail-panel .pe-ef-section .app-stat-card {
  display: block;
  padding: 0;
  margin: 0;
  border: none;
  background: transparent;
  box-shadow: none;
}

/* Hide the legacy "1 · Identity" / "2 · Structure" eyebrow visual noise
   inside each accordion section. The accordion summary already names
   the section — the eyebrow is keeping it as a textual hook for the
   MR-M21-03 regression tests but removing it from view. */
.app-root .pe-detail-panel .pe-ef-section .app-stat-card > div:first-child:not([class]) {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(1px,1px,1px,1px);
  white-space: nowrap;
  margin: -1px;
  padding: 0;
  border: 0;
}

/* The legacy `.app-kv` row becomes a TABLE-ROW visual: a 2-column grid
   (label,value) with a hairline border, just like the EPS Parameter|Value
   tables. Predictable height = content height. NO stretching. */
.app-root .pe-detail-panel .pe-ef-section .app-kv {
  display: grid;
  grid-template-columns: minmax(140px, 38%) minmax(0, 1fr);
  column-gap: 0;
  row-gap: 0;
  padding: 0;
  border: 1px solid var(--ps-border-hairline);
  border-top: none;
  font-size: 11px;
  background: var(--ps-bg-surface);
}

.app-root .pe-detail-panel .pe-ef-section .app-kv:first-of-type {
  border-top: 1px solid var(--ps-border-hairline);
  border-top-left-radius: var(--saip-radius-sm);
  border-top-right-radius: var(--saip-radius-sm);
}

.app-root .pe-detail-panel .pe-ef-section .app-kv:last-of-type {
  border-bottom-left-radius: var(--saip-radius-sm);
  border-bottom-right-radius: var(--saip-radius-sm);
}

.app-root .pe-detail-panel .pe-ef-section .app-kv > .app-kv-k {
  display: block;
  flex: none;
  align-self: stretch;
  padding: 6px 10px;
  background: var(--ps-bg-surface-muted);
  border-right: 1px solid var(--ps-border-hairline);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-primary);
  margin: 0;
  line-height: 1.3;
  word-break: normal;
  overflow-wrap: anywhere;
}

.app-root .pe-detail-panel .pe-ef-section .app-kv > .app-kv-v {
  display: block;
  flex: none;
  align-self: stretch;
  padding: 6px 10px;
  min-width: 0;
  font-size: 11px;
  color: var(--ps-text-heading);
  margin: 0;
  line-height: 1.4;
  word-break: normal;
  overflow-wrap: anywhere;
}

/* The legacy "interpretation_state pill row" + decoded-cards + raw-hex
   <details> inside the Content section are NOT .app-kv rows; tighten
   their margins so they sit flush against the table. */
.app-root .pe-detail-panel .pe-ef-section .pe-ef-decoded-cards {
  padding: 0;
  margin: 8px 0 0;
}

/* On very narrow panel widths (<=420px), the 2-col table collapses to
   a single column. Each cell becomes its own row inside the same
   bordered container — still no blank space, just stacked. */
@container pe-detail-panel (max-width: 420px) {
  .pe-detail-panel .pe-ef-section .app-kv {
    grid-template-columns: minmax(0, 1fr);
  }
  .pe-detail-panel .pe-ef-section .app-kv > .app-kv-k {
    border-right: none;
    border-bottom: 1px solid var(--ps-border-hairline);
  }
}

/* MR-M21-09c — apply the same dense table-row treatment to ALL legacy
   `.app-kv` rows inside the EF detail panel (not only the accordion
   sections). This catches the legacy `#profile-explorer-file-detail-
   canonical` 8-field summary + the `file-detail__row` rows + the
   engineering <details> block. No template change needed for these
   surfaces; CSS only. */
.app-root .pe-detail-panel #profile-explorer-file-detail-canonical .app-kv,
.app-root .pe-detail-panel .file-detail__user .app-kv,
.app-root .pe-detail-panel .file-detail__engineering .app-kv {
  display: grid;
  grid-template-columns: minmax(140px, 38%) minmax(0, 1fr);
  column-gap: 0;
  row-gap: 0;
  padding: 0;
  border: 1px solid var(--ps-border-hairline);
  border-top: none;
  font-size: 11px;
  background: var(--ps-bg-surface);
}

.app-root .pe-detail-panel #profile-explorer-file-detail-canonical .app-kv:first-of-type,
.app-root .pe-detail-panel .file-detail__user .app-kv:first-of-type,
.app-root .pe-detail-panel .file-detail__engineering .app-kv:first-of-type {
  border-top: 1px solid var(--ps-border-hairline);
}

.app-root .pe-detail-panel #profile-explorer-file-detail-canonical .app-kv > .app-kv-k,
.app-root .pe-detail-panel .file-detail__user .app-kv > .app-kv-k,
.app-root .pe-detail-panel .file-detail__engineering .app-kv > .app-kv-k {
  display: block;
  flex: none;
  margin: 0;
  padding: 5px 10px;
  background: var(--ps-bg-surface-muted);
  border-right: 1px solid var(--ps-border-hairline);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--ps-text-primary);
  line-height: 1.3;
  word-break: normal;
  overflow-wrap: anywhere;
  align-self: stretch;
}

.app-root .pe-detail-panel #profile-explorer-file-detail-canonical .app-kv > .app-kv-v,
.app-root .pe-detail-panel .file-detail__user .app-kv > .app-kv-v,
.app-root .pe-detail-panel .file-detail__engineering .app-kv > .app-kv-v {
  display: block;
  flex: none;
  margin: 0;
  padding: 5px 10px;
  background: var(--ps-bg-surface);
  font-size: 11px;
  color: var(--ps-text-heading);
  line-height: 1.4;
  word-break: normal;
  overflow-wrap: anywhere;
  min-width: 0;
  align-self: stretch;
}

/* Drop the old "At a glance — canonical EF view" eyebrow on the legacy
   block when both the new MR-M21-09b at-a-glance card + the canonical
   block are present (we now hide the canonical block — same data, the
   new at-a-glance is the source of truth). */
.app-root .pe-detail-panel #profile-explorer-file-detail-canonical {
  display: none;
}

/* MR-M21-09c — strip the inline-bordered "Standards conformance —
   diagnostic" container chrome around the conformance table; the
   table itself carries the .pe-report-data-table styling. */
.app-root #profile-explorer-file-detail-conformance {
  padding: 0 !important;
  background: transparent !important;
  border: none !important;
}

.app-root #profile-explorer-file-detail-conformance > div:first-child {
  font-size: 12px !important;
  text-transform: none !important;
  letter-spacing: 0.02em !important;
  color: var(--ps-text-heading) !important;
  border-bottom: 1px solid var(--ps-border-strong);
  padding-bottom: 4px;
  margin-bottom: 6px !important;
}

/* ── M27 dashboard merge ── */
/* Sponsor 2026-06-12 dashboard-merge — per-section row overflow: the server
   renders ALL rows; rows beyond the first 10 are hidden until the CSP-safe
   "See more" toggle (dashboard_sections.js) adds .is-expanded on the
   section's [data-dash-rows] container. */
.app-root [data-dash-rows] .app-row-overflow {
  display: none;
}

.app-root [data-dash-rows].is-expanded .app-row-overflow {
  display: table-row;
}

/* ── Profile Element Explorer filter tiles (Sponsor 2026-06-18 Wave 2) ──
   The tiles are <button data-pe-filter-tile> rows. The RESTING and .is-active
   states are expressed PURELY in CSS here — never via inline style — so the JS
   reflectTiles() toggle (.is-active) gives an OBVIOUS visual change on click
   (defect B). Two palettes: GREEN (per-group filters) + GREY (diagnostic
   filters: All · With findings · Not fully interpreted). */
.app-root .pe-explorer-tile {
  font-size: 10px;
  line-height: 1.2;
  cursor: pointer;
  border-radius: 9px;
  padding: 3px 10px;
  border: 1px solid transparent;
  font-weight: 500;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.app-root .pe-explorer-tile:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 1px;
}

/* GREEN — per-group filters. Resting = light fill / green text; hover deepens;
   active = solid green fill / white text. */
.app-root .pe-explorer-tile-green {
  background: var(--saip-green-50);
  color: var(--saip-green-500);
  border-color: var(--saip-green-500);
}
.app-root .pe-explorer-tile-green:hover {
  background: #c8ecd7;
}
.app-root .pe-explorer-tile-green.is-active {
  background: var(--saip-green-500);
  color: #ffffff;
  border-color: var(--saip-green-500);
}

/* GREY — diagnostic filters. Resting = light grey / grey text; hover deepens;
   active = solid navy fill / white text. */
.app-root .pe-explorer-tile-grey {
  background: var(--saip-grey-200);
  color: var(--ps-text-primary);
  border-color: var(--ps-text-muted);
}
.app-root .pe-explorer-tile-grey:hover {
  background: var(--saip-grey-300);
}
.app-root .pe-explorer-tile-grey.is-active {
  background: var(--saip-navy-800);
  color: #ffffff;
  border-color: var(--ps-text-heading);
}

/* The horizontal tile STRIP (defect A): full-width, directly under the
   sub-banner title and ABOVE the 3-column grid. Grey row on top, green below. */
.app-root [data-pe-explorer-tilestrip] {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--saip-spacing-3);
}
.app-root [data-pe-explorer-tilestrip] [data-pe-explorer-tiles-grey],
.app-root [data-pe-explorer-tilestrip] [data-pe-explorer-tiles-green] {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}
.app-root [data-pe-explorer-tilestrip] .pe-explorer-tilestrip-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ps-text-secondary);
  margin-right: 2px;
}

/* ===================================================================== *
 * M34 Wave 1A — Active-Profile context spine (value-free cohesion bar)   *
 * SUITE-ACTIVE-PROFILE-SPINE-UX-01                                       *
 * ===================================================================== */
.app-root .app-active-profile {
  /* UXSESS-L1 — the federated "Working on" strip. Context ribbon identity
     kept (surface + violet left spine + hairline) but on SEMANTIC tokens:
     the raw light palette never flipped in dark (council a11y-M1 — the bar
     was a white island in dark mode). */
  background: var(--ps-bg-surface);
  /* SESSION-BAR-POLISH-01 (council B1) — the violet spine paints as an inset
     shadow instead of a border: a border-left consumed 3px of layout and
     pushed the bar content 3px off the canonical shell rail (the .app-wrap
     gutter absorbs the paint; 0.00px alignment doctrine). */
  box-shadow: inset 3px 0 0 var(--ps-accent);
  border-bottom: 1px solid var(--ps-border-hairline);
}
.app-root .app-active-profile[hidden] { display: none; }
.app-root .app-active-profile-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--saip-spacing-3);
  /* SESSION-BAR-POLISH-01 (council B1) — vertical padding ONLY: the former
     `padding: 8px 0` shorthand reset padding-inline to 0 and clobbered
     .app-wrap's --ps-auth-shell-gutter (same specificity, later in file),
     leaving the bar flush left of the canonical shell rail. */
  padding-block: 8px;
  flex-wrap: wrap;
}
/* SESSION-BAR-POLISH-01 — row-1 identity micro-labels (council M3): sentence
   case, secondary colour, never a second uppercase eyebrow; the filename link
   stays the only 700-weight element on the row. */
.app-root .app-ap-id-label {
  font-size: var(--saip-text-xs);
  font-weight: 500;
  color: var(--ps-text-secondary);
  white-space: nowrap;
}
.app-root .app-ap-id-type {
  font-size: var(--saip-text-sm);
  font-weight: 600;
  color: var(--ps-text-primary);
  white-space: nowrap;
}
.app-root .app-ap-id-sep { color: var(--ps-text-muted); }
.app-root .app-active-profile-id {
  display: flex;
  align-items: center;
  gap: var(--saip-spacing-2);
  min-width: 0;
  flex-wrap: wrap;
  margin-right: auto; /* UXSESS-L1: pins Session-details + Close to the trailing edge */
}
.app-root .app-active-profile-eyebrow {
  font-size: var(--saip-text-xs); /* 11px floor — a11y: no micro-text */
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ps-text-secondary);
  font-weight: 600;
}
.app-root .app-active-profile-file {
  font-size: var(--saip-text-base);
  font-weight: 700;
  color: var(--ps-text-heading);
  max-width: 38ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* UXSESS-L1 (council ux-M4) — the identity name IS the one rest-state
   handoff: a real link, underlined on hover only (quiet at rest). */
a.app-root .app-active-profile-file, .app-root a.app-active-profile-file {
  text-decoration: none;
}
.app-root a.app-active-profile-file:hover,
.app-root a.app-active-profile-file:focus-visible {
  text-decoration: underline;
}
.app-root a.app-active-profile-file:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}
.app-root .app-active-profile-count {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-secondary);
}
/* CMTIX-01 — the bar's conformance chip, ambient Clause pill, and
   .app-active-profile-actions container CSS retired with the row-2 actions
   band (the actions live on the Cockpit banner now). */
/* Quiet chips (the auth-link idiom), ≥24px targets — semantic tokens. */
.app-root .app-ap-action {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  font-size: var(--saip-text-xs);
  font-weight: 600;
  color: var(--ps-text-primary);
  text-decoration: none;
  padding: 3px 10px;
  border: 1px solid var(--ps-border-strong);
  border-radius: var(--saip-radius-sm);
  background: var(--ps-bg-surface);
  white-space: nowrap;
  cursor: pointer;
}
.app-root .app-ap-action:hover,
.app-root .app-ap-action:focus-visible {
  border-color: var(--ps-accent);
  background: var(--ps-accent-soft-bg);
}
.app-root .app-ap-action:focus-visible {
  outline: 2px solid var(--ps-focus-ring);
  outline-offset: 2px;
}
/* "Open analysis" stays the ONE filled primary inside the disclosure. */
.app-root .app-ap-action-primary {
  color: var(--saip-white);
  background: var(--ps-accent);
  border-color: var(--ps-accent);
}
.app-root .app-ap-action-primary:hover,
.app-root .app-ap-action-primary:focus-visible {
  color: var(--saip-white);
  background: var(--saip-navy-700);
  border-color: var(--ps-text-heading-brand);
}
/* UXSESS-L1 — the ONE global Close: ghost weight (arbitration 1 — nothing is
   destroyed; never destructive red). Border materialises on hover/focus. */
.app-root .app-ap-action-ghost {
  border-color: transparent;
  background: transparent;
  color: var(--ps-text-secondary);
}
.app-root .app-ap-action-ghost:hover,
.app-root .app-ap-action-ghost:focus-visible {
  border-color: var(--ps-border-strong);
  background: var(--ps-bg-inset);
  color: var(--ps-text-primary);
}
/* UXSESS-L1 — the "Session details" disclosure. The summary reuses the chip
   grammar (no new toggle style — council visual-M3); the opened panel is a
   second IN-FLOW full-width row of the strip (no card, no shadow, no radius —
   council visual-M2). Nothing interactive inside <summary> (visual-B1). */
.app-root .app-ap-details { display: contents; }
/* Chromium ≥131 wraps <details> content in ::details-content. SESSION-BAR-
   POLISH-01 root-cause fix: flattening the pseudo too (display:contents)
   removed the box its closed-state `content-visibility: hidden` acts on, so
   the panel LEAKED always-visible and the summary toggled nothing. The pseudo
   now stays a real box and becomes the wrapping flex item itself (order/basis
   here; engines without the pseudo ignore this and use .app-ap-panel's own
   order/basis below). Closed-state hiding is the UA default again. */
.app-root .app-ap-details::details-content { order: 10; flex-basis: 100%; }
.app-root .app-ap-summary { list-style: none; }
.app-root .app-ap-summary::-webkit-details-marker { display: none; }
.app-root .app-ap-summary::after { content: " ▾" / ""; font-size: 0.85em; }
.app-root .app-ap-details[open] > .app-ap-summary::after { content: " ▴" / ""; }
.app-root .app-ap-panel {
  /* order:10 wraps the OPEN panel under the whole strip row (the Close form
     stays on row 1); focus order (summary → panel links → Close) stays the
     DOM order — the panel is only traversed when open. */
  order: 10;
  flex-basis: 100%;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--saip-spacing-2) var(--saip-spacing-3);
  align-items: baseline;
  border-top: 1px solid var(--ps-border-hairline);
  padding: 8px 0;
}
.app-root .app-ap-group-label {
  font-size: var(--saip-text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ps-text-secondary);
  font-weight: 600;
  white-space: nowrap;
}
.app-root .app-ap-group-links {
  display: flex;
  align-items: center;
  gap: var(--saip-spacing-2);
  flex-wrap: wrap;
  min-width: 0;
}
.app-root .app-ap-opaque-id {
  font-family: var(--saip-font-mono);
  font-size: var(--saip-text-2xs);
  color: var(--ps-text-muted);
}
/* SESSION-BAR-POLISH-01 — metadata VALUES in the Session-details panel
   (label:value grid rows on the existing .app-ap-panel grammar; council M4).
   Measure capped so the value column never smears across the 1760px shell. */
.app-root .app-ap-meta-value {
  font-size: var(--saip-text-sm);
  color: var(--ps-text-primary);
  max-width: 60ch;
  overflow-wrap: anywhere;
}
/* CMTIX-01 — the SESSION-BAR-POLISH-01 row-2 ACTIONS band CSS retired with
   the band (the bar is identity + close only). */
/* UXSESS-L1 — the one-shot "Session closed" strip: NEUTRAL confirmation
   (closure is not a success — council visual-M4): the brand spine withdraws
   to a grey hairline spine; normal flow, never fixed. */
.app-root .app-session-toast {
  background: var(--ps-bg-surface);
  /* SESSION-BAR-POLISH-01 — same inset-shadow spine as the bar (0.00px rail). */
  box-shadow: inset 3px 0 0 var(--ps-border-strong);
  border-bottom: 1px solid var(--ps-border-hairline);
}
.app-root .app-session-toast-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--saip-spacing-3);
  padding: 8px 0;
  flex-wrap: wrap;
}
.app-root .app-session-toast .app-ap-action { text-decoration: none; }
/* UXSESS-L2 Pass-2 a11y (1.4.1 G183): the in-text "history" link must not be
   colour-alone against the strip copy — persistent underline. */
.app-root .app-session-toast-text a { text-decoration: underline; }
.app-root .app-session-toast-text {
  font-size: var(--saip-text-sm);
  color: var(--ps-text-primary);
}

/* COMPLIANCE-CLOSE-DEDUP-01 — the ONE global close-session confirmation dialog.
   Generic (design-system scope) because the federated active-profile bar renders
   on EVERY authenticated surface, not only Compliance (where the page-scoped
   .cmp-dialog lives). Token values mirror .cmp-dialog in conformance_pillar.css.
   Tokens only — no new raw hex. */
.app-root .app-close-dialog {
  border: 1px solid var(--ps-border-strong);
  border-radius: 12px;
  padding: 1.25rem 1.4rem;
  max-width: 30rem;
  color: var(--ps-text-primary);
  background: var(--ps-bg-surface);
}
.app-root .app-close-dialog::backdrop { background: var(--ps-backdrop); }
.app-root .app-close-dialog h2 { margin: 0 0 0.5rem; font-size: var(--saip-text-h2); }
.app-root .app-close-dialog-actions {
  display: flex;
  gap: 0.6rem;
  justify-content: flex-end;
  margin-top: 1.1rem;
}

/* ===================================================================== *
 * M34 Wave 1A — Dashboard workspace hero + 5-pillar launchpad            *
 * ===================================================================== */
.app-root .app-dash-hero {
  background: var(--ps-bg-surface-muted);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-lg);
  padding: var(--saip-spacing-5);
}
.app-root .app-dash-hero-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--saip-spacing-4);
  flex-wrap: wrap;
}
.app-root .app-dash-hero-sub {
  color: var(--ps-text-secondary);
  font-size: var(--saip-text-sm);
  margin: .35rem 0 0;
}
.app-root .app-dash-continue {
  display: flex;
  flex-direction: column;
  gap: 3px;
  text-decoration: none;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-sm);
  padding: 10px 14px;
  min-width: 220px;
}
.app-root .app-dash-continue:hover { border-color: var(--ps-accent); }
.app-root .app-dash-continue-eyebrow {
  font-size: var(--saip-text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ps-text-secondary);
  font-weight: 600;
}
.app-root .app-dash-continue-file {
  font-size: var(--saip-text-sm);
  font-weight: 600;
  color: var(--ps-text-heading);
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-root .app-dash-continue-health { display: flex; align-items: center; gap: 8px; }
.app-root .app-dash-continue-count { font-size: var(--saip-text-xs); color: var(--ps-text-secondary); }
.app-root .app-dash-launchpad {
  display: grid;
  /* DA-A1-NAV-FINISH-01 — 5 pillars in nav order (Studio first); responsive
     5-up that wraps to 2-up below tablet width. List markup → reset the
     default list styling. */
  grid-template-columns: repeat(5, 1fr);
  gap: var(--saip-spacing-3);
  margin-top: var(--saip-spacing-4);
  list-style: none;
  padding: 0;
}
.app-root .app-dash-launchpad > li { margin: 0; }
.app-root .app-dash-launchpad > li > .app-dash-pillar { height: 100%; }
@media (max-width: 760px) {
  .app-root .app-dash-launchpad { grid-template-columns: repeat(2, 1fr); }
}
.app-root .app-dash-pillar {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-decoration: none;
  background: var(--ps-bg-surface);
  border: 1px solid var(--ps-border-hairline);
  border-radius: var(--saip-radius-sm);
  padding: 12px 14px;
}
.app-root .app-dash-pillar:hover,
.app-root .app-dash-pillar:focus-visible {
  border-color: var(--ps-accent);
  box-shadow: var(--ps-shadow-card);
}
.app-root .app-dash-pillar-name {
  font-size: var(--saip-text-sm);
  font-weight: 700;
  color: var(--ps-text-heading);
}
.app-root .app-dash-pillar-desc {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-secondary);
}

/* M34 1A — finding -> KB tolerant-search handoff link (evidence trace). */
.app-root .app-kb-find {
  font-size: var(--saip-text-xs);
  font-weight: 600;
  color: var(--ps-link);
  text-decoration: none;
  margin-left: 6px;
  white-space: nowrap;
}
.app-root .app-kb-find:hover,
.app-root .app-kb-find:focus-visible { text-decoration: underline; }

/* ===================================================================== *
 * M34 Wave 1D — Consequence engine section (ranked + bucketed)           *
 * ===================================================================== */
.app-root .m34-consequences { }
.app-root .m34-conseq-frame {
  font-size: var(--saip-text-sm);
  color: var(--ps-text-secondary);   /* FT-DARK-TEXT */
  margin: .25rem 0 .6rem;
}
.app-root .m34-conseq-counts {
  display: flex;
  gap: var(--saip-spacing-3);
  flex-wrap: wrap;
  margin: 0 0 var(--saip-spacing-4);
}
.app-root .m34-conseq-count {
  font-size: var(--saip-text-xs);
  font-weight: 700;
  padding: 2px 10px;
  border-radius: 999px;
}
.app-root .m34-conseq-count.is-blocking { background: var(--saip-red-100); color: var(--saip-red-700); }
.app-root .m34-conseq-count.is-review   { background: var(--saip-amber-100); color: var(--saip-amber-600); }
.app-root .m34-conseq-count.is-info     { background: var(--saip-ice-100); color: var(--ps-text-heading-brand); }
.app-root .m34-conseq-bucket { margin-bottom: var(--saip-spacing-4); }
.app-root .m34-conseq-buckethead {
  font-size: var(--saip-text-sm);
  margin: 0 0 var(--saip-spacing-2);
  color: var(--ps-text-secondary);
}
.app-root .m34-conseq-item {
  border: 1px solid var(--ps-border-hairline);
  border-left-width: 3px;
  border-radius: 6px;
  padding: var(--saip-spacing-2) var(--saip-spacing-3);
  margin-bottom: 6px;
}
.app-root .m34-conseq-item.is-blocking { border-left-color: var(--saip-red-700); }
.app-root .m34-conseq-item.is-review   { border-left-color: #d97706; }
.app-root .m34-conseq-item.is-info     { border-left-color: var(--ps-text-heading-brand); }
.app-root .m34-conseq-summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--saip-spacing-2);
  flex-wrap: wrap;
}
.app-root .m34-conseq-sev {
  font-size: var(--saip-text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  border-radius: 4px;
}
.app-root .m34-conseq-sev.is-blocking { background: var(--saip-red-100); color: var(--saip-red-700); }
.app-root .m34-conseq-sev.is-review   { background: var(--saip-amber-100); color: var(--saip-amber-600); }
.app-root .m34-conseq-sev.is-info     { background: var(--saip-ice-100); color: var(--ps-text-heading-brand); }
.app-root .m34-conseq-title { font-weight: 600; font-size: var(--saip-text-sm); color: var(--ps-text-heading-brand); }   /* FT-DARK-TEXT */
.app-root .m34-conseq-subsystem { font-size: var(--saip-text-xs); color: var(--ps-text-secondary); margin-left: auto; }
.app-root .m34-conseq-body { margin-top: var(--saip-spacing-2); display: flex; flex-direction: column; gap: 4px; }
.app-root .m34-conseq-statement { font-size: var(--saip-text-sm); line-height: 1.55; margin: 0; }
.app-root .m34-conseq-fix { font-size: var(--saip-text-sm); color: var(--ps-text-secondary); margin: 0; }   /* FT-DARK-TEXT */
.app-root .m34-conseq-refs { font-size: var(--saip-text-xs); color: var(--ps-text-secondary); margin: 0; }

/* M34 Wave 1C — requirement conformance result (reason-coded chips + rows). */
.app-root .m34-conf-chip {
  font-size: var(--saip-text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 1px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.app-root .m34-conf-chip.is-met   { background: var(--ps-sev-pass-bg); color: var(--saip-green-700); }
.app-root .m34-conf-chip.is-unmet { background: var(--saip-red-100); color: var(--saip-red-700); }
.app-root .m34-conf-row.is-unmet td { background: rgba(155, 28, 28, 0.03); }
.app-root .m34-conf-table th {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-secondary);
  border-bottom: 1px solid var(--ps-border-hairline);
  padding: 6px 8px;
}
.app-root .m34-conf-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--ps-border-hairline);
  vertical-align: top;
}

/* Inline form on the Active-Profile bar — CMTIX-01: the only remaining
   consumer is the ghost Close-session form (save-reference moved to the
   Cockpit banner, which uses .pe-ws-inline-form). */
.app-root .app-ap-form { display: inline; margin: 0; }

/* ===================================================================== *
 * M34 Wave 2D — Fleet Query (aggregate chips + fact filters)             *
 * ===================================================================== */
.app-root .m34-fleet-agg { display: flex; flex-wrap: wrap; gap: var(--saip-spacing-2); }
.app-root .m34-fleet-chip {
  font-size: var(--saip-text-xs);
  font-weight: 600;
  color: var(--ps-text-heading-brand);
  text-decoration: none;
  background: var(--ps-bg-surface-muted);
  border: 1px solid var(--ps-border-hairline);
  border-radius: 999px;
  padding: 3px 12px;
}
.app-root .m34-fleet-chip:hover { border-color: var(--ps-text-heading-brand); }
.app-root .m34-fleet-chip.is-active { background: var(--saip-navy-700); color: var(--saip-ice-0); border-color: var(--ps-text-heading-brand); }
.app-root .m34-fleet-label {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-heading-brand);   /* FT-DARK-TEXT — filter label flips on the theme-aware section */
  text-decoration: none;
  margin-right: 8px;
  border-bottom: 1px solid transparent;
}
.app-root .m34-fleet-label.is-active { border-bottom-color: var(--ps-text-heading-brand); font-weight: 700; }
.app-root .m34-fleet-yes { color: var(--saip-green-700); font-weight: 700; }
.app-root .m34-fleet-no { color: var(--ps-text-muted); }

/* M34 1B / audit C5 — persistent STRUCTURAL mode pill on the Delta result. */
.app-root .m34-structural-pill {
  display: inline-block;
  vertical-align: middle;
  margin-left: 10px;
  font-size: var(--saip-text-2xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--ps-text-heading-brand);
  background: var(--saip-ice-100);
  border: 1px solid var(--ps-border-hairline);
  border-radius: 4px;
  padding: 2px 8px;
}

/* ===================================================================== *
 * M34 Wave 2E — Value-true compare (verdict-only) pill + chips           *
 * ===================================================================== */
.app-root .m34-valuetrue-pill {
  display: inline-block;
  vertical-align: middle;
  margin-left: 8px;
  font-size: var(--saip-text-2xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  /* FT-DC-PILL-CONTRAST-AA-01: the former deep-green keep (≈6.86:1) folds
     onto the darkened pass token (≈6.26:1) — RGB Δ≈14.6, imperceptible;
     both clear AA with margin. */
  color: var(--ps-sev-pass-fg);
  background: var(--ps-sev-pass-bg);
  border: 1px solid var(--ps-sev-pass-bd);
  border-radius: 4px;
  padding: 2px 8px;
}
/* PDLT-02/03 — value-true section classes (replaced the inline styles). */
.app-root .m34-vt-scope {
  display: inline-block; vertical-align: middle; margin-left: 8px;
  font-size: var(--saip-text-xs); font-weight: 600;
  color: var(--ps-text-secondary);
}
.app-root .m34-vt-intro {
  font-size: var(--saip-text-sm); color: var(--ps-text-secondary);
  margin: 2px 0 .6rem;
}
.app-root .m34-vt-family { margin-bottom: var(--saip-spacing-3); }
.app-root .m34-vt-famhead {
  font-size: var(--saip-text-sm);
  color: var(--ps-text-secondary);
  margin: 0 0 4px;
}
/* PDLT-07b — fixed column geometry so the AKA / Secret-codes / Keyset tables
   all share the SAME column widths and line up vertically (Sponsor 2026-07-23
   "on peut aligner un peu ?"). Widths ride on a <colgroup>; table-layout:fixed
   makes them authoritative, and the long hex values wrap inside their column. */
.app-root .m34-vt-table { width: 100%; table-layout: fixed; }
.app-root .m34-vt-col-member { width: 18%; }
.app-root .m34-vt-col-member--wide { width: 82%; }
.app-root .m34-vt-col-val { width: 32%; }
.app-root .m34-vt-col-result { width: 18%; }
.app-root .m34-vt-table th {
  padding: 4px 8px; text-align: left;
  font-size: var(--saip-text-xs); text-transform: uppercase;
  letter-spacing: .03em; color: var(--ps-text-secondary);
  border-bottom: 1px solid var(--ps-border-hairline);
}
.app-root .m34-vt-table td {
  padding: 5px 8px; vertical-align: top;
  border-bottom: 1px solid var(--ps-border-hairline);
  font-size: var(--saip-text-sm);
}
.app-root .m34-vt-result-col { text-align: right; }
/* PDLT-07 — owner-visible sensitive value bytes (mono, wrap long keysets). */
.app-root .m34-vt-val {
  font-family: var(--saip-font-mono); font-size: var(--saip-text-xs);
  color: var(--ps-text-heading); overflow-wrap: anywhere; word-break: break-all;
}
.app-root .m34-vt-chip {
  font-size: var(--saip-text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 1px 9px;
  border-radius: 999px;
  white-space: nowrap;
}
.app-root .m34-vt-chip.is-identical { background: var(--ps-sev-pass-bg); color: var(--saip-green-700); }
.app-root .m34-vt-chip.is-differs { background: var(--saip-red-100); color: var(--saip-red-700); }
.app-root .m34-vt-chip.is-only { background: var(--saip-amber-100); color: var(--saip-amber-600); }
.app-root .m34-vt-chip.is-indeterminate { background: var(--saip-ice-100); color: var(--ps-text-heading-brand); }
.app-root .m34-vt-row.is-differs td { background: rgba(155, 28, 28, 0.03); }

/* ===================================================================== *
 * FT-WAVES-2-5 batch B3 — inline-style token utilities                   *
 *                                                                        *
 * The recurring raw-hex inline-style idioms across the admin / auth /    *
 * onboarding templates (the dark-mode blockers, brief 02 T5) fold onto   *
 * these small token-backed utilities. Guard:                             *
 * tests/web/test_semantic_token_adoption.py                              *
 * ::test_template_inline_styles_carry_no_raw_hex_colours.                *
 * Mapping rule: --ps-* semantic first, --saip-* primitive fallback,      *
 * identical or ≤1 ramp step from the retired literal.                    *
 * ===================================================================== */

/* Body prose — was the raw dark-grey `font-size:base` + `line-height:1.6` idiom (×12). */
.app-root .app-prose {
  font-size: var(--saip-text-base);
  color: var(--ps-text-primary);
  line-height: 1.6;
}
/* Plain body text — was the raw dark-grey `font-size:base` idiom (+variants). */
.app-root .app-text-body {
  font-size: var(--saip-text-base);
  color: var(--ps-text-primary);
}
/* Secondary body text — was the raw mid-grey `font-size:base` idiom (+variants). */
.app-root .app-text-quiet {
  font-size: var(--saip-text-base);
  color: var(--ps-text-secondary);
}
/* Small helper/hint text — was the raw mid-grey `font-size:xs` idiom (+variants). */
.app-root .app-hint {
  font-size: var(--saip-text-xs);
  color: var(--ps-text-secondary);
}
/* Uppercase field eyebrow label — was the raw section-navy idiom (×10). */
.app-root .app-label-eyebrow {
  display: block;
  font-size: var(--saip-text-sm);
  /* F3 dark defect fix — was var(--saip-navy-700) (fixed navy, ~1.15:1 on the
     dark surface = invisible). --ps-text-heading-brand is byte-identical
     navy-700 in light and flips to a violet-tint (AA) in dark. */
  color: var(--ps-text-heading-brand);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
/* Full-width form input — was the raw light-grey-border idiom (×10). */
.app-root .app-input-plain {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--ps-border-strong);
  border-radius: 6px;
  font-size: var(--saip-text-md);
  margin-top: 4px;
  background: var(--ps-bg-surface);
  color: var(--ps-text-primary);
}
/* Compact filter/toolbar input — same border idiom, dense variant (×16). */
.app-root .app-input-inline {
  padding: 6px 8px;
  border: 1px solid var(--ps-border-strong);
  border-radius: 6px;
  font-size: var(--saip-text-sm);
}
/* Left-accent note — was the ad-hoc `background+border-left` callouts. */
.app-root .app-note {
  border-left: 4px solid var(--ps-border-strong);
  padding: 10px 14px;
}
.app-root .app-note-warn    { background: var(--ps-sev-major-bg);    border-left-color: var(--ps-sev-major-bd); }
.app-root .app-note-info    { background: var(--ps-sev-info-bg);     border-left-color: var(--ps-sev-info-bd); }
.app-root .app-note-success { background: var(--ps-sev-pass-bg);     border-left-color: var(--ps-sev-pass-bd); }
.app-root .app-note-danger  { background: var(--ps-sev-critical-bg); border-left-color: var(--ps-sev-critical-bd); }
.app-root .app-note-neutral { background: var(--ps-bg-inset);        border-left-color: var(--ps-border-strong); }
/* Simple ruled table cells — was the raw strong/hairline border-bottom idiom. */
.app-root .app-th-rule { padding: 8px; border-bottom: 1px solid var(--ps-border-strong); }
.app-root .app-td-rule { padding: 8px; border-bottom: 1px solid var(--ps-border-hairline); }
