/* ==========================================================================
   WealthX Design Kit — v3.0 · structural precision
   Linked once per screen: <link rel="stylesheet" href="../design-system/wx-kit.css">
   NEVER inlined. No REMOTE fonts. No decorative layer.

   Governing decisions: context/00-DECISIONS.md C-4
     · light-first, near-black CTA, one brandable accent that defaults to ink
     · one LOCALLY BUNDLED font — amended. The original rule said "system fonts
       only" because a third-party font REQUEST leaks a Client's IP on a
       regulated product. A local file makes no request, so the rationale
       survives. What the old rule cost us: a system stack renders SF on macOS
       and Segoe on Windows, so a regulated figure changed shape depending on
       the Partner's operating system. "Determinism is a product claim" — it
       now extends to the numerals themselves.
     · motion <=320ms, and never on an element containing a figure
     · provenance aqua/iris are NOT brandable (L7 s1). Dark mode re-earns their
       CONTRAST while holding their HUE identical (195deg / 256deg) — a network
       may not redefine them, but a rendering mode is not a network.
     · no ornament. Depth is four neutral surfaces, hairlines, and nothing else.

   Type is named by ROLE, not by size. The console has five voices, not nine
   sizes. Line-heights are declared in px so every block lands on the 4px grid.
   ========================================================================== */

/* Subset of Inter (SIL OFL 1.1), built offline by tools/build-font.py.
   Variable: wght 100-900 + opsz 14-32, so large text is optically corrected
   rather than merely scaled. See design-system/fonts/OFL.txt. */
@font-face {
  font-family: "WX Sans";
  src: url("fonts/wx-sans.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   1 · TOKENS
   ========================================================================== */
:root {
  color-scheme: light;
  /* -- surfaces: four steps, no more ------------------------------------ */
  --wx-canvas:    #FAFBFC;
  --wx-surface:   #FFFFFF;
  --wx-raise:     #F4F6F8;
  --wx-sunken:    #EEF1F4;

  /* -- text: four steps, every one of them WCAG AA on surface AND on raise.
        v2's ramp did not clear that bar: text-3 measured 3.14:1 and text-4
        1.95:1 against white. text-3 was what .wx-micro used — 175 times,
        on all 22 screens — so the console's most common voice was also its
        least legible. The old #8A929E is retained as text-4, demoted to
        decoration and disabled states. It is never body text again. ------- */
  --wx-text:      #0E1116;   /* 18.9:1 */
  --wx-text-2:    #414A57;   /*  9.0:1 */
  --wx-text-3:    #5F6875;   /*  5.6:1 — 5.2:1 on raise, still AA */
  --wx-text-4:    #8A929E;   /*  3.1:1 — decorative / disabled ONLY */

  /* -- lines ------------------------------------------------------------- */
  --wx-line:        rgba(14, 17, 22, .08);
  --wx-line-mid:    rgba(14, 17, 22, .13);
  --wx-line-strong: rgba(14, 17, 22, .20);

  /* -- brand: defaults to ink. A Topology B network overrides these three
        and nothing else. WealthX's own expression is typographic, not
        chromatic — which is why the palette has no decorative hue. -------- */
  --wx-brand:      #14181F;
  --wx-brand-lift: #2A313D;
  --wx-brand-wash: #EEF1F4;
  --wx-on-brand:   #FFFFFF;

  /* -- semantic: colour only where it carries meaning -------------------- */
  --wx-pos:       #12724A;   /* gain     */
  --wx-pos-wash:  #E7F3ED;
  --wx-neg:       #B8342B;   /* loss     */
  --wx-neg-wash:  #FBEBEA;
  --wx-warn:      #8C5B0F;   /* pending  */
  --wx-warn-wash: #FBF1E0;

  /* -- provenance: FIXED. Not brandable, not decorative.
        aqua = computed by the deterministic plane
        iris = generated by the probabilistic plane
        A compliance signal that changes meaning per network is not a signal.
        Dark mode adjusts LIGHTNESS only; the hue angles (195deg, 256deg) are
        byte-identical across themes. Re-earning contrast is not rebranding. */
  --wx-det:       #0B6E8F;
  --wx-det-wash:  #E4F1F6;
  --wx-prob:      #6242B8;
  --wx-prob-wash: #EFEBFA;

  /* -- focus: its own token. v2 focused with --wx-det, which is the
        DETERMINISTIC PROVENANCE mark — so every focused control wore the
        "this figure is computed" signal. A compliance mark that also means
        "keyboard is here" is not a mark. ---------------------------------- */
  --wx-focus:      #0B6E8F;
  --wx-focus-ring: rgba(11, 110, 143, .30);

  /* -- charts: muted navy -> sky -> sand -> sage, reused everywhere ------ */
  --wx-c1: #2E4E6E;  --wx-c2: #5B8CA8;  --wx-c3: #C2A878;
  --wx-c4: #6E8F72;  --wx-c5: #8A7CA8;  --wx-c6: #A8756B;

  /* -- type ---------------------------------------------------------------
        One bundled face. The system stack stays as fallback, so a failed
        font load degrades instead of breaking. --wx-mono survives for the
        two genuinely code-like surfaces only (Accord bounds, <code>) —
        monospace there carries "machine-readable", which is meaning, not
        decoration. No FIGURE uses it any more: Inter's tnum aligns rupee
        columns better than a monospace face and does it identically on
        every operating system. ------------------------------------------ */
  --wx-font: "WX Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --wx-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
             "Liberation Mono", monospace;

  /* The editorial register, added 4 Aug 2026 under "luxury within restraint".
     A display serif carries the Milanese note, but the stack is SYSTEM faces
     only — New York and Didot ship with Apple OSes, Georgia everywhere else.
     No remote font request, ever: C-4's rule is about the request, not the
     voice. For hero titles on editorial surfaces (landing, client-app) and
     NOWHERE on a work queue — a reconciliation table in Didot is theatre. */
  --wx-font-display: "New York", ui-serif, "Didot", "Bodoni MT", Georgia,
             "Times New Roman", serif;

  /* Five text sizes and four figure sizes. --wx-t-3xl (36px) and --wx-t-4xl
     (46px) are gone: both had zero uses. 11/12/13 remain because the console
     is dense, but they are no longer asked to express HIERARCHY — three steps
     inside a 2px range cannot. Hierarchy is weight, colour and space. */
  --wx-t-xs:   11px;
  --wx-t-sm:   12px;
  --wx-t-base: 13px;   /* console default — dense-table baseline */
  --wx-t-md:   15px;
  --wx-t-lg:   20px;   /* page title */
  --wx-t-xl:   28px;   /* the one figure a screen is about */
  --wx-t-d1:   34px;   /* display — editorial hero only, with --wx-font-display */
  --wx-t-d2:   44px;   /* display — editorial sub-hero only */

  /* Line-heights in PX, all multiples of 4. Vertical rhythm becomes provable
     rather than eyeballed — see .wx-flow. */
  --wx-lh-xs:   16px;
  --wx-lh-sm:   16px;
  --wx-lh-base: 20px;
  --wx-lh-md:   24px;
  --wx-lh-lg:   24px;
  --wx-lh-xl:   32px;
  --wx-lh-d1:   40px;
  --wx-lh-d2:   48px;

  /* -- space ------------------------------------------------------------- */
  --wx-1: 4px;   --wx-2: 8px;   --wx-3: 12px;  --wx-4: 16px;  --wx-5: 20px;
  --wx-6: 24px;  --wx-7: 32px;  --wx-8: 40px;  --wx-9: 56px;  --wx-10: 72px;

  /* -- shape: commit, do not sit between. 8px surfaces, 4px insets, pill for
        status. The old in-between 14px is what read as soft rather than
        precise; --wx-r-lg is gone. ---------------------------------------- */
  --wx-r-sm:   4px;
  --wx-r:      8px;
  --wx-r-full: 999px;

  /* -- elevation: hairlines and tonal shifts carry hierarchy.
        Shadow is for overlays only. ------------------------------------- */
  --wx-e1: 0 1px 2px rgba(14, 17, 22, .04);
  --wx-e2: 0 2px 8px rgba(14, 17, 22, .07);
  --wx-e3: 0 12px 32px rgba(14, 17, 22, .13);

  /* -- motion: the interaction scale. --wx-d-3 is the ceiling for anything
        the Partner triggered. C-4c (00-DECISIONS, 3 Aug 2026) opened a
        separate CHOREOGRAPHY scale — --wx-d-4/-5, defined in §17 and
        reachable only from wx-motion.js and wx-lander.js. Nothing a Partner
        clicks may exceed 320ms. See context/layers/L7-motion-spec.md. ----- */
  --wx-ease: cubic-bezier(.32, .72, 0, 1);
  --wx-d-1: 120ms;
  --wx-d-2: 200ms;
  --wx-d-3: 320ms;

  /* -- frame ------------------------------------------------------------- */
  --wx-rail: 72px;
  --wx-max: 1440px;

  /* The translucent topbar. This was the ONE colour in the kit hardcoded
     outside :root — an rgba(), so it slipped past a hex-literal audit. In dark
     it left a light bar under light text: the breadcrumb measured 1.06:1. */
  --wx-edge:   transparent;   /* light needs no lift line; shadow does that job */
  --wx-topbar: rgba(250, 251, 252, .88);
  --wx-scrim:  rgba(14, 17, 22, .32);
  /* text placed ON a filled semantic/ink surface; inverts with the theme */
  --wx-on-accent: #FFFFFF;
  /* muted text on the ink panel; must invert with the theme, because on
     dark the ink panel is near-WHITE and white-at-72% vanishes on it */
  --wx-on-brand-2: rgba(255, 255, 255, .72);
}

/* ==========================================================================
   1b · DARK — a token remap and nothing else.
   Every colour in this kit already lives in :root and the 22 screens contain
   zero hex literals, so dark costs no screen edits at all.

   Light remains the DEFAULT (C-4). Dark follows the operating system, and
   ?theme=light|dark overrides it — the same query-string idiom as ?client=,
   because localStorage is forbidden and context has to survive a full load.

   Two rules that are not stylistic:
     · Provenance holds its HUE (195deg aqua, 256deg iris) and re-earns only
       lightness. A compliance signal that changes meaning per rendering mode
       is not a signal.
     · Elevation is expressed as a LIGHTER SURFACE, not a shadow. Shadows do
       not read on dark; a raised thing must get closer to the light.
   Every pair below is verified >=4.5:1 by tools/contrast-audit.py.
   ========================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* Deeper page so a card reads as floating rather than painted on. The blue
     in the darks is 2-3 points, not a tint — enough that the neutrals stop
     looking like flat grey under a warm room light. */
  --wx-canvas:    #06070A;
  --wx-surface:   #0F1218;
  --wx-raise:     #171B23;
  --wx-sunken:    #030406;

  --wx-text:      #F4F6FA;   /* 15.9:1 on raise */
  --wx-text-2:    #AAB2BF;   /*  8.1:1 */
  --wx-text-3:    #828B99;   /*  5.0:1 */
  --wx-text-4:    #5E6675;   /*  3.0:1 — decorative / disabled ONLY */

  --wx-line:        rgba(255, 255, 255, .11);
  --wx-line-mid:    rgba(255, 255, 255, .17);
  --wx-line-strong: rgba(255, 255, 255, .27);
  /* The top edge where a raised surface catches light. This is a HAIRLINE, the
     thing the design language says carries hierarchy — not the ornament layer
     C-4 deleted. No glow, no gradient, no atmosphere: one 1px line. */
  --wx-edge: rgba(255, 255, 255, .07);

  /* Ink inverts. "Near-black CTA" was never about the colour black — it was
     about maximum contrast against the page. On dark that is near-white. */
  --wx-brand:      #F2F4F7;
  --wx-brand-lift: #FFFFFF;
  --wx-brand-wash: #232830;
  --wx-on-brand:   #0B0D10;

  --wx-pos:       #19A169;   /* 4.6:1 on its own wash, 5.5:1 on surface */
  --wx-pos-wash:  #0E2A1E;
  --wx-neg:       #D86058;
  --wx-neg-wash:  #2E1513;
  --wx-warn:      #BA7A14;   /* 4.6:1 on its own wash */
  --wx-warn-wash: #2A1D08;

  --wx-det:       #0E95C1;   /* hue 195deg — identical to light */
  --wx-det-wash:  #0A2530;
  --wx-prob:      #8F78CE;   /* hue 256deg — identical to light */
  --wx-prob-wash: #1D1533;

  --wx-focus:      #4FC2E4;
  --wx-focus-ring: rgba(79, 194, 228, .32);

  --wx-c1: #4B7FB3;  --wx-c2: #5B8CA8;  --wx-c3: #C2A878;
  --wx-c4: #6E8F72;  --wx-c5: #8A7CA8;  --wx-c6: #A8756B;

  --wx-topbar: rgba(11, 13, 16, .88);
  --wx-scrim:  rgba(3, 4, 6, .66);
  --wx-on-accent: #0B0D10;
  --wx-on-brand-2: rgba(11, 13, 16, .72);

  --wx-e1: 0 1px 2px rgba(0, 0, 0, .40);
  --wx-e2: 0 2px 8px rgba(0, 0, 0, .50);
  --wx-e3: 0 12px 32px rgba(0, 0, 0, .64);
}

/* The OS preference is resolved in wx-shell.js, which sets data-theme on <html>
   before first paint of the page body. Doing it there rather than with a
   second @media block is deliberate: duplicating 30 tokens to satisfy
   prefers-color-scheme would mean two places to keep in agreement, and they
   would drift. One selector, one source of truth. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   2 · RESET
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--wx-canvas);
  color: var(--wx-text);
  font: 400 var(--wx-t-base)/var(--wx-lh-base) var(--wx-font);
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums lining-nums;
  /* Inter carries an opsz axis (14-32). Letting the browser drive it means
     large text is optically corrected, not merely scaled up. */
  font-optical-sizing: auto;
}
h1, h2, h3, h4, p, figure { margin: 0; }
img, svg, canvas { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }
/* Focus has its own token. It used to borrow --wx-det, the deterministic
   provenance mark, so every focused control claimed to be a computed figure. */
