/* ============================================================
 * JARGAN SEMANTIC TOKEN LAYER — Single Source of Truth
 * BU-373: Collapse competing token/theme files into ONE look-agnostic layer
 * ------------------------------------------------------------
 * RULES:
 *   - Token NAMES are semantic, never look-named
 *     --color-canvas (NOT --paper / --ed-canvas / --newsprint)
 *     --radius-md    (NOT --rounded / --sharp)
 *     --font-display (NOT --serif / --playfair)
 *   - :root is the CONTROL theme = the usable production Blazor admin look
 *     (sans / rounded / soft-shadow / Traffic Orange). The editorial Newsprint
 *     look is now the swappable variant, applied via class jgn-variant-b.
 *   - A throwaway variant is ONE added CSS layer of token value
 *     overrides — zero markup edits required
 *   - This file replaces:
 *       jargan-tokens.css  (ed-* editorial tokens, look-named)
 *       tokens.css         (color-brand and neutral tokens, mixed)
 *       styles-editorial.css :root block (duplicate overrides)
 *       themes-canonical.css :root block (--body-bg etc)
 *       themes.css :root block (glass-morphism vars)
 * ============================================================ */

/* ---- Google Fonts ---- */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Lora:ital,wght@0,400;0,500;0,600;1,400;1,600&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
  /* ---- Palette (semantic roles, not colour names) ---- */
  --color-canvas:           #f8f8f8;   /* page background */
  --color-surface:          #ffffff;   /* raised card/panel surface */
  --color-ink:              #1a1a1a;   /* primary text */
  --color-ink-dim:          #4a4a4a;   /* secondary text (neutral gray) */
  --color-ink-muted:        #6b6b6b;   /* tertiary / meta text (5.3:1 on #F9F9F7) */
  /* blue-gray text ramp (the app's dominant secondary/tertiary text hue — distinct
     from the neutral ink-dim/muted; exact dominant defaults so the un-skinned app is
     identical, theme blocks flip them light on dark vibes). PM-010 text sweep. */
  --color-ink-secondary:    #6b7280;   /* blue-gray secondary (Tailwind gray-500) */
  --color-ink-tertiary:     #9ca3af;   /* blue-gray tertiary / placeholder (gray-400) */
  /* text that lives on ALWAYS-DARK chrome (app header, dark modals, inverted strips)
     — must STAY light on every theme. Do NOT route this to ink-* (those flip dark on
     light vibes). */
  --color-invert-ink-dim:   #c7ccd3;   /* light-gray label on dark chrome */
  --color-ink-on-brand:     #ffffff;   /* button label on brand fills — white in every state; brand fills are deepened toward black so white holds */
  /* ink as an rgb triple, for text the app sets via rgba(0,0,0,α) (muted-by-opacity
     body/meta text). Default 0,0,0 = byte-identical; themes flip it light so the
     opacity-muted text stays legible on dark vibes. Use: rgba(var(--color-ink-rgb), α). */
  --color-ink-rgb:          0, 0, 0;
  --signal-tone:          #ffffff;
  --signal-amt:           0%;
  --color-status-success-text: color-mix(in srgb, var(--color-status-success), var(--signal-tone) var(--signal-amt));
  --color-status-warning-text: color-mix(in srgb, var(--color-status-warning), var(--signal-tone) var(--signal-amt));
  --color-status-error-text:   color-mix(in srgb, var(--color-status-error), var(--signal-tone) var(--signal-amt));
  --color-status-info-text:    color-mix(in srgb, var(--color-status-info), var(--signal-tone) var(--signal-amt));
  --color-indigo-text:         color-mix(in srgb, var(--color-indigo), var(--signal-tone) var(--signal-amt));
  --color-vis-highlight-text:  color-mix(in srgb, var(--color-vis-highlight), var(--signal-tone) var(--signal-amt));

  /* BU-532 (reskin-tokenize /plan/generator): the Plan Generator's material
     signal family — stage-category tabs, gantt bars, revision-kind markers.
     Distinct from the orange stage ramp; defaults are the page's exact values. */
  --color-sig-green:        #4caf50;
  --color-sig-green-rgb:    76, 175, 80;
  --color-sig-blue:         #2196f3;
  --color-sig-blue-rgb:     33, 150, 243;
  --color-sig-amber:        #ff9800;
  --color-sig-amber-rgb:    255, 152, 0;
  --color-sig-purple:       #9c27b0;
  --color-sig-purple-rgb:   156, 39, 176;
  --color-sig-slate:        #607d8b;
  --color-sig-blue-deep:    #1976d2;   /* selected gantt bar / calendar drop target */
  --color-sig-blue-deep-rgb: 25, 118, 210;
  --color-status-error-strong: #b91c1c; /* danger button hover/pressed fill */
  --color-fill-neutral-strong: #495057; /* dark neutral chip fill with on-accent text */
  --color-surface-rgb:      255, 255, 255;  /* surface as rgb for rgba(var(),a) glass that auto-themes */
  /* ---- Soft-tint system (PM-010 tint sweep) ----
     Pale colored badge/surface backgrounds: bg+border use rgba(var(--*-rgb), a) so they
     composite over the THEMED surface (pale on light, dark on dark) — auto-theming, no
     per-theme values. Text uses theme-aware tint-*-text tokens: dark on the pale default
     badge, flipped light on dark vibes (so default identity holds AND text stays legible). */
  --color-status-success-rgb: 25, 135, 84;
  --color-status-warning-rgb: 217, 119, 6;
  --color-status-error-rgb:   220, 38, 38;
  --color-status-info-rgb:    2, 132, 199;
  --color-indigo-rgb:         99, 102, 241;
  --color-tint-warning-text:  #854F0B;
  --color-tint-info-text:     #0C447C;
  --color-tint-success-text:  #27500A;
  --color-tint-error-text:    #b91c1c;
  --color-tint-indigo-text:   #3C3489;
  --color-line:             #e2e2e2;   /* hairline divider / border (light) */
  --color-line-strong:      #d1d5db;   /* heavier divider (gray-300) */
  --color-line-subtle:      #f3f4f6;   /* faint divider / row separator (gray-100) */
  --color-line-invert:      #2a2f37;   /* divider on always-dark chrome */
  --color-hover-surface:    #f3f3f3;   /* hover / focus surface fill */
  /* near-white elevation tints the app hardcodes on panels/rows (PM-010 Phase 3
     structural tokenize). Exact defaults so the un-skinned app is identical;
     variants remap them so these surfaces recolor instead of staying white. */
  --color-surface-raised:   #fafafa;   /* subtle raised panel (warm) */
  --color-surface-sunken:   #f5f5f5;   /* sunken / hover row (warm) */
  --color-surface-cool:     #f9fafb;   /* cool faint panel */
  --color-surface-cool-2:   #f3f4f6;   /* cool muted row */
  --color-accent:           #DD5100;   /* brand signal — Traffic Orange */
  --color-accent-rgb:       221, 81, 0; /* accent as rgb triple for rgba(var(--color-accent-rgb), a) tints (PM-010 tokenize) */
  --color-accent-aa:        #BD4500;   /* AA-safe shade on light canvas (4.62:1) */
  --color-accent-on-dark:   #FF6B1A;   /* accent on inverted/dark backgrounds */
  --color-accent-tint:      rgba(221, 81, 0, 0.06);
  --color-accent-hover:     rgba(221, 81, 0, 0.10);
  --color-accent-strong:    #8A3300;   /* accent pressed/hover-dark */
  --color-on-accent:        #FFFFFF;   /* text/icon on an accent fill */
  --color-invert-bg:        #111111;   /* inverted strip background */
  --color-invert-ink:       #FFFFFF;   /* text on inverted strip */

  /* ---- Funnel-stage ramp (canonical — consumed by markup AND charts) ---- */
  --color-stage-1:          #FFB380;   /* Awareness */
  --color-stage-2:          #FF8138;   /* Consideration */
  --color-stage-3:          #DD5100;   /* Decision */
  --color-stage-4:          #8A3300;   /* Retention */
  /* BU-382: named stage aliases. The 4 funnel stages were hardcoded with three
     different (and inconsistent) hue sets across compose-workbench.css,
     PlanGenerator.razor.css, and design-system.css. These named tokens alias the
     canonical --color-stage-1..4 ramp (which the per-vibe block below maps onto
     --v-stage-* so it repaints per vibe). Every stage circle / pill / chip /
     swatch / quad-letter routes through these for ONE consistent, vibe-aware set. */
  --color-stage-awareness:     var(--color-stage-1);
  --color-stage-consideration: var(--color-stage-2);
  --color-stage-decision:      var(--color-stage-3);
  --color-stage-retention:     var(--color-stage-4);

  /* ---- Status ---- */
  --color-indigo:           #6366f1;   /* chart/secondary indigo (funnel-level, position-bar, tier badge) — PM-010 tokenize */
  --color-status-success:   #198754;
  --color-status-warning:   #D97706;
  --color-status-error:     #DC2626;
  --color-status-info:      #0284C7;

  /* ---- Platform identity (signal-only; do not use for body text) ---- */
  --color-platform-instagram: #E1306C;
  --color-platform-facebook:  #1877F2;
  --color-platform-linkedin:  #0A66C2;
  --color-platform-youtube:   #FF0000;
  --color-platform-twitter:   #1DA1F2;
  --color-platform-tiktok:    #000000;
  --color-platform-spotify:   #1DB954;
  --color-platform-email:     #6B7280;
  --color-platform-sms:       #25D366;
  --color-platform-blog:      #14B8A6;

  /* ---- Position-type signal (IL concept colours) ---- */
  --color-pt-coin:          #0DCAF0;
  --color-pt-barbell:       #F59E0B;
  --color-pt-flywheel:      #198754;
  --color-pt-hub-spoke:     #6F42C1;
  --color-pt-pyramid:       #DD5100;

  /* ---- /assets stage-picker swatches (BU-658 tokenize) ----
     Blue/amber/green/violet rainbow - a DIFFERENT visual language from the
     canonical orange --color-stage-1..4 ramp, so it gets its own set rather
     than aliasing it (aliasing would repaint the page). Defaults = the exact
     literals previously hardcoded in AssetLibraryPage.razor. */
  --color-funnel-awareness:     #3b82f6;
  --color-funnel-consideration: #f59e0b;
  --color-funnel-decision:      #10b981;
  --color-funnel-retention:     #8b5cf6;

  /* ---- /assets library-level (clearance) badge identity (BU-658 tokenize) ---- */
  --color-liblevel-global:      #3b82f6;   /* Organization */
  --color-liblevel-group:       #10b981;   /* Workspace */
  --color-liblevel-partner:     #f59e0b;   /* Partner */
  --color-liblevel-engagement:  #ec4899;   /* Engagement */
  --color-liblevel-user:        #8b5cf6;   /* Personal */
  --color-liblevel-none:        #6c757d;   /* no matching level */

  /* ---- Cockpit shell (BU-658 tokenize /assets) ----
     The Asset Library is an always-dark instrument surface with its own shell and
     signal palette, distinct from the themed light chrome. Defaults equal the
     page's exact prior literals, so the tokenize pass is a zero-pixel change and
     a variant can repaint the whole cockpit from this one block. */
  --color-cockpit-bg:            #0b0d11;        /* page canvas */
  --color-cockpit-surface:       #14181e;        /* panel / card surface */
  --color-cockpit-surface-2:     #1a2028;        /* input + ghost-button fill */
  --color-cockpit-surface-3:     #202732;        /* raised chip / icon fill */
  --color-cockpit-line:          #29313d;        /* panel border */
  --color-cockpit-line-soft:     #232a34;        /* hairline / inner divider */
  --color-cockpit-ink:           #f8fafc;        /* primary text */
  --color-cockpit-ink-dim:       #c7d0dc;        /* secondary text */
  --color-cockpit-ink-muted:     #8d98a7;        /* label / meta text */
  --color-cockpit-ink-muted-rgb: 141, 152, 167;  /* muted as rgb triple for rgba() tints */
  --color-cockpit-ink-strong:    #ffffff;        /* text on an accent fill / spinner head */
  --color-cockpit-muted:         #717b88;        /* disabled tone */
  --color-cockpit-accent-hover:  #c94a00;        /* primary-button hover fill */
  --color-cockpit-overlay-rgb:   255, 255, 255;  /* white inset / hover overlays */
  --color-cockpit-shadow-rgb:    0, 0, 0;        /* menu drop shadow */
  --color-cockpit-scrim-rgb:     4, 6, 10;       /* modal scrim */
  /* cockpit signal family - brighter than the themed status set so it reads on the
     dark instrument surface; rgb triples back the rgba() tints. */
  --color-cockpit-success:       #3ecf7b;
  --color-cockpit-success-rgb:   62, 207, 123;
  --color-cockpit-warning:       #f4b740;
  --color-cockpit-warning-rgb:   244, 183, 64;
  --color-cockpit-error:         #e0523b;
  --color-cockpit-error-rgb:     224, 82, 59;
  --color-cockpit-info:          #4f8ef7;
  --color-cockpit-info-rgb:      79, 142, 247;
  --color-cockpit-violet-rgb:    160, 107, 240;  /* retention-stage tint */
  /* text colours that sit on the cockpit's tinted chips / alerts */
  --color-cockpit-success-text:      #dff6e8;
  --color-cockpit-success-chip-text: #ddf6e7;
  --color-cockpit-warning-text:      #ffe7ba;
  --color-cockpit-warning-chip-text: #ffe8b2;
  --color-cockpit-error-text:        #ffd5cf;
  --color-cockpit-error-chip-text:   #ffd8d3;
  --color-cockpit-neutral-chip-text: #b5bec9;

  /* ---- Visualization ---- */
  --color-vis-line:         #1F2937;
  --color-vis-node-fill:    #FFFFFF;
  --color-vis-node-border:  #374151;
  --color-vis-node-text:    #1F2937;
  --color-vis-title:        #111827;
  --color-vis-subtitle:     #6B7280;
  --color-vis-highlight:    #3B82F6;
  --color-vis-highlight-rgb: 59, 130, 246;  /* BU-386: rgb triple for rgba(var(),a) pricing-panel tints */

  /* ---- Visualization: studio-warm two-family system (BU-530, proven in BU-524 lab) ----
     Orange family marks the focal role of each position type (Hub, Fulcrum,
     Side A, Driver, Apex); plum family carries the structure. Ink on tints
     holds WCAG AA (worst measured lab pair 5.3:1).
     TOKENIZATION-GATE CONTRACT: every value is a computed chain over the
     themed canonicals (--color-surface / --color-ink / --color-accent) plus
     the --signal-amt lighten, so ALL vibes repaint the viz with zero per-vibe
     blocks. Plum is the viz's structural signal: hue is identity (literal),
     legibility is themed (signal-lighten + surface mixes). Default resolves
     within a perceptual hair of the lab values. */
  --viz-ink:                var(--color-ink, #1A1A1A);
  --viz-ink-muted:          color-mix(in srgb, #6E6961, var(--signal-tone) var(--signal-amt));
  --viz-line:               var(--color-ink, #1A1A1A);
  --viz-focal:              var(--color-accent, #DD5100);
  --viz-focal-deep:         color-mix(in srgb, color-mix(in srgb, var(--color-accent) 75%, #200800), var(--signal-tone) var(--signal-amt));
  --viz-focal-tint:         color-mix(in srgb, var(--color-accent) 15%, var(--color-surface));
  --viz-structure-text:     color-mix(in srgb, #4B4B5F, var(--signal-tone) var(--signal-amt));
  --viz-structure-border:   color-mix(in srgb, #8A8AA4, var(--signal-tone) var(--signal-amt));
  --viz-structure-tint:     color-mix(in srgb, #8A8AA4 18%, var(--color-surface));
  --viz-structure-tint-deep:color-mix(in srgb, #8A8AA4 28%, var(--color-surface));
  --viz-neutral-fill:       color-mix(in srgb, var(--color-ink) 5%, var(--color-surface));

  /* ---- Borders ---- */
  --border-grid:            1px solid #e2e2e2;
  --border-section:         2px solid var(--color-accent);
  --border-subtle:          1px solid var(--color-line);

  /* ---- Radius ---- */
  --radius-none:            0;
  --radius-sm:              4px;     /* editorial: sharp corners */
  --radius-md:              8px;
  --radius-lg:              12px;
  --radius-xl:              16px;
  --radius-pill:            9999px; /* kept for avatars / stage dots only */

  /* ---- Shadows ---- */
  --shadow-none:            none;
  --shadow-sm:              0 1px 2px rgba(0,0,0,0.06);   /* editorial: flat, no soft shadows */
  --shadow-md:              0 2px 8px rgba(0,0,0,0.08);
  --shadow-lg:              0 8px 24px rgba(0,0,0,0.12);
  --shadow-xl:              0 16px 48px rgba(0,0,0,0.16);
  --shadow-hover:           0 4px 12px rgba(0,0,0,0.12);
  --shadow-hover-transform: translateY(-2px);

  /* ---- Type families ---- */
  --font-display:           'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-prose:             'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-ui:                'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono:              'JetBrains Mono', 'SF Mono', 'Monaco', 'Fira Code', monospace;

  /* ---- Type scale ---- */
  --text-hero:              clamp(3rem, 7vw, 6rem);
  --text-display:           clamp(2rem, 4vw, 3.5rem);
  --text-h1:                clamp(1.5rem, 3vw, 2.25rem);
  --text-h2:                1.5rem;
  --text-h3:                1.125rem;
  --text-body:              0.9375rem;   /* 15px */
  --text-ui:                0.875rem;    /* 14px */
  --text-caption:           0.75rem;     /* 12px */
  --text-label:             0.6875rem;   /* 11px */
  --text-mono:              0.8125rem;   /* 13px */

  /* ---- Leading ---- */
  --leading-tight:          0.9;
  --leading-display:        1.1;
  --leading-body:           1.65;
  --leading-ui:             1.4;

  /* ---- Weight ---- */
  --weight-display:         700;
  --weight-strong:          700;
  --weight-medium:          500;
  --weight-body:            400;

  /* ---- Tracking ---- */
  --tracking-display:       -0.025em;
  --tracking-hero:          -0.035em;
  --tracking-eyebrow:       0.16em;

  /* ---- Spacing (4-unit grid) ---- */
  --space-1:    4px;
  --space-2:    8px;
  --space-3:    12px;
  --space-4:    16px;
  --space-5:    20px;
  --space-6:    24px;
  --space-8:    32px;
  --space-10:   40px;
  --space-12:   48px;
  --space-16:   64px;

  /* ---- Layout (themeable — a swap can rearrange) ---- */
  --content-max:            880px;
  --shell-max:              1200px;
  --measure-max:            540px;
  --split-cols:             1fr 1fr;  /* variant can flip to 1fr */
  --split-gap:              var(--space-8);
  --grid-min:               220px;
  --space-section:          var(--space-12);
  --space-block:            var(--space-6);

  /* ---- Motion ---- */
  --motion-fast:            150ms ease-out;
  --motion-mid:             250ms ease-out;
  --motion-slow:            350ms ease-out;

  /* ---- Legacy aliases (for components that still reference old names) ----
     These keep the build green while the migration progresses. Remove each
     alias once its consumer has been updated to the canonical name above. */
  --color-brand-primary:               var(--color-accent);
  --color-brand-primary-light-10:      #E56B1A;
  --color-brand-primary-dark-10:       var(--color-accent-aa);
  --color-brand-primary-dark-20:       var(--color-accent-strong);
  --color-neutral-white:               var(--color-canvas);
  --color-neutral-gray-50:             #F5F5F5;
  --color-neutral-gray-100:            #EEEEEA;
  --color-neutral-gray-200:            var(--color-line);
  --color-neutral-gray-300:            #D4D4CE;
  --color-neutral-gray-400:            #A3A3A3;
  --color-neutral-gray-500:            var(--color-ink-muted);
  --color-neutral-gray-600:            #525252;
  --color-neutral-gray-700:            var(--color-ink-dim);
  --color-neutral-gray-800:            #262626;
  --color-neutral-gray-900:            var(--color-ink);
  --color-neutral-black:               var(--color-ink);
  --ed-canvas:                         var(--color-canvas);
  --ed-ink:                            var(--color-ink);
  --ed-ink-dim:                        var(--color-ink-dim);
  --ed-ink-muted:                      var(--color-ink-muted);
  --ed-ink-meta:                       var(--color-ink-muted);
  --ed-divider:                        var(--color-line);
  --ed-hover-surface:                  var(--color-hover-surface);
  --ed-accent:                         var(--color-accent);
  --ed-accent-aa:                      var(--color-accent-aa);
  --ed-accent-dark:                    var(--color-accent-on-dark);
  --ed-accent-tint:                    var(--color-accent-tint);
  --ed-accent-hover:                   var(--color-accent-hover);
  --ed-border-grid:                    var(--border-grid);
  --ed-border-section:                 var(--border-section);
  --ed-border-subtle:                  var(--border-subtle);
  --ed-border-radius:                  var(--radius-md);
  --ed-shadow-none:                    var(--shadow-none);
  --ed-shadow-hover:                   var(--shadow-hover);
  --ed-shadow-hover-t:                 var(--shadow-hover-transform);
  --ed-font-display:                   var(--font-display);
  --ed-font-prose:                     var(--font-prose);
  --ed-font-ui:                        var(--font-ui);
  --ed-font-mono:                      var(--font-mono);
  --ed-text-hero:                      var(--text-hero);
  --ed-text-display:                   var(--text-display);
  --ed-text-h1:                        var(--text-h1);
  --ed-text-h2:                        var(--text-h2);
  --ed-text-h3:                        var(--text-h3);
  --ed-text-body:                      var(--text-body);
  --ed-text-ui:                        var(--text-ui);
  --ed-text-caption:                   var(--text-caption);
  --ed-text-label:                     var(--text-label);
  --ed-text-mono:                      var(--text-mono);
  --ed-leading-tight:                  var(--leading-tight);
  --ed-leading-display:                var(--leading-display);
  --ed-leading-body:                   var(--leading-body);
  --ed-leading-ui:                     var(--leading-ui);
  --ed-sp-1:  var(--space-1);
  --ed-sp-2:  var(--space-2);
  --ed-sp-3:  var(--space-3);
  --ed-sp-4:  var(--space-4);
  --ed-sp-5:  var(--space-5);
  --ed-sp-6:  var(--space-6);
  --ed-sp-8:  var(--space-8);
  --ed-sp-10: var(--space-10);
  --ed-sp-12: var(--space-12);
  --ed-sp-16: var(--space-16);
  --ed-transition:      var(--motion-mid);
  --ed-transition-fast: var(--motion-fast);
  /* theme-canonical legacy aliases */
  --body-bg:               var(--color-canvas);
  --body-color:            var(--color-ink);
  --surface-bg:            var(--color-hover-surface);
  --voice-badge-color:     var(--color-accent);
  --error-color:           var(--color-status-error);
  /* themes.css legacy aliases */
  --radius-2xl:            16px;
  --radius-none: 0;
  --glass-panel-bg:        var(--color-surface);
  --glass-panel-border:    var(--color-line);
  --glass-panel-shadow:    var(--shadow-md);
  --glass-card-bg:         var(--color-surface);
  --glass-card-bg-hover:   var(--color-hover-surface);
  --glass-card-border:     var(--color-line);
  --glass-card-border-hover: var(--color-ink-muted);
  --glass-card-shadow:     var(--shadow-sm);
  --glass-card-shadow-hover: var(--shadow-hover);
  --input-bg:              var(--color-surface);
  --input-bg-focus:        var(--color-surface);
  --input-border:          var(--color-line);
  --input-border-focus:    var(--color-ink-muted);
  --btn-bg:                var(--color-ink);
  --btn-border:            var(--color-ink);
  --btn-color:             var(--color-invert-ink);
  --btn-bg-hover:          var(--color-ink-dim);
  --success-bg:            rgba(25, 135, 84, 0.08);
  --success-border:        rgba(25, 135, 84, 0.35);
  --success-color:         #155936;
  --error-bg:              rgba(220, 38, 38, 0.08);
  --error-border:          rgba(220, 38, 38, 0.35);
  --error-color-state:     #7F1D1D;
  /* transitions aliases */
  --transition-fast:       var(--motion-fast);
  --transition-normal:     var(--motion-mid);
  --transition-slow:       var(--motion-slow);
  /* font aliases */
  --font-sans:             var(--font-ui);
  /* spacing aliases */
  --space-0:  0;
  /* text size aliases */
  --text-xs:   var(--text-label);
  --text-sm:   var(--text-caption);
  --text-base: var(--text-body);
  --text-lg:   var(--text-h3);
  --text-xl:   1.25rem;
  --text-2xl:  var(--text-h2);
  --text-3xl:  1.875rem;
  --text-4xl:  2.25rem;
  --font-normal:    var(--weight-body);
  --font-medium:    var(--weight-medium);
  --font-semibold:  600;
  --font-bold:      var(--weight-strong);
  --leading-normal: var(--leading-ui);
  --leading-relaxed: 1.75;
}

/* ============================================================================
 * BU-823 — THE HOUSE SMALL-VIEWPORT SCALE (breakpoint-aware token overrides)
 * ----------------------------------------------------------------------------
 * The --text-* and --space-* tokens above are global and NOT breakpoint-aware,
 * which is why no page can express "smaller on a phone" without hand-writing it.
 * This block makes them breakpoint-aware, so density is something a page can ask
 * for from the token layer instead of re-deriving per component.
 *
 * THE ASK, verbatim (Beau, 2026-08-20): "you turn it into what feels like a kid's
 * toy almost, with big letters and big shapes. it looks coarse and cheap... you're
 * underestimating how much people can read on a phone. font sizes don't actually
 * have to be that big anymore... it looks better when shapes and icons are
 * relatively small."
 *
 * THE SCALE, enforced by JarganApiDemo/scripts/density-gate.mjs at 390 and 768:
 *   body 14 · UI 13 · caption 12 · label 11 · h3 15 · h2 17 · h1 20
 *   icons 16-18 · controls 36
 *
 * WHY 900px, and not responsive.css's 767.98 canon. The gate asserts at 768, and a
 * max-width:767.98px block does not match a viewport that is exactly 768 wide, so
 * the canon breakpoint would leave one of the two asserted viewports uncovered.
 * 900px is also the single most-declared breakpoint in Jargan.Client (89 blocks,
 * vs 82 at 640, 80 at 720, 58 at 480, 19 at 768 and 18 at 767.98 -- counted with
 *   grep -rhoE 'max-width:[[:space:]]*900px' --include='*.css' Jargan.Client
 * on 2026-08-21), so this follows a breakpoint the pages ALREADY declare, which is
 * the house rule for this card. Standardising the 11+ live breakpoints is a
 * different and explicitly larger ask.
 *
 * The aliases below the primitives (--text-xs/sm/base/lg/2xl, --ed-sp-*,
 * --space-section, --space-block, --split-gap) are all var() references, and var()
 * resolves at computed-value time on the element — so they inherit these overrides
 * automatically and are not restated here. The few aliases that were authored as
 * LITERALS (--text-xl/3xl/4xl) do not, so they are restated to keep the ladder
 * monotonic.
 *
 * NOT SET HERE, deliberately: control height (36px) and icon size (16-18px). The
 * gate enforces both, but there is no --control-* or --icon-* token in this file
 * today to override, and inventing one that only exists below 900px would be a
 * token that silently evaporates on desktop. Those two remain per-component levers
 * (e.g. responsive.css:99-106's 44px touch floor, VoiceLibrary.razor.css:1132).
 *
 * Colour is untouched — no raw hex or rgb() is introduced; colour continues to
 * route through the --v-* semantic layer.
 * ========================================================================== */
@media (max-width: 900px) {
  :root {
    /* ---- Type scale: the house small-viewport scale ---- */
    --text-hero:      20px;   /* was clamp(3rem, 7vw, 6rem)   -> 48px at 390 */
    --text-display:   20px;   /* was clamp(2rem, 4vw, 3.5rem) -> 32px at 390 */
    --text-h1:        20px;   /* was clamp(1.5rem, 3vw, 2.25rem) -> 24px at 390 */
    --text-h2:        17px;   /* was 1.5rem    (24px) */
    --text-h3:        15px;   /* was 1.125rem  (18px) */
    --text-body:      14px;   /* was 0.9375rem (15px) */
    --text-ui:        13px;   /* was 0.875rem  (14px) */
    --text-caption:   12px;   /* was 0.75rem   (12px) — already at the floor */
    --text-label:     11px;   /* was 0.6875rem (11px) — unchanged, the scale's base */
    --text-mono:      12px;   /* was 0.8125rem (13px) */

    /* 20px is the top of the scale at these widths: nothing outranks h1 on a
       phone, so hero and display collapse onto it rather than towering over it. */

    /* ---- Literal-valued aliases: restated so the ladder stays monotonic ---- */
    --text-xl:        16px;   /* was 1.25rem   (20px) — sits between h3 and h2 */
    --text-3xl:       18px;   /* was 1.875rem  (30px) */
    --text-4xl:       20px;   /* was 2.25rem   (36px) — capped at h1 */

    /* ---- Spacing: tightened so denser type is not swimming in desktop gutters.
       Shrinking type while keeping 64px section gaps still reads as coarse. ---- */
    --space-1:         3px;   /* was  4px */
    --space-2:         6px;   /* was  8px */
    --space-3:         8px;   /* was 12px */
    --space-4:        12px;   /* was 16px */
    --space-5:        14px;   /* was 20px */
    --space-6:        16px;   /* was 24px */
    --space-8:        20px;   /* was 32px */
    --space-10:       24px;   /* was 40px */
    --space-12:       28px;   /* was 48px */
    --space-16:       36px;   /* was 64px */
  }
}

/* ============================================================================
 * BU-823 — /voices INSTRUMENT STRIP: row alignment when a label wraps
 * ----------------------------------------------------------------------------
 * WHERE THIS BELONGS, and why it is not there. The natural home for these four
 * rules is Pages/VoiceLibrary/VoiceLibrary.razor.css, next to the .vlb-chan
 * block they correct. That file is outside this unit's write fence, and this
 * stylesheet is the only one in it. FOLLOW-UP: move this block into
 * VoiceLibrary.razor.css verbatim and delete it from here — it is component
 * CSS boarding in the token layer, and it should not stay.
 *
 * THE DEFECT, measured on /voices at 1440 (2026-08-21). One position type is
 * named "4 Quadrant Grid" in the data — three tokens where every sibling
 * ("Coin", "Barbell", "Flywheel", "Pyramid", "Hub+Spoke") is one. The strip
 * packs 3 readouts + 6 channels into ~1113px and every cell is already at its
 * min-content floor, so there is no width to give: the label MUST wrap. It
 * wrapped to three lines ("4" / "QUADRANT" / "GRID"), and because
 * .vlb-chan__top is align-items:center the glyph and the count were centred
 * against a 45px-tall label — so they landed on the MIDDLE line. The tile read
 * as: a bare "4" floating on line one with no icon, then "QUADRANT" with a
 * second, unrelated "0" beside it. Two numbers, no icon, no pattern. Its meter
 * also sat 13px lower than every sibling's (y=431 vs y=418), which is what made
 * the row's baseline look jagged.
 *
 * THE FIX is four rules, and none of them hides or truncates the label:
 *   1. justify-content:flex-start + meter margin-top:auto — labels pin to the
 *      top of the cell, meters pin to the bottom. Every tile now agrees on both
 *      edges no matter how many lines its label takes. This is the rule that
 *      actually fixes the jagged row, and it holds for ANY label length.
 *   2. align-items:baseline on __top — the glyph and the count sit on the
 *      label's FIRST baseline instead of its vertical centre, so the tile reads
 *      icon + LABEL + value on line one exactly like its five siblings.
 *   3. 16px -> 10px side padding and 0.12em -> 0.08em tracking, applied ONLY
 *      above 1080px. That returns 12px to the name box (58px -> 70px), which is
 *      enough to keep "4 QUADRANT" together on line one: the label wraps to two
 *      lines instead of three and the leading "4" is never orphaned next to the
 *      count. Both are also density moves in this card's own direction.
 *      1080px is the breakpoint VoiceLibrary.razor.css ALREADY declares for
 *      this strip (it goes 2-up below it, where cells are wide and the label
 *      fits on one line unaided), per the house rule about reusing live
 *      breakpoints.
 *   4. The meter track carries its own channel accent instead of flat
 *      --line-soft. A zero-count channel is a legitimate empty state, but an
 *      untinted grey bar next to five saturated ones read as an unstyled
 *      placeholder. The track is now visibly the channel's own colour at low
 *      strength, so "empty" reads as deliberate. The FILL stays at its true 0% —
 *      no minimum-width cheat, because that would misreport the data.
 *
 * Colour routes through --posc / --line-soft / --purple via color-mix; no raw
 * hex or rgb() is introduced.
 *
 * SPECIFICITY. jargan-semantic-tokens.css loads at index.html:26, BEFORE
 * Jargan.Client.styles.css at :45, so a tie loses to the scoped rule. Blazor
 * scoping makes those selectors .vlb-chan[b-ovll02sj99] = (0,2,0); the doubled
 * class and the three-class descendant selectors below are (0,3,0), which wins
 * on specificity rather than on load order.
 * ========================================================================== */
.vlb-instr .vlb-chan.vlb-chan {
  justify-content: flex-start;
}

.vlb-instr .vlb-chan .vlb-chan__top {
  align-items: baseline;
}

.vlb-instr .vlb-chan .vlb-chan__meter {
  margin-top: auto;
  background: color-mix(in srgb, var(--posc, var(--purple)) 22%, var(--line-soft));
}

@media (min-width: 1081px) {
  .vlb-instr .vlb-chan.vlb-chan {
    padding-left: 10px;
    padding-right: 10px;
  }

  .vlb-instr .vlb-chan .vlb-chan__name {
    letter-spacing: 0.08em;
  }
}


/* ============================================================
 * THROWAWAY VARIANT LAYER (A/B swap demo — BU-373 acceptance)
 * Apply: add class="jgn-variant-b" to <html> or <body>
 * Restyles the migrated surface with ZERO markup edits; swaps the surface to
 * the editorial Newsprint look (serif / warm canvas / sharp corners / hard
 * offset shadow).
 * ============================================================ */
.jgn-variant-b {
  --color-canvas:       #E8E0CF;
  --color-surface:      #F6F1E5;
  --color-ink:          #111111;
  --color-ink-dim:      #404040;
  --color-ink-muted:    #737373;
  --color-line:         #E5E5E0;
  --color-hover-surface: #F5F5F5;
  --color-invert-bg:    #111111;
  --color-invert-ink:   #FFFFFF;
  --radius-none:        0;
  --radius-sm:          0;
  --radius-md:          0;
  --radius-lg:          0;
  --radius-xl:          0;
  --shadow-sm:          none;
  --shadow-md:          none;
  --shadow-lg:          none;
  --shadow-hover:       4px 4px 0 0 #111111;
  --shadow-hover-transform: translate(-2px, -2px);
  --font-display:       'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-prose:         'Lora', Georgia, 'Times New Roman', serif;
  --weight-display:     800;
  --border-grid:        1px solid #111111;
  --border-section:     4px solid #111111;
  --ed-border-radius:   0;
  --radius-2xl:         0;
  --glass-panel-shadow: none;
  --glass-card-shadow:  none;
}

/* PM-010: Bootstrap bridge — most admin screens are Bootstrap-painted (--bs-* vars),
   which bypass the canonical tokens. Override Bootstrap's surface variables in the
   variant ONLY (control keeps Bootstrap defaults, so control is byte-identical).
   This makes Bootstrap-based screens repaint on the jgn-variant-b token swap. */
.jgn-variant-b {
  --bs-body-bg:            #E8E0CF;   /* paper canvas (= variant --color-canvas) */
  --bs-body-color:         #111111;   /* ink */
  --bs-secondary-bg:       #F6F1E5;   /* paper surface */
  --bs-secondary-color:    #404040;
  --bs-tertiary-bg:        #EFE8D8;
  --bs-tertiary-color:     #737373;
  --bs-emphasis-color:     #111111;
  --bs-border-color:       #C9BFA8;   /* editorial rule */
  --bs-card-bg:            #F6F1E5;
  --bs-card-color:         #111111;
  --bs-card-border-color:  #C9BFA8;
  --bs-card-cap-bg:        #EFE8D8;
  --bs-heading-color:      #111111;
}

/* PM-010: Bootstrap component surfaces — Bootstrap sets these per-component, so a
   root --bs-* override loses to the component's local default. Clearance under the
   variant (control unaffected) so the swap repaints them app-wide. */
.jgn-variant-b .card,
.jgn-variant-b .modal-content,
.jgn-variant-b .dropdown-menu,
.jgn-variant-b .list-group-item,
.jgn-variant-b .offcanvas,
.jgn-variant-b .accordion-button,
.jgn-variant-b .accordion-item {
  background-color: var(--color-surface);
  color: var(--color-ink);
  border-color: var(--color-line);
}
.jgn-variant-b .card { border-radius: var(--radius-md); }
.jgn-variant-b .table { --bs-table-bg: transparent; --bs-table-color: var(--color-ink); }

/* PM-010: Bootstrap component-variable families — extend the variant bridge across
   the rest of Bootstrap so tables, forms, nav, badges, buttons repaint on swap.
   Variant-clearanced only (control = Bootstrap defaults, untouched). Accent buttons
   keep Traffic Orange (brand signal, not theme-invariant chrome). */
.jgn-variant-b {
  /* tables */
  --bs-table-bg:            transparent;
  --bs-table-color:         var(--color-ink);
  --bs-table-border-color:  var(--color-line);
  --bs-table-striped-bg:    rgba(17,17,17,0.03);
  --bs-table-hover-bg:      rgba(17,17,17,0.05);
  /* forms */
  --bs-form-control-bg:     var(--color-surface);
  /* nav / tabs */
  --bs-nav-tabs-border-color: var(--color-line);
  --bs-nav-tabs-link-active-bg: var(--color-surface);
  --bs-nav-tabs-link-active-color: var(--color-ink);
  /* generic borders + headings */
  --bs-border-color-translucent: var(--color-line);
}
/* form inputs + secondary/light/outline buttons -> paper surface (primary stays accent) */
.jgn-variant-b .form-control,
.jgn-variant-b .form-select,
.jgn-variant-b .input-group-text {
  background-color: var(--color-surface);
  color: var(--color-ink);
  border-color: var(--color-line);
}
.jgn-variant-b .btn-light,
.jgn-variant-b .btn-outline-secondary,
.jgn-variant-b .btn-secondary {
  --bs-btn-bg: var(--color-surface);
  --bs-btn-color: var(--color-ink);
  --bs-btn-border-color: var(--color-line);
  --bs-btn-hover-bg: var(--color-hover-surface);
}
.jgn-variant-b .badge.bg-light,
.jgn-variant-b .badge.text-bg-light { background-color: var(--color-surface) !important; color: var(--color-ink) !important; }

/* PM-010: Bootstrap .table uses a nested colour cascade (striped/active/hover states
   override --bs-table-color). Set the state variables too so table text reskins. */
.jgn-variant-b .table {
  --bs-table-color: var(--color-ink);
  --bs-table-striped-color: var(--color-ink);
  --bs-table-active-color: var(--color-ink);
  --bs-table-hover-color: var(--color-ink);
  --bs-emphasis-color: var(--color-ink);
}

/* PM-010: panel/host shells + remaining surface containers -> variant surface */
.jgn-variant-b .panel-host-panel,
.jgn-variant-b .panel-host-panel-header,
.jgn-variant-b .panel-host-panel-body,
.jgn-variant-b [class*="panel-host"],
.jgn-variant-b .neo-panel,
.jgn-variant-b .neo-panel-flat {
  background-color: var(--color-surface);
  border-color: var(--color-line);
}

/* PM-010: scoped-CSS components ([b-hash] selectors tie + win on load order) need
   higher specificity to reskin. html.jgn-variant-b (0,2,1) beats scoped (0,2,0). */
html.jgn-variant-b .panel-host-panel,
html.jgn-variant-b .panel-host-panel-header,
html.jgn-variant-b .panel-host-panel-body,
html.jgn-variant-b [class*="panel-host"],
html.jgn-variant-b .neo-panel,
html.jgn-variant-b .neo-panel-flat,
html.jgn-variant-b .neo-card {
  background-color: var(--color-surface);
  border-color: var(--color-line);
  color: var(--color-ink);
}
html.jgn-variant-b .panel-host-panel { background-color: var(--color-surface) !important; }

/* ============================================================
 * CANONICAL LAYER → data-theme awareness (PM-010 convergence).
 * The app's themes set --bg-color/--surface-bg/--bg-* but never the canonical
 * --color-* tokens, so components routed to var(--color-surface) stayed light
 * under every theme. Map the canonical tokens per data-theme here so a theme
 * = values in ONE block and every --color-*-consuming component recolors.
 * Mirrors each theme's existing surface/ink intent.
 * ============================================================ */
/* BU-666: dark's standalone hardcoded --color-* block used to live here (~50 literal
   values duplicating what the generic --v-* mapping below now produces for it). It is
   DELETED, and "dark" is removed from that mapping's :not() exclusion chain, so Dark
   Mode reads the SAME shared mapping every other vibe reads. Its palette now lives in
   ONE place — jargan-vibe-palette.css :root[data-theme="dark"], added by this card —
   which feeds both the root content and Layout/ShellSidebar.razor.css, so they cannot
   drift. Before this, dark had NO --v-* block at all: the sidebar fell through to the
   file's LIGHT neutral base and rendered white glass with near-black ink beside a
   deep-navy page (the same mismatch BU-663 found on pastel-mondrian).
   Only the BU-532 link trio survives, for the reason stated below.
   SPECIFICITY: generic is now [data-theme] + 2 :not() = (0,3,0); this selector mirrors
   its :not() chain plus :root + the theme attr = (0,4,0), so it wins. Same idiom as
   the sepia-ledger / pastel-mondrian fixup blocks further down. */
:root[data-theme="dark"]:not([data-theme="default"]):not([data-theme="instrument-paper"]) {
  /* BU-532: the legacy dark block predates the link routing the generic vibe
     mapping has — a, .btn-link stayed #0071c1 (3.36:1 on charcoal). Same fix.
     BU-666 keeps these three rather than inheriting the generic's var(--v-info):
     they are this theme's shipped link pair and pinning them keeps every link and
     hover pixel-identical through the re-plumbing. #6ab8ff is 7.50:1 on surface
     #16213e, #8ec9ff is 9.36:1 — both clear AA comfortably. */
  --color-brand-secondary: #6ab8ff;
  --bs-link-color:         #6ab8ff;
  --bs-link-hover-color:   #8ec9ff;
}
/* BU-665: pastel-mondrian's standalone hardcoded --color-* block used to live here
   (~35 literal hex values duplicating what the generic --v-* mapping below produces).
   It is DELETED. pastel-mondrian now reads the SAME shared mapping every other vibe
   reads (see the generic block below, and its :root[data-theme="pastel-mondrian"]
   light-vibe fixup near the sepia-ledger one). One palette — jargan-vibe-palette.css
   :root[data-theme="pastel-mondrian"] — feeds both the root content and the sidebar,
   so they cannot drift. BU-663 had closed the sidebar gap by ADDING a second copy of
   the values; this removes the copy instead. */

/* FRESH-VIBE PROOF (PM-010): "Dusk" — a never-used deep-indigo vibe, authored
   values-only to test rapid-reskin against the calibrated gate. Canonical + the
   themes.css component-surface layer, one block, NO new application rules, NO
   component edits. What still leaks = families not yet in the shared contract. */
[data-theme="dusk"] {
  --color-canvas:         #16121f;
  --color-surface:        #221b33;
  --color-surface-raised: #2b2240;
  --color-surface-sunken: #1b1528;
  --color-surface-cool:   #221b33;
  --color-surface-cool-2: #251d38;
  --color-hover-surface:  #312747;
  --color-ink:            #ece8f5;
  --signal-amt:           44%;
  --color-accent-strong:  #ff8a4d;
  --color-accent-aa:      #ff8a4d;
  --color-ink-rgb:        236, 232, 245;
  --color-surface-rgb:    34, 27, 51;
  --color-tint-warning-text:  #f0b860;
  --color-tint-info-text:     #93c5fd;
  --color-tint-success-text:  #86efac;
  --color-tint-error-text:    #fca5a5;
  --color-tint-indigo-text:   #c7d2fe;
  --color-ink-dim:        #b6acce;
  --color-ink-muted:      #a59cc0;
  --color-ink-secondary:  #b6acce;
  --color-ink-tertiary:   #a59cc0;
  --color-line:           rgba(255,255,255,0.12);
  --color-line-strong:    rgba(255,255,255,0.20);
  --color-line-subtle:    rgba(255,255,255,0.08);
  /* themes.css component-surface layer (same block) */
  --surface-bg:           #221b33;
  --body-bg:              #16121f;
  --kpi-strip-bg:         #1b1528;
  --pack-card-bg:         #221b33;
  --card-footer-bg:       #1b1528;
  --voice-badge-bg:       #2b2240;
  --glass-panel-bg:       rgba(34,27,51,0.85);
  --glass-card-bg:        rgba(43,34,64,0.7);
  --input-bg:             #1b1528;
  --input-bg-focus:       #2b2240;
  --bs-secondary-bg:      #2b2240;
  --bs-light:             #221b33;
  --bs-light-rgb:         34,27,51;
  --bs-body-color:        #ece8f5;
  --bs-body-bg:           #16121f;
  --bs-emphasis-color:    #ffffff;
  --bs-heading-color:     #ece8f5;
  --bs-secondary-color:   #b6acce;
}

/* COLD-VIBE ACCEPTANCE (PM-010): "Deep Sea" — abyssal teal-black, bioluminescent
   cyan accents, kelp-green highlights. Authored values-only, same shape as Dusk:
   canonical + themes.css component layer + --bs-* + tint-text + ink-rgb + surface-rgb,
   ONE block, NO new application rules, NO component edits. Brand accent stays Traffic
   Orange (signal preserved; keeps the gate's accent exclusion valid). */
[data-theme="deep-sea"] {
  --color-canvas:         #04161a;
  --color-surface:        #0a2329;
  --color-surface-raised: #0e2e36;
  --color-surface-sunken: #061b20;
  --color-surface-cool:   #0a2329;
  --color-surface-cool-2: #0c2a31;
  --color-hover-surface:  #123a44;
  --color-ink:            #e0f5f0;
  --signal-amt:           44%;
  --color-accent-strong:  #ff8a4d;
  --color-accent-aa:      #ff8a4d;
  --color-ink-dim:        #9ec9c4;
  --color-ink-muted:      #6f9b97;
  --color-ink-secondary:  #9ec9c4;
  --color-ink-tertiary:   #6f9b97;
  --color-ink-rgb:        224, 245, 240;
  --color-surface-rgb:    10, 35, 41;
  --color-line:           rgba(120, 230, 220, 0.14);
  --color-line-strong:    rgba(120, 230, 220, 0.22);
  --color-line-subtle:    rgba(120, 230, 220, 0.08);
  --color-tint-warning-text: #f0d890;
  --color-tint-info-text:    #7fe3ff;
  --color-tint-success-text: #9be8a0;
  --color-tint-error-text:   #ff9a9a;
  --color-tint-indigo-text:  #b8c0ff;
  /* themes.css component-surface layer */
  --surface-bg:           #0a2329;
  --body-bg:              #04161a;
  --kpi-strip-bg:         #061b20;
  --pack-card-bg:         #0a2329;
  --card-footer-bg:       #061b20;
  --voice-badge-bg:       #0e2e36;
  --glass-panel-bg:       rgba(10,35,41,0.85);
  --glass-card-bg:        rgba(14,46,54,0.7);
  --input-bg:             #061b20;
  --input-bg-focus:       #0e2e36;
  --bs-secondary-bg:      #0e2e36;
  --bs-light:             #0a2329;
  --bs-light-rgb:         10,35,41;
  --bs-body-color:        #e0f5f0;
  --bs-body-bg:           #04161a;
  --bs-emphasis-color:    #ffffff;
  --bs-heading-color:     #e0f5f0;
  --bs-secondary-color:   #9ec9c4;
}

/* carbon-lime — MAPS the Blazor --color-* scheme onto the canonical --v-* vars
   from jargan-vibe-palette.css (shared, single source of truth with the SPA).
   No raw hex: the vibe + the brand colour live in the palette, one place. */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) {
  --color-canvas:         var(--v-canvas);
  --color-surface:        var(--v-surface);
  --color-surface-raised: var(--v-surface-raised);
  --color-surface-sunken: var(--v-surface-sunken);
  --color-surface-cool:   var(--v-surface);
  --color-surface-cool-2: var(--v-surface-raised);
  --color-hover-surface:  var(--v-hover);
  --color-ink:            var(--v-ink);
  --signal-amt:           44%;
  /* BRAND signal = JARGAN orange. Reads the --admin surface variant first so the
     admin can strategically diverge from the SPA, else the shared --v-accent-brand. */
  --color-accent:         var(--v-accent-brand--admin, var(--v-accent-brand));
  --color-on-accent:      var(--v-on-accent);
  --color-accent-hover:   var(--v-accent-brand-soft);
  --color-accent-tint:    var(--v-accent-brand-tint);
  --color-accent-strong:  var(--v-accent-2);
  --color-accent-aa:      var(--v-accent-2);
  --color-ink-dim:        var(--v-ink-dim);
  --color-ink-muted:      var(--v-ink-muted);
  --color-ink-secondary:  var(--v-ink-dim);
  --color-ink-tertiary:   var(--v-ink-muted);
  --color-ink-rgb:        var(--v-ink-rgb);
  --color-ink-on-brand:   #ffffff;   /* white on EVERY vibe — brand button fills deepen toward black so white holds */
  --color-surface-rgb:    var(--v-surface-rgb);
  --color-line:           var(--v-line);
  --color-line-strong:    var(--v-line-strong);
  --color-line-subtle:    var(--v-line-subtle);
  /* PM-010 reskin coverage: stage identity recolors per vibe (matches SP-509 on the
     SPA). Without these the pale default stage tints (#FFB380 etc.) stay constant and
     stage-tinted borders (compose media-card multilink / engagement tabs) never repaint
     under the dark vibe. Map onto the canonical per-vibe stage palette. */
  --color-stage-1:        var(--v-stage-awareness);
  --color-stage-2:        var(--v-stage-consideration);
  --color-stage-3:        var(--v-stage-decision);
  --color-stage-4:        var(--v-stage-retention);
  --color-tint-warning-text: var(--v-warning);
  --color-tint-info-text:    var(--v-info);
  --color-tint-success-text: var(--v-success);
  --color-tint-error-text:   var(--v-error);
  --color-tint-indigo-text:  #b8c0ff;
  --surface-bg:           var(--v-surface);
  --body-bg:              var(--v-canvas);
  --kpi-strip-bg:         var(--v-canvas);
  --pack-card-bg:         var(--v-surface);
  --card-footer-bg:       var(--v-canvas);
  --voice-badge-bg:       var(--v-surface-raised);
  --glass-panel-bg:       color-mix(in srgb, var(--v-surface) 85%, transparent);
  --glass-card-bg:        color-mix(in srgb, var(--v-surface-raised) 70%, transparent);
  --input-bg:             var(--v-canvas);
  --input-bg-focus:       var(--v-surface-raised);
  --bs-secondary-bg:      var(--v-surface-raised);
  --bs-light:             var(--v-surface);
  --bs-light-rgb:         var(--v-surface-rgb);
  --bs-body-color:        var(--v-ink);
  --bs-body-bg:           var(--v-canvas);
  --bs-emphasis-color:    #ffffff;
  --bs-heading-color:     var(--v-ink);
  --bs-secondary-color:   var(--v-ink-dim);
  /* Bootstrap .card kept its white --bs-card-bg (bridged for .jgn-variant-b but not
     the vibe), leaving template-builder / edit-form cards white with light labels on
     top. Bridge the card family onto the token origin. */
  --bs-card-bg:           var(--v-surface);
  --bs-card-color:        var(--v-ink);
  --bs-card-border-color: var(--v-line);
  --bs-card-cap-bg:       var(--v-surface-sunken);
  /* PM-010 reskin coverage: legacy color-text- / color-border aliases are used by
     older pages (btn-cancel, pager-count, etc.) with a hardcoded light fallback and
     were never defined per-theme, so they stayed light-gray under the vibe. Define
     them on the canonical origin so every legacy consumer reskins. */
  --color-text-primary:   var(--v-ink);
  --color-text-secondary: var(--v-ink-dim);
  --color-text-tertiary:  var(--v-ink-muted);
  --color-text:           var(--v-ink);
  --color-border:         var(--v-line);
  /* Status colours were never redefined for the vibe, so they fell back to the dark
     default saturated set (#DC2626 etc.) which sits at ~3.5:1 on charcoal and never
     repaints. Route to the canonical light status palette (AA on dark). Fixes status
     text/badges app-wide incl. the simulator --over/--ok tokens that alias these. */
  --color-status-success: var(--v-success);
  --color-status-warning: var(--v-warning);
  --color-status-error:   var(--v-error);
  --color-status-info:    var(--v-info);
  /* Links: the global `a, .btn-link` rule uses var(--color-brand-secondary, #0071c1)
     and Bootstrap's --bs-link-color; both land at ~3.5:1 blue on charcoal. Route to
     the vibe's light info cyan so every link clears AA. */
  --color-brand-secondary: var(--v-info);
  --bs-link-color:        var(--v-info);
  --bs-link-hover-color:  var(--v-info);
  /* --border-grid is a composite border shorthand hardcoded to a light hairline
     (1px solid #e2e2e2) and never redefined per-theme, so jgn-card / jgn-stat-grid
     top-rules didn't repaint. Route to the token line. */
  --border-grid:          1px solid var(--v-line);
  /* PM-010 reskin coverage: the data-theme vibe blocks were missing the Bootstrap
     border/table bridge that .jgn-variant-b carries, so generic Bootstrap borders
     (--bs-border-color default #dee2e6) and table cells leaked light defaults under
     the dark vibe (the /analytics matrix sub-header, utility-bordered cells). Mirror
     the variant bridge onto the data-theme so the swap repaints them. */
  --bs-border-color:             var(--v-line);
  --bs-border-color-translucent: var(--v-line);
  --bs-table-bg:                 transparent;
  --bs-table-color:              var(--v-ink);
  --bs-table-border-color:       var(--v-line);
  --bs-table-striped-color:      var(--v-ink);
  --bs-table-hover-color:        var(--v-ink);
  --bs-table-active-color:       var(--v-ink);
  /* BU-390: invert strip (.badge.bg-dark and other inverted chrome) was pinned to the
     root default #111/#fff and never repainted per vibe, so on the LIGHT sepia vibe the
     dark badge stayed dark-locked (gate darkChrome). Map the invert pair onto the vibe
     ink/canvas so the strip flips with the skin: a vibe-ink strip on a light vibe
     (changed from #111 -> repaints), a light strip on a dark vibe. */
  --color-invert-bg:             var(--v-ink);
  --color-invert-ink:            var(--v-canvas);
}

/* ============================================================
   BU-800 — the DEFAULT state is a dark cockpit, so its text tokens must be
   light. F-065 made the dark cockpit the default backdrop and koi-shell.js
   paints it (DARK_WATER --v-canvas #0b1216 whenever no explicit vibe is
   chosen), but the --color-* -> --v-* mapping below is guarded
   `[data-theme]:not([data-theme="default"])`, so the default state kept the
   light palette from :root: #1a1a1a ink on a #0b1216 ground = 1.22:1, and
   Bootstrap's own --bs-body-color (#212529) made every bare <h1> and
   .text-muted invisible. Measured on /plan/generator, 2026-08-15: 7 elements
   below AA, worst 1.08:1. This is F-065 round 3's deferred finding #12
   ("11 of 12 findings; #12 is token-layer", d7322025).

   Values are the dark-cockpit set the rebuilt pages had been HARDCODING in
   their own scoped CSS (MediaLibraryPage/AssetLibraryPage/TemplateList) —
   which is the violation this block exists to end: those hardcodes come out
   and route here instead.

   NOT mapped onto --v-ink: koi-shell.js overwrites --v-ink with #06222b
   ("dark koi bodies") in this exact state, so the vibe ink var is not a text
   colour here. That token hijack is its own defect; this block does not
   depend on it.
   ============================================================ */
:root:not([data-theme]),
:root[data-theme="default"] {
  --color-canvas:           #0b1216;
  --color-surface:          #12161c;
  --color-surface-raised:   #171c23;
  --color-surface-sunken:   #0e1319;
  --color-surface-cool:     #12161c;
  --color-surface-cool-2:   #171c23;
  --color-hover-surface:    #1b2129;
  --color-ink:              #f2f5f7;
  --color-ink-dim:          #c7ccd3;
  --color-ink-muted:        #8a929c;
  --color-ink-secondary:    #c7ccd3;
  --color-ink-tertiary:     #8a929c;
  --color-ink-rgb:          242, 245, 247;
  --color-ink-on-brand:     #ffffff;   /* white in every state — brand button fills deepen toward black so white holds */
  --color-surface-rgb:      18, 22, 28;
  --color-line:             #232a33;
  --color-line-strong:      #2e3742;
  --color-line-subtle:      #1b2129;
  --signal-tone:            #ffffff;
  --signal-amt:             44%;

  /* Bootstrap bridge: a bare <h1> takes --bs-body-color / --bs-heading-color,
     not --color-ink, which is why the plan title stayed #212529 dark. */
  --bs-body-bg:             #0b1216;
  --bs-body-color:          #f2f5f7;
  --bs-heading-color:       #f2f5f7;
  --bs-emphasis-color:      #ffffff;
  --bs-secondary-color:     #c7ccd3;
  --bs-secondary-bg:        #171c23;
  --bs-light:               #12161c;
  --bs-light-rgb:           18, 22, 28;
}

/* BU-390: sepia-ledger is the only LIGHT vibe in the shared block above, so the
   on-dark signal-tone (#fff) + pale on-dark text tokens lighten text toward white on
   cream (invisible). Re-point them to AA-on-cream values for this vibe only. The two
   dark vibes (carbon-lime, harbor-night) keep the shared on-dark values.
   SPECIFICITY IDIOM (BU-390, counts corrected BU-665): the generic-vibe mapping block
   above is [data-theme] + 3 :not() = (0,4,0) and ALSO matches sepia-ledger, so a plain
   :root[data-theme="sepia-ledger"] (0,2,0) override LOSES to it. To win, a per-vibe
   token override MUST out-specify the generic chain — mirror it and add :root + the
   theme attribute. This selector keeps a 4th :not(pastel-mondrian) that is now
   redundant (the theme attribute already excludes it) but harmless: it only raises
   this block to (0,6,0), still above the generic (0,4,0). Left in place deliberately —
   BU-665 changed nothing about the already-working vibes.
   Reuse this prefix for any future per-vibe override that overrides a generic-block token. */
:root[data-theme="sepia-ledger"]:not([data-theme="default"]):not([data-theme="instrument-paper"]):not([data-theme="pastel-mondrian"]) {
  /* signal mix darkens toward sepia ink on the light vibe (was #ffffff lighten) so every
     *-text color-mix (status-*-text, indigo-text, vis-highlight-text, compose stage text,
     and the .btn-outline-* color-mix in jgn-components.css) resolves AA-dark on cream. */
  --signal-tone: #241c10;
  --color-tint-indigo-text: #3c3489;
  /* BU-388: colored tokens that are AA as a FILL/badge-bg on cream but FAIL AA as TEXT.
     Add AA-dark sepia TEXT variants WITHOUT touching the bright fill values (--v-accent-2
     #b5532a and --v-warning #b06410 stay correct as badge backgrounds / borders).
       - --color-accent-aa / --color-accent-strong: the accent-2 sienna used AS TEXT
         (.jgn-badge--accent, compose/analytics/media accent labels). #b5532a is 4.60 on
         cream but 4.11 on the raised surface (sub-AA); darker sienna #8a3d1c -> 7.05 cream
         / 6.30 surface, still recognizably sienna. Drives the bulk of /compose contrast=109.
       - --color-tint-warning-text: line 740 of the generic block sets this to raw
         var(--v-warning) #b06410 (3.40 on tinted badge). Darker amber #854F0B -> 4.88 on
         the badge tint / 6.24 on cream, still amber. (Matches the dark-default value.) */
  --color-accent-aa:         #8a3d1c;
  --color-accent-strong:     #8a3d1c;
  --color-tint-warning-text: #854F0B;
}

/* BU-665: pastel-mondrian is now a LIGHT vibe inside the shared mapping above (its
   standalone hardcoded block was deleted). It needs the SAME small light-vibe fixup
   sepia-ledger and gallery-plaster get — nothing more. This is not duplication: it is
   the three tokens the generic block sets for DARK vibes that are illegible on cream.
   Measured on this vibe's surface #fdf6e6 / canvas #f7ead4:
     --signal-tone   generic inherits #ffffff at 44% -> lightens every *-text color-mix
                     toward white on cream. Flip the TONE (keep the 44% amount) to this
                     vibe's darkest ink so signal text DARKENS instead. Same idiom as
                     sepia-ledger #241c10 and gallery-plaster #23201c.
     --color-tint-indigo-text  generic sets #b8c0ff = 1.62:1 on cream (fails AA badly).
                     #3c3489 = 9.4:1 on cream — the sepia-ledger value, same reason.
     --bs-emphasis-color  generic sets #ffffff (correct on dark vibes, invisible here).
                     #1f3344 is pastel-mondrian's own deepened ink, 11.6:1 on cream.
   Everything else the generic block produces was contrast-checked against this vibe and
   clears AA unmodified: accent #b34100 5.30:1, accent-2 #8a5b3d 5.30:1, warning #8f5f18
   5.11:1, info #2f6472 6.12:1, success #4f6b52 5.42:1, error #a6353a 5.11:1, and the
   invert pair #f7ead4-on-#304c61 7.59:1. SPECIFICITY: generic is (0,4,0); this selector
   mirrors its :not() chain plus :root + the theme attr = (0,5,0), so it wins. */
:root[data-theme="pastel-mondrian"]:not([data-theme="default"]):not([data-theme="instrument-paper"]) {
  --signal-tone:            #1f3344;
  --color-tint-indigo-text: #3c3489;
  --bs-emphasis-color:      #1f3344;
}

/* PM-010 reskin coverage: Bootstrap .table-secondary (the /analytics coverage-matrix
   sub-header row) hardcodes its OWN light --bs-table-bg via the table-variant mixin,
   so it ignores the row-level remap above. Override it to a token surface so it
   repaints with the vibe instead of staying near-white. */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .table-secondary {
  --bs-table-bg:           var(--v-surface-sunken);
  --bs-table-color:        var(--v-ink);
  --bs-table-border-color: var(--v-line);
}

/* PM-010 reskin coverage: Bootstrap form controls keep a white --bs-body-bg default,
   so /preferences/profile inputs stayed white under the dark vibe. Route them to token
   surfaces (the .jgn-variant-b bridge does the same for the class variant). */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .form-control,
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .form-select,
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .input-group-text {
  background-color: var(--v-surface);
  color: var(--v-ink);
  border-color: var(--v-line);
}

/* PM-010 reskin coverage: outline/secondary/light buttons keep Bootstrap's fixed
   gray --bs-btn-border-color (#6c757d) which never repaints. Bridge them onto the
   token origin so their chrome reskins with the vibe (also covers the legacy edit
   forms). Solid/brand buttons keep Traffic Orange (signal, theme-invariant). */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-outline-secondary,
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-light,
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-secondary {
  --bs-btn-border-color: var(--v-line);
  --bs-btn-color: var(--v-ink);
  --bs-btn-hover-bg: var(--v-hover);
  --bs-btn-hover-border-color: var(--v-line-strong);
  --bs-btn-hover-color: var(--v-ink);
}
/* Solid secondary/light buttons also carry a fixed Bootstrap fill (#6c757d / #f8f9fa)
   that never repaints. Route the fill to a token surface. */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-secondary,
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-light {
  --bs-btn-bg: var(--v-surface-raised);
  --bs-btn-active-bg: var(--v-hover);
}
/* Bootstrap accordion ships white --bs-accordion-bg / light borders. Bridge it. */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .accordion {
  --bs-accordion-bg: var(--v-surface);
  --bs-accordion-color: var(--v-ink);
  --bs-accordion-border-color: var(--v-line);
  --bs-accordion-btn-bg: var(--v-surface);
  --bs-accordion-btn-color: var(--v-ink);
  --bs-accordion-active-bg: var(--v-surface-raised);
  --bs-accordion-active-color: var(--v-ink);
  --bs-accordion-btn-focus-border-color: var(--v-line);
}
/* Some outline-secondary buttons (e.g. template filter pills) carry a direct
   border-color: #6b7280 that beats the --bs-btn-border-color var. Force the resting
   border to the OPAQUE border token (not the near-transparent --v-line): the swap
   leaves the inherited light value in place when applied to a freshly-loaded
   (data-theme=null) tree, and a transparent target can't out-distance #e2e2e2 in
   the gate's RGB-channel check. #3a424a repaints unambiguously. */
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-outline-secondary:not(:hover):not(.active),
[data-theme]:not([data-theme="default"]):not([data-theme="instrument-paper"]) .btn-secondary:not(:hover):not(.active) {
  border-color: var(--v-border) !important;
}

/* BU-532: gallery-plaster is a LIGHT vibe under the generic mapping (which assumes
   dark and sets a 44% white signal-lighten). Same idiom as sepia-ledger: keep the
   amount, flip the tone to the vibe's ink so signal text DARKENS on plaster. */
:root[data-theme="gallery-plaster"]:not([data-theme="default"]):not([data-theme="instrument-paper"]):not([data-theme="pastel-mondrian"]) {
  --signal-tone: #23201c;
}