:focus-visible {
  outline: 2px solid var(--wx-focus);
  outline-offset: 2px;
  border-radius: var(--wx-r-sm);
}
.wx-hide { display: none !important; }
.wx-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ==========================================================================
   3 · TYPE — five text voices, four figure sizes.

   Named by ROLE. v2 named by size (h1..h4) and it went wrong in three ways
   that were all visible in the markup:
     · .wx-h2 had ZERO uses, while .wx-h3 was squatted by display NUMERALS
       ("88%" wearing a heading class) because figures had no ramp of their own.
     · Section headers split 39/39 between .wx-h4 and .wx-eyebrow — a perfect
       tie between two patterns doing one job.
     · All 36 <h2> tags were styled .wx-h4, the SMALLEST heading, so the
       document outline and the visual hierarchy disagreed on every screen.

   The rule now: a heading is a heading, a figure is a figure, and the loudest
   thing on a screen is the number it exists to show — not its own title.
   ========================================================================== */

/* -- text ---------------------------------------------------------------- */
.wx-title {                                   /* one per screen */
  font-size: var(--wx-t-lg); font-weight: 600;
  letter-spacing: -.02em; line-height: var(--wx-lh-lg);
  color: var(--wx-text);
}
.wx-section {                                 /* THE section header */
  font-size: var(--wx-t-base); font-weight: 600;
  letter-spacing: -.006em; line-height: var(--wx-lh-base);
  color: var(--wx-text);
}
.wx-body    { font-size: var(--wx-t-base); line-height: var(--wx-lh-base); color: var(--wx-text-2); }
.wx-meta    { font-size: var(--wx-t-sm);   line-height: var(--wx-lh-sm);   color: var(--wx-text-3); }
.wx-caption { font-size: var(--wx-t-xs);   line-height: var(--wx-lh-xs);   color: var(--wx-text-3);
              font-weight: 500; letter-spacing: .01em; }

/* -- figures -------------------------------------------------------------
   Every figure on every screen. Tabular + lining so columns align on the
   decimal and a live value cannot reflow under the reader mid-glance.
   These no longer use --wx-mono: Inter's tnum aligns rupee columns better
   than a monospace face, and renders identically on every OS. ----------- */
.wx-display, .wx-num, .wx-num--sm, .wx-kpi__value, .wx-kpi__delta,
.wx-table .wx-n, .wx-tabs__count, .wx-tier, .wx-nba__meta b,
.wx-stepper__n, .wx-facet__n, .wx-rail__badge {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}
.wx-display {                                 /* the one figure a screen is about */
  font-size: var(--wx-t-xl); font-weight: 600;
  letter-spacing: -.022em; line-height: var(--wx-lh-xl);
  color: var(--wx-text);
}
.wx-num     { font-weight: 500; letter-spacing: -.004em; }
.wx-num--sm { font-size: var(--wx-t-sm); font-weight: 500; letter-spacing: -.004em; }

/* -- the editorial register (4 Aug 2026, "luxury within restraint") --------
   Display-serif hero type for editorial surfaces: landing, client-app, and
   native Home heroes. Never on a work queue, never on a figure — a number's
   authority is its provenance, not its typeface. -------------------------- */
.wx-display-1, .wx-display-2 {
  font-family: var(--wx-font-display);
  font-weight: 500;
  letter-spacing: -.018em;
  color: var(--wx-text);
}
.wx-display-1 { font-size: var(--wx-t-d1); line-height: var(--wx-lh-d1); }
.wx-display-2 { font-size: var(--wx-t-d2); line-height: var(--wx-lh-d2); letter-spacing: -.022em; }

/* Risk/return scorecard — one product's two claims, side by side, with the
   provenance marks doing the talking. From the reference teardown's
   scorecard idea; numbers via data-metric-id as always. */
.wx-scorecard {
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--wx-line); border-radius: var(--wx-r);
  background: var(--wx-surface);
}
.wx-scorecard > div { padding: var(--wx-4) var(--wx-5); }
.wx-scorecard > div + div { border-left: 1px solid var(--wx-line); }
.wx-scorecard__label { font-size: var(--wx-t-xs); font-weight: 500; letter-spacing: .01em;
                       color: var(--wx-text-3); }
.wx-scorecard__value { font-size: var(--wx-t-lg); font-weight: 600; letter-spacing: -.02em;
                       line-height: var(--wx-lh-lg); color: var(--wx-text);
                       font-variant-numeric: tabular-nums lining-nums; }

/* Morning brief — the day's deterministic figures, narrated. The card is a
   panel variant; the sentence inside carries the iris mark because it is
   generated, and every numeral in it is envelope-checked. */
.wx-brief { border-left: 2px solid var(--wx-prob); }

/* The doctor ladder — analyse (T0) → propose (T2) → execute (T3, locked).
   Three rungs, one per autonomy tier, rendered with the tier system that
   already exists. The locked rung shows its reason; it is never hidden. */
.wx-ladder { display: flex; flex-direction: column; gap: var(--wx-3); }
.wx-ladder__rung { display: flex; align-items: center; gap: var(--wx-3);
                   padding: var(--wx-3) var(--wx-4);
                   border: 1px solid var(--wx-line); border-radius: var(--wx-r);
                   background: var(--wx-surface); }
.wx-ladder__rung--done { border-color: var(--wx-line-strong); }
.wx-ladder__rung--locked { background: var(--wx-raise); color: var(--wx-text-3); }

/* Semantic text colour. Three, matching the three semantic tokens — a figure
   that is a gain, a loss, or awaiting something. Nothing else gets colour. */
.wx-pos  { color: var(--wx-pos); }
.wx-neg  { color: var(--wx-neg); }
.wx-warn { color: var(--wx-warn); }

/* -- TRANSITIONAL ---------------------------------------------------------
   Legacy selectors, kept only so the console stays intact between this
   commit and the screen sweep. Every one is deleted in Phase 6 and CI then
   fails on their reappearance. Do not use them in new markup. ----------- */
.wx-h1 { font-size: var(--wx-t-lg);  font-weight: 600; letter-spacing: -.02em;  line-height: var(--wx-lh-lg); color: var(--wx-text); }
.wx-h4 { font-size: var(--wx-t-base); font-weight: 600; letter-spacing: -.006em; line-height: var(--wx-lh-base); color: var(--wx-text); }
.wx-small { font-size: var(--wx-t-sm); line-height: var(--wx-lh-sm); color: var(--wx-text-2); }
.wx-micro { font-size: var(--wx-t-xs); line-height: var(--wx-lh-xs); color: var(--wx-text-3); }
.wx-eyebrow {
  font-size: var(--wx-t-xs); font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--wx-text-3);
}

/* ==========================================================================
   4 · LAYOUT — rhythm is the PARENT's job.

   v2 let every child set its own margin inline. Across 22 screens that became
   383 style="" attributes encoding 10 distinct margin-top values, 8
   margin-bottom and 9 gap — and 2/6/10/14/18px were not on the spacing scale
   at ALL. Roughly 77 declarations sat off-grid.

   That is what "cluttered" actually was. Not ornament, not element count: the
   eye re-measuring every region because no two gaps agreed. A child must not
   know what follows it, so it cannot be the thing that decides the gap.

   Three rhythms. There is no fourth.
   ========================================================================== */
.wx-wrap  { max-width: var(--wx-max); margin-inline: auto; padding-inline: var(--wx-6); }
.wx-row   { display: flex; align-items: center; gap: var(--wx-3); }
.wx-row-b { display: flex; align-items: center; justify-content: space-between; gap: var(--wx-3); }
.wx-col   { display: flex; flex-direction: column; gap: var(--wx-3); }
.wx-grid  { display: grid; gap: var(--wx-4); }
.wx-spacer{ flex: 1; }
.wx-divide{ height: 1px; background: var(--wx-line); border: 0; margin: var(--wx-5) 0; }

/* Vertical flow. Spaces siblings without either of them knowing it happened,
   which is why it replaces margin-top/margin-bottom rather than renaming it. */
/* The modifiers carry the rule themselves rather than only setting the
   variable. A modifier that silently does nothing without its base class is a
   trap, and this one is applied by a script. */
.wx-flow > * + *,
.wx-flow--tight > * + *,
.wx-flow--loose > * + * { margin-block-start: var(--wx-flow, var(--wx-4)); }
.wx-flow--tight { --wx-flow: var(--wx-2); }
.wx-flow--loose { --wx-flow: var(--wx-6); }

/* A label and the figure it names are ONE unit, not two blocks in a rhythm.
   4px is the only gap that says "these belong to each other"; it is a
   component, not a fourth rhythm. This is what the 28 style="margin-top:4px"
   attributes were all reaching for. */
.wx-titleblock > * + * { margin-block-start: var(--wx-1); }

/* The remaining named spacing relationships. Each one existed already as a
   hand-tuned inline margin on several screens; naming them is what stops the
   next person picking 14px because it looked right that afternoon. */
.wx-spaced  { margin-block: var(--wx-2); }        /* a bar or stack between two lines of text */
.wx-actions { margin-block-start: var(--wx-5); }  /* the action row that closes a block */

/* Deliberately flush: a list whose items carry their own dividing rules, so a
   gap would double the separation. gap:0 is a decision, not an omission. */
.wx-col--flush, .wx-grid--flush, .wx-row--flush { gap: 0; }

/* Gap modifiers, same three rhythms, for flex/grid containers. */
.wx-col--tight, .wx-row--tight, .wx-grid--tight { gap: var(--wx-2); }
.wx-col--loose, .wx-row--loose, .wx-grid--loose { gap: var(--wx-6); }

/* .wx-row is nowrap by default, which is right for a toolbar and wrong for a
   set of chips: in a 340px rail twelve of them overlap into illegibility
   rather than flowing onto a second line. */
.wx-row--wrap { flex-wrap: wrap; }

/* Column shapes the screens actually use, so a two-column page does not need
   an inline grid-template-columns to exist. */
.wx-grid--sidebar { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; }
.wx-grid--halves  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.wx-grid--thirds  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1080px) {
  .wx-grid--sidebar, .wx-grid--halves, .wx-grid--thirds { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   5 · SURFACES
   ========================================================================== */
.wx-card {
  background: var(--wx-surface);
  border: 1px solid var(--wx-line);
  border-radius: var(--wx-r);
  padding: var(--wx-5);
}
.wx-card--flush { padding: 0; overflow: hidden; }
.wx-card--hover { transition: border-color var(--wx-d-1) var(--wx-ease); }
.wx-card--hover:hover { border-color: var(--wx-line-strong); }
.wx-editorial { position: relative; overflow: hidden; min-height: 220px; background: var(--wx-sunken); border-radius: var(--wx-r); }
.wx-editorial img { width: 100%; height: 100%; min-height: 220px; object-fit: cover; display: block; }
.wx-editorial__overlay { position: absolute; inset: auto var(--wx-5) var(--wx-5) auto; padding: var(--wx-3); background: var(--wx-surface); border: 1px solid var(--wx-line); border-radius: var(--wx-r); color: var(--wx-text-2); font-size: var(--wx-t-sm); }
.wx-access { display: inline-flex; align-items: center; gap: var(--wx-1); font-size: var(--wx-t-xs); color: var(--wx-text-2); }
.wx-access::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--wx-brand); }
.wx-access--research::before { background: var(--wx-text-3); }
.wx-access--blocked::before { background: var(--wx-warn); }
.wx-brand-mark { width: 20px; height: 20px; color: currentColor; }
.wx-attribution { color: var(--wx-text-3); }
.wx-panel {
  background: var(--wx-surface);
  border: 1px solid var(--wx-line);
  border-radius: var(--wx-r);
}
.wx-panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--wx-3);
  padding: var(--wx-4) var(--wx-5);
  border-bottom: 1px solid var(--wx-line);
}
.wx-panel__body { padding: var(--wx-5); }
.wx-inset { background: var(--wx-raise); border-radius: var(--wx-r); padding: var(--wx-4); }

/* ==========================================================================
   6 · CONTROLS
   ========================================================================== */
.wx-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--wx-2);
  height: 32px; padding: 0 var(--wx-4);
  background: var(--wx-surface);
  border: 1px solid var(--wx-line-mid);
  border-radius: var(--wx-r);
  font-size: var(--wx-t-base); font-weight: 500;
  cursor: pointer; white-space: nowrap;
  transition: background var(--wx-d-1) var(--wx-ease),
              border-color var(--wx-d-1) var(--wx-ease),
              transform var(--wx-d-1) var(--wx-ease);
}
.wx-btn:hover  { background: var(--wx-raise); border-color: var(--wx-line-strong); }
.wx-btn:active { transform: scale(.985); }          /* press feedback, <160ms */
.wx-btn[disabled] { opacity: .45; cursor: not-allowed; }
.wx-btn[disabled]:active { transform: none; }

.wx-btn--primary {
  background: var(--wx-brand); border-color: var(--wx-brand); color: var(--wx-on-brand);
}
.wx-btn--primary:hover { background: var(--wx-brand-lift); border-color: var(--wx-brand-lift); }
.wx-btn--ghost   { background: transparent; border-color: transparent; color: var(--wx-text-2); }
.wx-btn--ghost:hover { background: var(--wx-raise); color: var(--wx-text); }
.wx-btn--danger  { color: var(--wx-neg); border-color: var(--wx-line-mid); }
.wx-btn--danger:hover { background: var(--wx-neg-wash); border-color: var(--wx-neg); }
.wx-btn--sm { height: 28px; padding: 0 var(--wx-3); font-size: var(--wx-t-sm); }
.wx-btn--lg { height: 40px; padding: 0 var(--wx-5); font-size: var(--wx-t-md); }
.wx-btn--icon { width: 32px; padding: 0; }

/* A T3 capability the policy engine will refuse. Rendered visibly locked,
   with the blocking reason adjacent — never hidden, never silently absent.
   context/00-DECISIONS.md C-3. */
.wx-btn--locked {
  background: var(--wx-raise); border-color: var(--wx-line);
  color: var(--wx-text-3); cursor: not-allowed;
}
.wx-btn--locked:active { transform: none; }

.wx-field { display: flex; flex-direction: column; gap: var(--wx-2); }
.wx-label { font-size: var(--wx-t-sm); font-weight: 500; color: var(--wx-text-2); }
.wx-help  { font-size: var(--wx-t-xs); color: var(--wx-text-3); }
.wx-input {
  height: 32px; padding: 0 var(--wx-3);
  background: var(--wx-surface);
  border: 1px solid var(--wx-line-mid); border-radius: var(--wx-r);
  transition: border-color var(--wx-d-1) var(--wx-ease);
}
.wx-input:hover { border-color: var(--wx-line-strong); }
.wx-input:focus { outline: none; border-color: var(--wx-focus); box-shadow: 0 0 0 3px var(--wx-focus-ring); }
.wx-input[readonly] { background: var(--wx-raise); color: var(--wx-text-2); }
textarea.wx-input { height: auto; padding: var(--wx-3); resize: vertical; }

/* segmented control — Male|Female|Other, All|Internal|External */
.wx-seg {
  display: inline-flex; padding: 2px; gap: 2px;
  background: var(--wx-raise); border: 1px solid var(--wx-line);
  border-radius: var(--wx-r);
}
.wx-seg button {
  height: 26px; padding: 0 var(--wx-3);
  background: transparent; border: 0; border-radius: calc(var(--wx-r) - 2px);
  font-size: var(--wx-t-sm); color: var(--wx-text-2); cursor: pointer;
  transition: background var(--wx-d-1) var(--wx-ease);
}
.wx-seg button[aria-selected="true"] {
  background: var(--wx-surface); color: var(--wx-text);
  font-weight: 500; box-shadow: var(--wx-e1);
}

.wx-range { width: 100%; accent-color: var(--wx-brand); }

/* underlined text tabs — the reference's page-level tab pattern */
.wx-tabs { display: flex; gap: var(--wx-5); border-bottom: 1px solid var(--wx-line); }
.wx-tabs button {
  position: relative; padding: var(--wx-3) 0;
  background: none; border: 0; cursor: pointer;
  font-size: var(--wx-t-base); color: var(--wx-text-3);
  transition: color var(--wx-d-1) var(--wx-ease);
}
.wx-tabs button:hover { color: var(--wx-text-2); }
.wx-tabs button[aria-selected="true"] { color: var(--wx-text); font-weight: 500; }
.wx-tabs button[aria-selected="true"]::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; background: var(--wx-text);
}
.wx-tabs__count {
  margin-left: var(--wx-2); padding: 1px 6px;
  background: var(--wx-sunken); border-radius: var(--wx-r-full);
  font-size: var(--wx-t-xs); color: var(--wx-text-2);
}

/* ==========================================================================
   7 · PROVENANCE — the one surface decision that carries the product
   L7 s1. Aqua means computed. Iris means generated. Every figure and every
   generated sentence is marked. These two hues are not brandable.
   ========================================================================== */
.wx-prov {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 7px 1px 5px;
  border-radius: var(--wx-r-full);
  font-size: var(--wx-t-xs); font-weight: 500;
  white-space: nowrap; vertical-align: middle;
}
.wx-prov::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.wx-prov--det  { background: var(--wx-det-wash);  color: var(--wx-det); }
.wx-prov--prob { background: var(--wx-prob-wash); color: var(--wx-prob); }

/* A generated sentence. Its left rule is the mark — no chip needed inline. */
.wx-narration {
  border-left: 2px solid var(--wx-prob);
  padding-left: var(--wx-3);
  color: var(--wx-text-2);
}
.wx-narration__src {
  display: block; margin-top: var(--wx-2);
  font-size: var(--wx-t-xs); color: var(--wx-text-3);
}

/* ==========================================================================
   8 · STATUS
   ========================================================================== */
.wx-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 20px; padding: 0 8px;
  background: var(--wx-sunken); border-radius: var(--wx-r-full);
  font-size: var(--wx-t-xs); font-weight: 500; color: var(--wx-text-2);
  white-space: nowrap;
}
.wx-chip--pos  { background: var(--wx-pos-wash);  color: var(--wx-pos); }
.wx-chip--neg  { background: var(--wx-neg-wash);  color: var(--wx-neg); }
.wx-chip--warn { background: var(--wx-warn-wash); color: var(--wx-warn); }

/* Autonomy tier badges. Same badge means the same thing on every surface —
   agents, capabilities, actions. T3 reads as restricted by design. */
.wx-tier {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 20px; padding: 0 5px;
  border: 1px solid var(--wx-line-mid); border-radius: var(--wx-r-sm);
  font-size: 10px; font-weight: 600;
  color: var(--wx-text-2); background: var(--wx-surface);
}
.wx-tier--t0 { color: var(--wx-text-3); }
.wx-tier--t1 { color: var(--wx-det);  border-color: var(--wx-det); }
.wx-tier--t2 { color: var(--wx-warn); border-color: var(--wx-warn); }
.wx-tier--t3 { color: var(--wx-neg);  border-color: var(--wx-neg); background: var(--wx-neg-wash); }

.wx-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--wx-text-4); flex: none; }
.wx-dot--live { background: var(--wx-pos); }
.wx-dot--warn { background: var(--wx-warn); }
.wx-dot--neg  { background: var(--wx-neg); }

/* ==========================================================================
   9 · NUDGE / NEXTBESTACTION / ACCORD
   Three concepts, three visual weights. If they look alike, users learn they
   are the same thing and the conceptual distinction is lost. L7 S-A4.
   ========================================================================== */
/* Nudge — lightest. Dismissible, no side effects, not measured. */
.wx-nudge {
  display: flex; align-items: flex-start; gap: var(--wx-3);
  padding: var(--wx-3);
  background: var(--wx-raise); border-radius: var(--wx-r);
  font-size: var(--wx-t-sm); color: var(--wx-text-2);
}
.wx-nudge__x {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--wx-text-3); line-height: 1;
}

/* NextBestAction — mid weight. Carries score, impact, effort, and a tracked
   completion state. A Nudge you measure is a NextBestAction. */
.wx-nba {
  border: 1px solid var(--wx-line-mid); border-left: 3px solid var(--wx-brand);
  border-radius: var(--wx-r); padding: var(--wx-4);
  background: var(--wx-surface);
}
/* The lead action is the ranked one. Rank is carried STRUCTURALLY — a heavier
   rule and a filled CTA — rather than by printing a score nobody can audit. */
.wx-nba--lead { border-left-width: 3px; border-left-color: var(--wx-text); box-shadow: var(--wx-e1); }

.wx-nba__meta {
  display: flex; flex-wrap: wrap; gap: var(--wx-3); margin-top: var(--wx-3);
  font-size: var(--wx-t-xs); color: var(--wx-text-3);
}
/* Facts read as a sentence fragment, separated by a mark rather than sitting
   in fake columns. Three label/value pairs implied a schema that did not
   exist: one row's "Impact" was a rupee figure and the next row's was the
   word "Retention". That is a slot being filled, not a column. */
.wx-nba__meta > span + span::before { content: '·'; margin-inline-end: var(--wx-2); color: var(--wx-text-4); }
.wx-nba__meta b { color: var(--wx-text-2); font-weight: 600; }

/* Accord — heaviest element in its rail. A standing, consented,
   compliance-bounded authorisation for a class of future action. */
.wx-accord {
  border: 1px solid var(--wx-text); border-radius: var(--wx-r);
  padding: var(--wx-5); background: var(--wx-surface); box-shadow: var(--wx-e2);
}
.wx-accord__bounds {
  margin-top: var(--wx-3); padding: var(--wx-3);
  background: var(--wx-raise); border-radius: var(--wx-r-sm);
  /* the ONE surviving monospace surface: these bounds are machine-read */
  font-family: var(--wx-mono); font-size: var(--wx-t-xs); color: var(--wx-text-2);
}

/* ==========================================================================
   10 · DATA — KPI, table, bars
   ========================================================================== */
.wx-kpi { display: flex; flex-direction: column; gap: var(--wx-1); }
.wx-kpi__label {
  font-size: var(--wx-t-xs); color: var(--wx-text-3);
  display: flex; align-items: center; gap: var(--wx-2);
}
.wx-kpi__value {
  font-size: var(--wx-t-lg); font-weight: 600;
  letter-spacing: -.015em; line-height: var(--wx-lh-lg);
}
.wx-kpi__delta { font-size: var(--wx-t-sm); }
.wx-kpi-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--wx-5);
}

/* Reference-grade table: sticky header, sortable, configurable columns,
   right-aligned numerals, hairline rules, no zebra. */
.wx-table-scroll { overflow: auto; }
.wx-table { font-size: var(--wx-t-base); }
/* A secondary table nested inside a panel, where the outer table is the
   subject and this one is supporting detail. */
.wx-table--sm { font-size: var(--wx-t-sm); }
/* A colour key square preceding a label. */
.wx-thumb {
  border-radius: var(--wx-r);
  margin-block-end: var(--wx-3);
  overflow: hidden;
}
/* Creative artwork is inline SVG with a viewBox and no height attribute, so
   the aspect ratio comes from the box rather than from an invalid attribute. */
.wx-thumb > svg { display: block; width: 100%; height: auto; }
.wx-swatch {
  display: inline-block; width: 8px; height: 8px; flex: none;
  border-radius: 2px; margin-inline-end: var(--wx-2);
}
.wx-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--wx-surface);
  padding: var(--wx-3) var(--wx-4);
  border-bottom: 1px solid var(--wx-line-mid);
  font-size: var(--wx-t-xs); font-weight: 600; color: var(--wx-text-3);
  text-align: left; white-space: nowrap;
}
.wx-table tbody td {
  padding: var(--wx-3) var(--wx-4);
  border-bottom: 1px solid var(--wx-line);
  vertical-align: middle;
}
.wx-table tbody tr:last-child td { border-bottom: 0; }
.wx-table tbody tr { transition: background var(--wx-d-1) var(--wx-ease); }
.wx-table tbody tr:hover { background: var(--wx-raise); }
.wx-table .wx-n {
  text-align: right; }
.wx-table th.wx-n { text-align: right; }
.wx-table__sort { cursor: pointer; user-select: none; }
.wx-table__sort::after { content: ' \2195'; color: var(--wx-text-4); }
.wx-table__sort[aria-sort="ascending"]::after  { content: ' \2191'; color: var(--wx-text); }
.wx-table__sort[aria-sort="descending"]::after { content: ' \2193'; color: var(--wx-text); }

.wx-bar { height: 6px; background: var(--wx-sunken); border-radius: var(--wx-r-full); overflow: hidden; }
.wx-bar > i { display: block; height: 100%; background: var(--wx-brand); border-radius: inherit; }

/* 100% stacked allocation bar */
.wx-stack { display: flex; height: 10px; border-radius: var(--wx-r-full); overflow: hidden; }
.wx-stack > i { display: block; height: 100%; }
.wx-legend { display: flex; flex-wrap: wrap; gap: var(--wx-4); margin-top: var(--wx-3); }
.wx-legend > span { display: inline-flex; align-items: center; gap: 6px; font-size: var(--wx-t-sm); color: var(--wx-text-2); }
.wx-legend i { width: 8px; height: 8px; border-radius: 2px; flex: none; }

/* completeness — "what is still missing". Absorbed from Track B; a Proposal's
   validity depends on profile completeness, so this is first-class. */
.wx-completeness { display: flex; flex-direction: column; gap: var(--wx-2); }
.wx-completeness__missing {
  display: flex; flex-wrap: wrap; gap: var(--wx-2);
  font-size: var(--wx-t-xs); color: var(--wx-warn);
}

/* ==========================================================================
   11 · WIZARDS — left stepper (KYC, proposal) and top segments (GIFT)
   ========================================================================== */
.wx-stepper { display: flex; flex-direction: column; gap: 2px; }
.wx-stepper__item {
  display: flex; align-items: center; gap: var(--wx-3);
  padding: var(--wx-3);
  border-radius: var(--wx-r);
  font-size: var(--wx-t-base); color: var(--wx-text-3);
  cursor: pointer; text-align: left; background: none; border: 0; width: 100%;
}
.wx-stepper__item:hover { background: var(--wx-raise); }
.wx-stepper__item[aria-current="step"] { background: var(--wx-raise); color: var(--wx-text); font-weight: 500; }
.wx-stepper__n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: none;
  border: 1px solid var(--wx-line-mid); border-radius: 50%;
  font-size: 10px;
}
/* A completed step draws its tick in CSS rather than typing "\2713".
   Two reasons, and neither is taste: that codepoint sits inside the emoji
   range verify-artefacts.sh forbids, and a typed tick renders in whatever
   font happens to own the glyph — so a completed step changed shape per OS,
   the same defect the bundled font exists to end. */
.wx-stepper__item[data-done="true"] .wx-stepper__n,
.wx-stepper__n[data-done="true"] {
  background: var(--wx-pos); border-color: var(--wx-pos); color: transparent;
  position: relative;
}
.wx-stepper__item[data-done="true"] .wx-stepper__n::after,
.wx-stepper__n[data-done="true"]::after {
  content: ''; position: absolute;
  left: 50%; top: 50%;
  width: 4px; height: 8px;
  margin: -5px 0 0 -2px;
  border: solid var(--wx-on-accent); border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.wx-stepper__item[aria-current="step"] .wx-stepper__n {
  background: var(--wx-text); border-color: var(--wx-text); color: var(--wx-on-accent);
}

.wx-progress-seg { display: flex; gap: 4px; }
.wx-progress-seg > i {
  flex: 1; height: 3px; background: var(--wx-sunken); border-radius: var(--wx-r-full);
}
.wx-progress-seg > i[data-on="true"] { background: var(--wx-text); }

/* ==========================================================================
   12 · FACETS — filter panel with live counts (reference J2)
   ========================================================================== */
.wx-facet { display: flex; flex-direction: column; gap: var(--wx-5); }
.wx-facet__group { display: flex; flex-direction: column; gap: var(--wx-1); }
.wx-facet__title {
  font-size: var(--wx-t-xs); font-weight: 600; color: var(--wx-text-3);
  margin-bottom: var(--wx-2);
}
.wx-facet__opt {
  display: flex; align-items: center; gap: var(--wx-2);
  padding: 5px var(--wx-2); border-radius: var(--wx-r-sm);
  font-size: var(--wx-t-sm); cursor: pointer;
}
.wx-facet__opt:hover { background: var(--wx-raise); }
.wx-facet__opt input { accent-color: var(--wx-brand); }
.wx-facet__n {
  margin-left: auto;   font-size: var(--wx-t-xs); color: var(--wx-text-3);
}

/* ==========================================================================
   13 · MODALS
   ========================================================================== */
.wx-scrim {
  position: fixed; inset: 0; z-index: 90;
  background: var(--wx-scrim);
  display: flex; align-items: center; justify-content: center;
  padding: var(--wx-6);
  animation: wx-fade var(--wx-d-2) var(--wx-ease);
}
.wx-modal {
  background: var(--wx-surface); border-radius: var(--wx-r);
  box-shadow: var(--wx-e3);
  width: min(920px, 100%); max-height: 88vh; overflow: auto;
  animation: wx-rise var(--wx-d-3) var(--wx-ease);
}
.wx-modal__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--wx-3);
  padding: var(--wx-5); border-bottom: 1px solid var(--wx-line);
  position: sticky; top: 0; background: var(--wx-surface); z-index: 1;
}
.wx-modal__body { padding: var(--wx-5); }
.wx-modal__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--wx-3);
  padding: var(--wx-5); border-top: 1px solid var(--wx-line);
  position: sticky; bottom: 0; background: var(--wx-surface);
}

/* Split modal — contextual education left, form right. The best pattern in
   the decoded reference (J6): a Partner selling an unfamiliar product gets the
   pitch and the form in one place. */
.wx-modal--split { display: grid; grid-template-columns: 340px 1fr; width: min(1040px, 100%); }
.wx-modal__aside {
  background: var(--wx-brand); color: var(--wx-on-brand);
  padding: var(--wx-7); display: flex; flex-direction: column; justify-content: space-between;
}
.wx-modal__aside .wx-title { color: var(--wx-on-brand); }
.wx-modal__aside p { color: var(--wx-on-brand-2); font-size: var(--wx-t-sm); }
@media (max-width: 860px) {
  .wx-modal--split { grid-template-columns: 1fr; }
  .wx-modal__aside { display: none; }
}

/* ==========================================================================
   14 · SHELL — rendered by wx-shell.js on every screen
   ========================================================================== */
.wx-app { display: grid; grid-template-columns: var(--wx-rail) 1fr; min-height: 100vh; }

.wx-rail {
  position: sticky; top: 0; height: 100vh;
  display: flex; flex-direction: column; align-items: center; gap: var(--wx-1);
  padding: var(--wx-4) 0;
  background: var(--wx-surface); border-right: 1px solid var(--wx-line);
}
.wx-rail__logo {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; margin-bottom: var(--wx-4);
  background: var(--wx-brand); color: var(--wx-on-brand);
  border-radius: var(--wx-r); font-weight: 700; font-size: var(--wx-t-sm);
}
.wx-rail__item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 56px; padding: var(--wx-2) 0;
  border-radius: var(--wx-r); border: 0; background: none;
  color: var(--wx-text-3); font-size: 10px; cursor: pointer;
  transition: background var(--wx-d-1) var(--wx-ease), color var(--wx-d-1) var(--wx-ease);
}
.wx-rail__item:hover { background: var(--wx-raise); color: var(--wx-text-2); }
.wx-rail__item[aria-current="page"] { background: var(--wx-brand-wash); color: var(--wx-text); font-weight: 500; }
.wx-rail__item svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.6; }
.wx-rail__badge {
  position: absolute; top: 2px; right: 8px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--wx-neg); color: var(--wx-on-accent); border-radius: var(--wx-r-full);
  font-size: 9px; line-height: 16px; text-align: center;
}

.wx-main { min-width: 0; display: flex; flex-direction: column; }
.wx-topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: var(--wx-4);
  height: 56px; padding: 0 var(--wx-6);
  background: var(--wx-topbar);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--wx-line);
}
.wx-crumb { display: flex; align-items: center; gap: var(--wx-2); font-size: var(--wx-t-sm); color: var(--wx-text-3); }
.wx-crumb b { color: var(--wx-text); font-weight: 500; }

/* Client context header — present whenever ?client= is in the URL. This is
   how context survives a full page load without any storage. */
.wx-ctx {
  display: flex; align-items: center; gap: var(--wx-4);
  padding: var(--wx-4) var(--wx-6);
  background: var(--wx-surface); border-bottom: 1px solid var(--wx-line);
}
.wx-avatar--sm { width: 24px; height: 24px; font-size: var(--wx-t-xs); }
.wx-avatar {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  background: var(--wx-sunken); border-radius: 50%;
  font-size: var(--wx-t-sm); font-weight: 600; color: var(--wx-text-2);
}
.wx-page { padding: var(--wx-6); flex: 1; }
.wx-page__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--wx-4); margin-bottom: var(--wx-5); }

@media (max-width: 780px) {
  .wx-app { grid-template-columns: 1fr; }
  .wx-rail {
    position: fixed; bottom: 0; top: auto; left: 0; right: 0; z-index: 40;
    height: auto; flex-direction: row; justify-content: space-around;
    padding: var(--wx-2) 0; border-right: 0; border-top: 1px solid var(--wx-line);
  }
  .wx-rail__logo { display: none; }
  .wx-page { padding: var(--wx-4) var(--wx-4) 76px; }
  .wx-topbar { padding-inline: var(--wx-4); }
}

/* ==========================================================================
   15 · STATES — every error diagnoses, explains, AND offers recovery.
   The third obligation is the one that goes missing. L7 S-A8.
   ========================================================================== */
.wx-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--wx-3);
  padding: var(--wx-9) var(--wx-5); text-align: center; color: var(--wx-text-3);
}
.wx-error {
  display: flex; gap: var(--wx-3);
  padding: var(--wx-4);
  background: var(--wx-neg-wash); border: 1px solid var(--wx-neg);
  border-radius: var(--wx-r); font-size: var(--wx-t-sm);
}
.wx-error__body { display: flex; flex-direction: column; gap: var(--wx-2); }
.wx-error__what { font-weight: 600; color: var(--wx-neg); }
.wx-error__why  { color: var(--wx-text-2); }
.wx-error__fix  { margin-top: var(--wx-1); }

.wx-note {
  padding: var(--wx-4); border-radius: var(--wx-r);
  background: var(--wx-warn-wash); border: 1px solid var(--wx-warn);
  font-size: var(--wx-t-sm); color: var(--wx-text-2);
}

/* ==========================================================================
   16 · MOTION — three primitives. Nothing that contains a figure moves.
   ========================================================================== */
@keyframes wx-fade { from { opacity: 0; } }
@keyframes wx-rise { from { opacity: 0; transform: translateY(8px); } }

[data-wx-reveal] {
  opacity: 0; transform: translateY(6px);
  transition: opacity var(--wx-d-3) var(--wx-ease) var(--wx-delay, 0ms),
              transform var(--wx-d-3) var(--wx-ease) var(--wx-delay, 0ms);
}
[data-wx-reveal].is-in { opacity: 1; transform: none; }

/* Continuity: the shell never animates. Only the page body reveals. */
.wx-rail, .wx-topbar, .wx-ctx { animation: none !important; }

/* ===========================================================================
   17 · C-4c CINEMATIC CHOREOGRAPHY — motion describes hierarchy and state.
   It adds no decorative surface: no glow, gradient, grain, or ambient layer.
   =========================================================================== */
:root {
  --wx-d-4: 480ms;
  --wx-d-5: 640ms;
}

.wx-motion-row { will-change: transform, opacity; }

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


/* ==========================================================================
   18 · MATERIAL — the premium pass

   The dark surfaces were flat because contrast RATIO is the wrong instrument
   for material separation: at these luminances it compresses, and deepening
   the canvas moved canvas/surface only 1.063 -> 1.074, which the eye cannot
   see. What reads as premium is the hairline where a raised surface catches
   light, plus interaction that answers immediately.

   This is not the ornament layer C-4 deleted. There is no glow, no gradient,
   no atmosphere — one 1px inset line, and it is transparent in light, where
   a shadow already does that job.
   ========================================================================== */
.wx-card, .wx-panel, .wx-accord, .wx-modal, .wx-inset, .wx-nba {
  box-shadow: inset 0 1px 0 0 var(--wx-edge);
}
/* A surface that already carries a shadow keeps both: the shadow puts it above
   the page, the line describes its top edge. */
.wx-accord { box-shadow: inset 0 1px 0 0 var(--wx-edge), var(--wx-e2); }
.wx-modal  { box-shadow: inset 0 1px 0 0 var(--wx-edge), var(--wx-e3); }

/* -- interaction: every control answers within one frame budget ------------
   Hover moves the SURFACE, never the geometry — a card that lifts on hover
   reflows its neighbours and turns one intent into two animations. */
.wx-card--hover { transition: border-color var(--wx-d-1) var(--wx-ease),
                              background   var(--wx-d-1) var(--wx-ease); }
.wx-card--hover:hover { border-color: var(--wx-line-strong); background: var(--wx-raise); }

.wx-table tbody tr:hover { background: var(--wx-raise); }
/* The row a Partner is about to click gets its left edge marked, so the target
   is unambiguous in a table of 80 near-identical rows. */
.wx-table tbody tr { box-shadow: inset 2px 0 0 -2px transparent;
                     transition: background var(--wx-d-1) var(--wx-ease),
                                 box-shadow var(--wx-d-1) var(--wx-ease); }
.wx-table tbody tr:hover { box-shadow: inset 2px 0 0 0 var(--wx-line-strong); }

/* Press is a scale, not a colour change: colour is how this product says
   gain, loss and provenance, and press must not borrow that vocabulary. */
.wx-btn:active, .wx-seg button:active, .wx-facet__opt:active { transform: scale(.985); }
.wx-rail__item { transition: background var(--wx-d-1) var(--wx-ease),
                             color      var(--wx-d-1) var(--wx-ease),
                             box-shadow var(--wx-d-2) var(--wx-ease); }
/* The current rail item is marked on its inner edge — a 2px rule that reads at
   a glance without needing a fill that competes with the badge. */
.wx-rail__item[aria-current="page"] { box-shadow: inset 2px 0 0 0 var(--wx-text); }
@media (max-width: 780px) {
  .wx-rail__item[aria-current="page"] { box-shadow: inset 0 2px 0 0 var(--wx-text); }
}

/* Chips and marks settle rather than snap when their state changes. */
.wx-chip, .wx-prov, .wx-tier { transition: background var(--wx-d-1) var(--wx-ease),
                                           color      var(--wx-d-1) var(--wx-ease); }


/* ==========================================================================
   19 · MOBILE — a parallel journey, not a narrower desktop

   Selected by [data-device="mobile"], which wx-shell.js sets from ?device= or
   from viewport width. Keyed on the ATTRIBUTE rather than a media query alone
   so the phone journey can be opened and reviewed on a desktop screen. A
   layout you can only see by dragging a window edge is a layout nobody checks.

   The premise: a Partner at a desk is BUILDING — proposals, comparisons,
   reconciliation. A Partner on a phone is TRIAGING, usually between meetings.
   Same data, different job. So the phone gets card lists instead of tables,
   sheets instead of centred dialogs, and one wizard step at a time.
   ========================================================================== */
[data-device="mobile"] .wx-app { grid-template-columns: 1fr; }

/* -- the tab bar. Thumb-reachable, five items, safe-area aware. ----------- */
[data-device="mobile"] .wx-rail {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  height: auto; flex-direction: row; justify-content: space-around;
  padding: var(--wx-2) 0 calc(var(--wx-2) + env(safe-area-inset-bottom, 0px));
  border-right: 0; border-top: 1px solid var(--wx-line);
  background: var(--wx-surface);
}
[data-device="mobile"] .wx-rail__logo { display: none; }
/* Six rail items plus two utilities does not fit a thumb bar. Coach and Trust
   are reachable from the screens that need them; the bar carries the five a
   Partner moves between. */
[data-device="mobile"] .wx-rail__item:nth-of-type(n+7) { display: none; }
[data-device="mobile"] .wx-rail__item { width: auto; flex: 1; min-height: 48px; }
[data-device="mobile"] .wx-rail__item[aria-current="page"] {
  box-shadow: inset 0 2px 0 0 var(--wx-text); background: transparent;
}
[data-device="mobile"] .wx-page { padding: var(--wx-4) var(--wx-4) calc(88px + env(safe-area-inset-bottom, 0px)); }
[data-device="mobile"] .wx-topbar { padding-inline: var(--wx-4); }

/* -- one column. Every desktop split collapses. -------------------------- */
[data-device="mobile"] .wx-grid--sidebar,
[data-device="mobile"] .wx-grid--halves,
[data-device="mobile"] .wx-grid--thirds,
[data-device="mobile"] .wx-grid { grid-template-columns: minmax(0, 1fr) !important; }
[data-device="mobile"] .wx-kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
[data-device="mobile"] .wx-page__head { flex-direction: column; align-items: stretch; }
[data-device="mobile"] .wx-page__head .wx-row { flex-wrap: wrap; }

/* -- touch targets. 44px is the floor, not the aspiration. --------------- */
[data-device="mobile"] .wx-btn { min-height: 44px; padding-inline: var(--wx-5); }
[data-device="mobile"] .wx-btn--sm { min-height: 40px; }
[data-device="mobile"] .wx-seg button { min-height: 40px; }
[data-device="mobile"] .wx-facet__opt { min-height: 44px; }
[data-device="mobile"] .wx-input { min-height: 44px; font-size: 16px; }  /* 16px stops iOS zooming on focus */

/* -- tables become card lists. Built by wx-mobile.js from the table itself,
      so no screen has to carry a second markup path. --------------------- */
[data-device="mobile"] .wx-table-scroll { overflow: visible; }
[data-device="mobile"] table.wx-table { display: none; }
.wx-cardlist { display: flex; flex-direction: column; }
[data-device="desktop"] .wx-cardlist { display: none; }
.wx-cardlist__item {
  padding: var(--wx-4) var(--wx-5);
  border-top: 1px solid var(--wx-line);
}
.wx-cardlist__item:first-child { border-top: 0; }
.wx-cardlist__lead { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--wx-3); }
.wx-cardlist__rest { display: flex; flex-wrap: wrap; gap: var(--wx-1) var(--wx-4); margin-block-start: var(--wx-2); }
.wx-cardlist__pair { display: flex; gap: var(--wx-1); font-size: var(--wx-t-xs); line-height: var(--wx-lh-xs); }
.wx-cardlist__k { color: var(--wx-text-3); }
.wx-cardlist__v { color: var(--wx-text-2); }

/* -- dialogs become sheets. A centred box on a phone wastes the edges and
      puts the primary action under the thumb of the other hand. ---------- */
[data-device="mobile"] .wx-scrim { align-items: flex-end; padding: 0; }
[data-device="mobile"] .wx-modal {
  width: 100%; max-height: 92vh;
  border-radius: var(--wx-r) var(--wx-r) 0 0;
  animation: wx-sheet var(--wx-d-3) var(--wx-ease);
}
[data-device="mobile"] .wx-modal--split { grid-template-columns: 1fr; }
[data-device="mobile"] .wx-modal__aside { display: none; }
[data-device="mobile"] .wx-modal__foot {
  padding-block-end: calc(var(--wx-5) + env(safe-area-inset-bottom, 0px));
}
/* The grab rail. A sheet that looks draggable is a sheet people try to close
   by dragging, so it gets the affordance even though the gesture is native. */
[data-device="mobile"] .wx-modal__head::before {
  content: ''; position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  width: 36px; height: 4px; border-radius: var(--wx-r-full); background: var(--wx-line-strong);
}
[data-device="mobile"] .wx-modal__head { padding-block-start: var(--wx-6); }
@keyframes wx-sheet { from { transform: translateY(16px); opacity: 0; } }

/* -- the wizard: one step at a time, progress above it ------------------- */
[data-device="mobile"] .wx-stepper { flex-direction: row; gap: var(--wx-1); overflow: hidden; }
[data-device="mobile"] .wx-stepper__item { flex: 1; flex-direction: column; gap: var(--wx-1);
                                           padding: var(--wx-2) var(--wx-1); text-align: center; }
[data-device="mobile"] .wx-stepper__item > span:last-child { display: none; }
[data-device="mobile"] .wx-stepper__item[aria-current="step"] > span:last-child {
  display: block; font-size: var(--wx-t-xs); line-height: var(--wx-lh-xs);
}

/* -- the primary action stays reachable ---------------------------------- */
.wx-sticky-action {
  position: sticky; bottom: 0; z-index: 30;
  display: none; gap: var(--wx-2);
  padding: var(--wx-3) var(--wx-4) calc(var(--wx-3) + env(safe-area-inset-bottom, 0px));
  background: var(--wx-surface); border-top: 1px solid var(--wx-line);
  margin-inline: calc(var(--wx-4) * -1);
}
[data-device="mobile"] .wx-sticky-action { display: flex; }
[data-device="mobile"] .wx-sticky-action .wx-btn { flex: 1; }

/* The topbar carries four things on desktop: network, breadcrumb, ARN, as-of.
   On a 390px screen that wraps to three lines inside a 56px bar. The ARN is
   reference data a Partner already knows; the as-of stamp is not — it governs
   every figure below it, so that is the one that stays. */
[data-device="mobile"] .wx-topbar { gap: var(--wx-2); }
[data-device="mobile"] .wx-crumb { min-width: 0; }
[data-device="mobile"] .wx-crumb > span:first-child,
[data-device="mobile"] .wx-crumb > span:nth-child(2) { display: none; }
[data-device="mobile"] .wx-crumb b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
[data-device="mobile"] .wx-topbar > .wx-caption { display: none; }   /* the ARN */

/* The client context header stacks rather than wraps mid-phrase. */
[data-device="mobile"] .wx-ctx { flex-wrap: wrap; padding: var(--wx-3) var(--wx-4); }
[data-device="mobile"] .wx-ctx nav { width: 100%; overflow-x: auto; }
[data-device="mobile"] .wx-ctx nav .wx-btn { flex: none; }

/* A flex row that cannot wrap is the single biggest source of horizontal
   overflow on a phone: orders.html put three form fields in one .wx-row and
   the row insisted on 449px inside a 390px viewport. Rows wrap, fields go full
   width, and the toolbars that must stay on one line scroll instead. */
[data-device="mobile"] .wx-row,
[data-device="mobile"] .wx-row-b,
[data-device="mobile"] .wx-legend { flex-wrap: wrap; }
[data-device="mobile"] .wx-field { flex: 1 1 100% !important; min-width: 0; }
[data-device="mobile"] .wx-input,
[data-device="mobile"] select.wx-input,
[data-device="mobile"] textarea.wx-input { width: 100% !important; max-width: 100%; }
[data-device="mobile"] .wx-tabs { overflow-x: auto; flex-wrap: nowrap; }
[data-device="mobile"] .wx-seg  { overflow-x: auto; max-width: 100%; }
/* Any grid still holding a fixed track from an inline style collapses. */
[data-device="mobile"] [style*="grid-template-columns"] { grid-template-columns: minmax(0, 1fr) !important; }
/* Last resort: nothing may push the document sideways. */
[data-device="mobile"] #wx-content { overflow-x: hidden; }
[data-device="mobile"] .wx-page { min-width: 0; }

/* A grid or flex child defaults to min-width:auto, which is its INTRINSIC
   width — so one long unbreakable string inside a 1fr track widens the whole
   document and no single element measures over the edge. That is why coach.html
   scrolled 55px sideways with nothing visibly sticking out. Letting tracks
   shrink is the fix; the panels already scroll their own overflow. */
.wx-grid > *, .wx-col > *, .wx-row > * { min-width: 0; }
.wx-panel__body, .wx-card { min-width: 0; }


/* ==========================================================================
   20 · APP FRAME — the mobile journey, reviewed on a desktop

   ?device=mobile on a wide window is the review case: it exists so the phone
   journey can be looked at without dragging a browser edge. Left as-is it
   pinned the app to the left margin and stretched the fixed tab bar across
   1900px of empty page — which reads as broken rather than as a phone.

   So above the breakpoint the app becomes a CENTRED DEVICE: a phone-width
   column on a recessed backdrop, with the tab bar bounded to the column
   instead of the window. Below the breakpoint — an actual phone — none of
   this applies and the app is full-bleed, because a real device does not
   need a picture of a device around it.
   ========================================================================== */
@media (min-width: 781px) {
  [data-device="mobile"] {
    --wx-frame: 420px;
    background: var(--wx-sunken);
  }
  [data-device="mobile"] body {
    position: relative;                   /* the tab bar anchors to THIS */
    max-width: var(--wx-frame);
    margin-inline: auto;
    background: var(--wx-canvas);
    /* The seam where the device meets the page it sits on. */
    box-shadow: 0 0 0 1px var(--wx-line), var(--wx-e3);
  }
  /* NATIVE SCROLL. The window must not scroll — the DEVICE scrolls. Letting
     the document scroll made the whole frame slide up the page, taking the
     topbar and tab bar with it, which is a web page pretending to be an app.
     Here the frame is exactly one viewport tall, the chrome is pinned to its
     top and bottom edges, and only the content between them moves. */
  [data-device="mobile"],
  [data-device="mobile"] body { height: 100vh; overflow: hidden; }
  [data-device="mobile"] .wx-app  { height: 100vh; overflow: hidden; }
  [data-device="mobile"] .wx-main { height: 100vh; min-height: 0; overflow: hidden;
                                    display: flex; flex-direction: column; }
  [data-device="mobile"] .wx-topbar { position: static; flex: none; max-width: none; }
  [data-device="mobile"] .wx-ctx    { flex: none; }
  [data-device="mobile"] .wx-page {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto; overflow-x: hidden;
    /* A scroll that reaches its end must not hand the gesture to the page
       behind it — that is the tell that you are inside a web page. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    /* The tab bar is out of flow, so the scroller has to reserve its full
       height or the last row sits under it — which is the clipped-content
       tell. 64px bar + 8px breathing room. */
    padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
  }

  /* The tab bar sits on the frame, not on the window. */
  [data-device="mobile"] .wx-rail {
    position: absolute; left: 50%; right: auto; bottom: 0;
    width: var(--wx-frame);
    transform: translateX(-50%);
    border-inline: 1px solid var(--wx-line);
  }
  [data-device="mobile"] .wx-scrim {
    position: absolute; left: 50%; right: auto; width: var(--wx-frame);
    transform: translateX(-50%);
  }
}


/* ==========================================================================
   21 · APP SHELL — the tab bar occupies space instead of covering content

   Reserving the bar's height with padding-bottom got most of the way there and
   still clipped 8 screens: any screen with its own inner scroller, sticky
   element or facet panel measured its own bottom, not the page's, and slid
   under the bar anyway. Padding is a guess about someone else's layout.

   So the shell becomes a flex column exactly one viewport tall: chrome at the
   top, chrome at the bottom, and one scrolling region between them. The bar
   takes real space, so nothing can be underneath it — the same structure a
   native app uses, and for the same reason.
   ========================================================================== */
[data-device="mobile"] .wx-app {
  display: flex; flex-direction: column;
  height: 100vh; height: 100dvh;      /* dvh so an address bar cannot crop it */
  min-height: 0; overflow: hidden;
}
[data-device="mobile"] .wx-main {
  order: 1; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
}
[data-device="mobile"] .wx-topbar,
[data-device="mobile"] .wx-ctx { position: static; flex: none; }
[data-device="mobile"] .wx-page {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: var(--wx-4) var(--wx-4) var(--wx-6);   /* no reservation needed now */
}
/* In flow, last, full width — not fixed, not absolute, not overlaying. */
[data-device="mobile"] .wx-rail {
  position: static; order: 2; flex: none;
  width: 100%; transform: none; inset: auto;
  border-inline: 0; border-top: 1px solid var(--wx-line);
  padding-bottom: calc(var(--wx-2) + env(safe-area-inset-bottom, 0px));
}
/* A sheet still needs to cover the frame, so it stays out of flow. */
[data-device="mobile"] .wx-scrim { position: fixed; inset: 0; transform: none; width: auto; }

@media (min-width: 781px) {
  [data-device="mobile"] .wx-scrim {
    position: absolute; inset: 0; left: 50%; right: auto;
    width: var(--wx-frame); transform: translateX(-50%);
  }
}

/* A suggestion is a sentence, not a verb. .wx-btn is nowrap by design — right
   for "Approve", wrong for "How is an equity fund taxed if a client sells after
   14 months?", which could not wrap and pushed the Coach rail 55px past the
   viewport. Suggestions wrap, left-align, and grow. */
.wx-btn--wrap {
  white-space: normal;
  text-align: left;
  height: auto;
  min-height: 32px;
  padding-block: var(--wx-2);
  line-height: var(--wx-lh-base);
}

/* ==========================================================================
   22 · AUTH — sign-in and landing, which have no rail

   Two screens sit outside the console shell: you cannot navigate a product you
   have not signed into. They opt out with data-wx-chrome="none", which
   wx-shell.js honours instead of rendering a rail nobody can use yet.
   ========================================================================== */
.wx-auth {
  display: grid; grid-template-columns: minmax(0, 1fr) 380px;
  gap: var(--wx-8); align-items: start;
  max-width: 1040px; margin-inline: auto;
  padding: var(--wx-10) var(--wx-6);
}
.wx-auth__panel {
  background: var(--wx-surface); border: 1px solid var(--wx-line);
  border-radius: var(--wx-r); padding: var(--wx-7);
  box-shadow: inset 0 1px 0 0 var(--wx-edge), var(--wx-e2);
}
.wx-auth__aside { padding-block: var(--wx-2); }
.wx-auth__mark {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-block-end: var(--wx-4);
  background: var(--wx-brand); color: var(--wx-on-brand);
  border-radius: var(--wx-r); font-weight: 700; font-size: var(--wx-t-md);
}
/* A person is a row, not a chip: name, role, and room for the tap. */
.wx-auth__who {
  display: flex; align-items: center; gap: var(--wx-3);
  width: 100%; height: auto; min-height: 56px;
  padding: var(--wx-3) var(--wx-4); text-align: left;
}
.wx-auth__whotext { display: flex; flex-direction: column; gap: 2px; }

@media (max-width: 900px) { .wx-auth { grid-template-columns: minmax(0, 1fr); gap: var(--wx-6); } }
[data-device="mobile"] .wx-auth { grid-template-columns: minmax(0, 1fr); padding: var(--wx-5) var(--wx-4); }
[data-device="mobile"] .wx-auth__panel { padding: var(--wx-5); }

/* No chrome: the page owns the whole frame. */
[data-wx-chrome="none"] .wx-page { padding: 0; }

/* -- landing. The one screen whose job is persuasion, so it gets the only
      display type in the product — and even that is Inter, not a second face. */
.wx-lander__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--wx-4);
  padding: var(--wx-4) var(--wx-6);
  border-bottom: 1px solid var(--wx-line); background: var(--wx-surface);
}
.wx-lander__hero { max-width: 760px; padding: var(--wx-10) var(--wx-6) var(--wx-8); }
.wx-lander__h1 {
  font-family: var(--wx-font-display);
  font-size: var(--wx-t-d2); line-height: var(--wx-lh-d2); font-weight: 500;
  letter-spacing: -.022em;
  color: var(--wx-text); margin: 0;
}
.wx-lander__sub {
  font-size: var(--wx-t-md); line-height: 24px; color: var(--wx-text-2); max-width: 60ch;
}
.wx-lander__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--wx-4); padding-inline: var(--wx-6); margin-block-end: var(--wx-8);
}
.wx-lander__grid--two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.wx-lander__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--wx-4);
  padding: var(--wx-6); border-top: 1px solid var(--wx-line); margin-block-start: var(--wx-8);
}
[data-wx-chrome="none"] .wx-panel,
[data-wx-chrome="none"] .wx-note { margin-inline: var(--wx-6); margin-block-end: var(--wx-8); }
[data-device="mobile"] .wx-lander__h1 { font-size: var(--wx-t-d1); line-height: var(--wx-lh-d1); }
[data-device="mobile"] .wx-lander__hero { padding: var(--wx-6) var(--wx-4) var(--wx-5); }
[data-device="mobile"] .wx-lander__grid,
[data-device="mobile"] .wx-lander__bar,
[data-device="mobile"] .wx-lander__foot { padding-inline: var(--wx-4); }
[data-device="mobile"] .wx-lander__foot { flex-direction: column; align-items: stretch; }
[data-device="mobile"][data-wx-chrome] .wx-panel,
[data-device="mobile"][data-wx-chrome] .wx-note { margin-inline: var(--wx-4); }

/* -- landing acts. Pinned, scrubbed scroll sections; wx-lander.js only.
      The pin is `position: sticky` — the browser's own, on the compositor.
      No JS touches layout, so there is no scroll jank to engineer around.

      --wx-act-len is how many viewport-heights of scroll the act consumes.
      More beats want more length, or the cuts land on top of each other. */
.wx-act { position: relative; min-height: calc(var(--wx-act-len, 2.4) * 100vh); }
.wx-act__stage {
  position: sticky; top: 0; min-height: 100vh;
  display: grid; place-items: center; padding: var(--wx-8) var(--wx-6);
}
/* Beats share one grid cell, so a `sequence` act cross-fades in place rather
   than reflowing. `accumulate` acts opt out and lay their beats out normally. */
.wx-act__reel { display: grid; width: 100%; max-width: 880px; }
.wx-act__reel > [data-wx-beat] { grid-area: 1 / 1; }
.wx-act__reel--stack {
  display: flex; flex-direction: column; gap: var(--wx-2);
}
.wx-act__reel--stack > [data-wx-beat] { grid-area: auto; }

/* An accumulating act stacks its beats, so each one must stay short enough
   that the whole stage still fits a viewport — a stage taller than the screen
   cannot be pinned, and its last beats would play off-screen. A capability is
   therefore a ROW, not a card: identity, claim and figure on one line. */
.wx-act__cap {
  display: grid; grid-template-columns: 104px minmax(0, 1fr) auto;
  align-items: center; gap: var(--wx-4);
  padding: var(--wx-3) var(--wx-4);
  border: 1px solid var(--wx-line); border-radius: var(--wx-r-sm);
  background: var(--wx-surface); color: inherit; text-decoration: none;
  transition: border-color var(--wx-d-1) var(--wx-ease);
}
.wx-act__cap:hover { border-color: var(--wx-line-strong); }
.wx-act__cap-verb { font-size: var(--wx-t-base); font-weight: 600; color: var(--wx-text); }
.wx-act__cap-body { font-size: var(--wx-t-sm); line-height: 20px; color: var(--wx-text-2); }
.wx-act__cap-fig  { text-align: right; }
.wx-act__label {
  font-size: var(--wx-t-xs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--wx-text-3); margin-block-end: var(--wx-3);
}
.wx-act__line {
  font-family: var(--wx-font-display);
  font-size: var(--wx-t-d1); line-height: var(--wx-lh-d1); font-weight: 500;
  letter-spacing: -.018em;
  color: var(--wx-text); margin: 0 0 var(--wx-3);
}
.wx-act__body { font-size: var(--wx-t-md); line-height: 24px; color: var(--wx-text-2); max-width: 56ch; }

/* Engine off — reduced motion, or no GSAP. The page must still say everything.
   Acts collapse to content height, the pin releases, every beat is visible. */
[data-wx-acts="static"] .wx-act { min-height: 0; }
[data-wx-acts="static"] .wx-act__stage { position: static; min-height: 0; padding-block: var(--wx-7); }
[data-wx-acts="static"] .wx-act__reel { display: flex; flex-direction: column; gap: var(--wx-5); }
[data-wx-acts="static"] .wx-act__reel > [data-wx-beat] { grid-area: auto; opacity: 1; visibility: visible; }

[data-device="mobile"] .wx-act { min-height: calc(var(--wx-act-len, 2.4) * 88vh); }
[data-device="mobile"] .wx-act__stage { padding: var(--wx-6) var(--wx-4); }
[data-device="mobile"] .wx-act__line { font-size: var(--wx-t-xl); line-height: var(--wx-lh-xl); }
/* One column on mobile: 104px of chip beside a figure leaves nothing for the
   claim, and the row would wrap into something taller than the card it replaced. */
[data-device="mobile"] .wx-act__cap { grid-template-columns: 1fr; gap: var(--wx-2); }
[data-device="mobile"] .wx-act__cap-fig { text-align: left; }
[data-device="mobile"] .wx-act__cap-body { display: none; }

/* ==========================================================================
   19 · AMBIENT LAYER — authorised by C-4d, spec'd in L7 s9

   C-4 deleted the decorative layer; C-4d reinstates exactly one, governed by
   wx-webgl.js. The six deleted CSS ornament classes are NOT reinstated and
   their CI gate still stands — this is a different mechanism, not their
   return by another name.

   Everything here is posture, not appearance. The canvas paints nothing on
   its own; wx-webgl.js owns the pixels. These rules exist so a screen cannot
   ship an ambient layer that sits in front of a figure, steals a click, or
   survives a reduced-motion preference.
   ========================================================================== */
[data-wx-webgl] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Behind every figure and control, always. Content sits at z-index 0 or
     above in a positioned ancestor; decoration never competes with it. */
  z-index: -1;
  pointer-events: none;
  /* `contain: strict` was tried here and removed: size containment collapses
     the intrinsic box, and since wx-webgl.js derives the drawing buffer from
     clientWidth, the canvas rendered at 633px inside a 1440px hero. Paint
     containment alone gives the isolation without touching layout. */
  contain: paint;
}

/* The host establishes the containing block. Without this the canvas escapes
   to the viewport and paints over the whole document. */
[data-wx-webgl-host] { position: relative; isolation: isolate; }

/* Reduced motion: wx-webgl.js removes the node entirely rather than hiding
   it — an empty canvas still costs a compositor layer. This rule is the
   belt-and-braces for the frame before the script runs, and for the case
   where scripting is unavailable. */
@media (prefers-reduced-motion: reduce) {
  [data-wx-webgl] { display: none !important; }
}

/* Full-bleed host for the landing ambient layer. .wx-lander__hero is a 760px
   text column; the ambient field belongs to the page, not to the column. */
.wx-lander__bleed { position: relative; isolation: isolate; width: 100%; }

/* ==========================================================================
   20 · CORPUS PRIMITIVES

   Added with the Screener. Every one of these existed as an invented class
   on that screen first and rendered unstyled — which is the argument for
   putting them here rather than leaving each screen to improvise: an
   invented class does not fail, it just looks broken.
   ========================================================================== */

/* "Not reported" — the em-dash that stands in for an absent figure. The
   corpus encodes absent as 0 to stay compact, so this mark is what keeps a
   missing 3-year history from being rendered as a 0.00% return. It must read
   as quieter than any real number, never as a value. */
.wx-muted { color: var(--wx-text-3); }

/* A qualifier on a Product: research-only, new-fund monitoring, suspended.
   Deliberately not a provenance mark — those are reserved for computed vs
   generated and may not take on a third meaning. */
.wx-tag {
  display: inline-block; margin-inline-start: var(--wx-2);
  padding: 1px var(--wx-2);
  font-size: var(--wx-t-xs); line-height: 16px; font-weight: 500;
  color: var(--wx-text-2); background: var(--wx-raise);
  border: 1px solid var(--wx-line); border-radius: 3px;
  vertical-align: 1px; white-space: nowrap;
}

/* Selection control inside a dense table. Sized to the row, not to the
   browser default, which is 13px and swims in a 40px row. */
.wx-check { accent-color: var(--wx-brand); width: 15px; height: 15px; cursor: pointer; }
.wx-check:disabled { cursor: not-allowed; opacity: .4; }

.wx-input--sm { padding: var(--wx-1) var(--wx-2); font-size: var(--wx-t-sm); }

/* A Product name is long — "Aditya Birla Sun Life Nifty Smallcap 50 Index
   Fund-Reg(G)" is not unusual. Two lines, then ellipsis: three-line rows
   destroy the scannability that makes a dense table worth having. */
.wx-table td .wx-cellclamp {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A facet option with no matches under the current filter state. Shown, not
   hidden: removing it is what turns a filter into a dead end. */
.wx-facet__opt.is-empty { opacity: .42; cursor: not-allowed; }
.wx-facet__opt.is-empty:hover { background: none; }

/* Best value in a comparison row. Marked ONLY on rows that declare a
   direction — "TER, lower is better" has one; "large-cap %" does not, and
   marking a winner there would assert a judgement the data cannot support.
   A wash rather than a colour: this is emphasis, not a semantic gain/loss,
   and it must not be mistaken for one on a screen full of returns. */
.wx-table td.is-best { background: var(--wx-brand-wash); font-weight: 500; }
