/* ═══════════════════════════════════════════════════════════════════════════
   SRIDIRA CMS · DESIGN TOKENS  (v3.0 "Meridian")
   ───────────────────────────────────────────────────────────────────────────
   The single source of truth for colour, type, space, radius, elevation and
   motion in the INTERNAL CRM. Nothing in this file is a component; nothing in
   a component should be a raw hex value.

   SCOPE — this file is loaded by portal.html and the internal admin pages ONLY.
   index.html (the walk-in kiosk) and the Channel Partner portal keep the
   SRIDIRA green + gold property identity and load theme.css alone. That is a
   deliberate brand decision, not an oversight: the kiosk is marketing, this is
   an instrument panel.

   CONTRAST — every foreground/background pair below was computed against
   WCAG 2.2 AA (4.5:1 body text, 3:1 UI boundaries) before it was written down.
   Where the specified brand hue could not carry text at 4.5:1 — #10B981,
   #F59E0B, #EF4444, #06B6D4 all fail as text on white — the hue is kept as the
   FILL and a darker sibling carries the TEXT. The UI still reads as the brand
   colour; it just does not fail an audit.

   THEMES — [data-theme="light"|"dark"] on <html>. "system" is expressed by
   removing the attribute and letting prefers-color-scheme decide.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light;

  /* ── BRAND ──────────────────────────────────────────────────────────── */
  --primary:            #2563EB;   /* 5.17:1 on white — safe as text AND as a fill */
  --primary-hover:      #1D4ED8;
  --primary-active:     #1E40AF;
  --primary-light:      #DBEAFE;
  --primary-tint:       #EFF6FF;
  --primary-contrast:   #FFFFFF;   /* 5.17:1 on --primary */
  --primary-ring:       rgba(37, 99, 235, .32);

  /* One gradient, used sparingly: dashboard hero, AI header, selected KPI.
     Never on an ordinary button or card. */
  --brand-gradient:     linear-gradient(135deg, #2563EB 0%, #4F46E5 100%);
  --brand-gradient-soft:linear-gradient(135deg, #EFF6FF 0%, #EEF2FF 100%);

  /* ── SURFACES ───────────────────────────────────────────────────────── */
  --background:         #F5F7FB;
  --surface:            #FFFFFF;
  --surface-secondary:  #F8FAFC;
  --surface-tertiary:   #F1F5F9;
  --surface-inverse:    #0F172A;
  /* CHROME — the sidebar, the avatar, the hero KPI. These stay dark in BOTH
     themes: a navigation rail that flips to white in dark mode is not dark
     mode, it is a light sidebar on a dark page. Separate tokens because the
     legacy --green-900 alias served two roles at once (ink AND dark surface)
     and one variable cannot be light-for-text and dark-for-background at the
     same time. Caught in dark-mode visual QA. */
  --chrome:             #0F172A;
  --chrome-2:           #1E293B;
  --chrome-accent:      #2563EB;
  --on-chrome:          #F8FAFC;   /* 16.9:1 on --chrome */
  --on-chrome-muted:    #A9B6C8;   /*  8.7:1 on --chrome */
  --surface-inverse-2:  #1E293B;
  --overlay:            rgba(15, 23, 42, .48);

  /* ── TEXT ───────────────────────────────────────────────────────────── */
  --text-primary:       #0F172A;   /* 17.85:1 on white, 16.65:1 on --background */
  --text-secondary:     #5A6A7F;   /* 5.15:1 on --background — #64748B was 4.44:1 and failed */
  --text-tertiary:      #64748B;   /* 4.76:1 — white surfaces only */
  --text-disabled:      #94A3B8;   /* non-text / decorative only */
  --text-inverse:       #FFFFFF;
  --text-on-inverse-2:  #CBD5E1;   /* 12.02:1 on --surface-inverse */
  --text-link:          #2563EB;

  /* ── BORDERS ────────────────────────────────────────────────────────── */
  --border:             #E2E8F0;   /* decorative dividers, table rules */
  --border-subtle:      #EEF2F7;
  --border-strong:      #748296;   /* 3.91:1 — inputs, checkboxes, any UI boundary */
  --border-focus:       #2563EB;

  /* ── STATUS ─────────────────────────────────────────────────────────────
     -fill  the brand hue, for dots, bars, chart series, left rails
     -text  the accessible sibling, for any glyph or label
     -bg    the tint, always paired with -text                            */
  --success:            #10B981;
  --success-text:       #047857;   /* 5.48:1 on white, 5.21:1 on --success-bg */
  --success-bg:         #ECFDF5;
  --success-border:     #A7F3D0;
  --success-solid:      #047857;   /* white on this = 5.48:1 */

  --warning:            #F59E0B;
  --warning-text:       #B45309;   /* 5.02:1 */
  --warning-bg:         #FFFBEB;
  --warning-border:     #FDE68A;
  --warning-solid:      #B45309;

  --danger:             #EF4444;
  --danger-text:        #B91C1C;   /* 6.47:1 */
  --danger-bg:          #FEF2F2;
  --danger-border:      #FECACA;
  --danger-solid:       #DC2626;   /* white on this = 4.83:1 */

  --info:               #06B6D4;
  --info-text:          #0E7490;   /* 5.36:1 */
  --info-bg:            #ECFEFF;
  --info-border:        #A5F3FC;
  --info-solid:         #0E7490;

  --neutral-bg:         #F1F5F9;
  --neutral-text:       #475569;
  --neutral-border:     #CBD5E1;

  /* ── TYPE ───────────────────────────────────────────────────────────────
     One family. The serif display face belonged to the property brochure;
     an instrument panel wants a single, legible, tabular-capable sans.     */
  --font-sans: "Inter", "Montserrat", -apple-system, BlinkMacSystemFont,
               "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  /* A 9-step scale replaces the 62 ad-hoc font sizes measured in the audit. */
  --text-2xs:   0.6875rem;  /*  11px  micro-labels, table meta            */
  --text-xs:    0.75rem;    /*  12px  captions, badges, column headers    */
  --text-sm:    0.8125rem;  /*  13px  dense table body                    */
  --text-base:  0.875rem;   /*  14px  default UI text                     */
  --text-md:    0.9375rem;  /*  15px  comfortable body, form values       */
  --text-lg:    1.0625rem;  /*  17px  card titles                         */
  --text-xl:    1.25rem;    /*  20px  view headings                       */
  --text-2xl:   1.5rem;     /*  24px  page titles                         */
  --text-3xl:   1.875rem;   /*  30px  KPI values                          */
  --text-4xl:   2.25rem;    /*  36px  hero figures                        */

  --leading-tight:  1.25;
  --leading-snug:   1.4;
  --leading-normal: 1.55;
  --leading-relaxed:1.7;

  --weight-normal:   400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  --tracking-tight: -0.011em;
  --tracking-wide:   0.02em;
  --tracking-caps:   0.06em;

  /* ── SPACE · strict 4px grid (the audit found 55% of declarations off it) */
  --space-0:  0;
  --space-1:  0.25rem;   /*  4px */
  --space-2:  0.5rem;    /*  8px */
  --space-3:  0.75rem;   /* 12px */
  --space-4:  1rem;      /* 16px */
  --space-5:  1.25rem;   /* 20px */
  --space-6:  1.5rem;    /* 24px */
  --space-8:  2rem;      /* 32px */
  --space-10: 2.5rem;    /* 40px */
  --space-12: 3rem;      /* 48px */
  --space-16: 4rem;      /* 64px */
  --space-20: 5rem;      /* 80px */

  /* ── RADIUS · restrained. 18px everywhere read as consumer, not enterprise */
  --radius-xs:   4px;
  --radius-sm:   6px;
  --radius-md:   8px;
  --radius-lg:   10px;   /* cards, panels — the default */
  --radius-xl:   14px;   /* modals, drawers */
  --radius-full: 999px;  /* pills, avatars only */

  /* ── ELEVATION · tight and low-opacity. Enterprise UIs whisper. ──────── */
  --shadow-xs:  0 1px 2px rgba(15, 23, 42, .05);
  --shadow-sm:  0 1px 3px rgba(15, 23, 42, .07), 0 1px 2px rgba(15, 23, 42, .04);
  --shadow-md:  0 4px 12px rgba(15, 23, 42, .08), 0 1px 3px rgba(15, 23, 42, .04);
  --shadow-lg:  0 12px 28px rgba(15, 23, 42, .10), 0 2px 6px rgba(15, 23, 42, .05);
  --shadow-xl:  0 24px 48px rgba(15, 23, 42, .14), 0 4px 10px rgba(15, 23, 42, .06);
  --shadow-focus: 0 0 0 3px var(--primary-ring);

  /* ── MOTION · the brief asks for 150–300ms; the audit found 19 durations */
  --duration-instant: 80ms;
  --duration-fast:   150ms;
  --duration-base:   200ms;
  --duration-slow:   280ms;
  --ease-out:      cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out:   cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring:   cubic-bezier(0.34, 1.4, 0.64, 1);
  --transition-colors: color var(--duration-fast) var(--ease-out),
                       background-color var(--duration-fast) var(--ease-out),
                       border-color var(--duration-fast) var(--ease-out);

  /* ── LAYOUT ─────────────────────────────────────────────────────────── */
  --sidebar-width:            256px;
  --sidebar-width-collapsed:   68px;
  --header-height:             60px;
  --bottomnav-height:          58px;
  --content-max:             1600px;   /* the audit found NO breakpoint above
                                          980px — content stretched forever */
  --touch-target:              44px;   /* WCAG 2.2 AA target size */

  /* ── Z-INDEX · one ladder, no more guessing ─────────────────────────── */
  --z-base: 0;      --z-sticky: 100;  --z-header: 200;  --z-sidebar: 300;
  --z-backdrop: 400; --z-drawer: 500; --z-modal: 600;   --z-popover: 700;
  --z-toast: 800;   --z-tooltip: 900;

  /* ── CHART SERIES · colour-blind-safe order, not a rainbow ──────────── */
  --chart-1: #2563EB; --chart-2: #06B6D4; --chart-3: #8B5CF6;
  --chart-4: #F59E0B; --chart-5: #10B981; --chart-6: #EC4899;
  --chart-7: #64748B; --chart-8: #14B8A6;
  --chart-grid:  #E2E8F0;
  --chart-axis:  #64748B;
}

/* ═══════════════════════════════════════════════════════════════════════════
   DARK THEME
   Not an inversion. Three things change character rather than value:
   · elevation stops being a shadow and becomes a lighter surface + a border,
     because shadows are invisible on #0B1120;
   · the primary lightens to #60A5FA — #2563EB is only 3.43:1 on #111827;
   · status hues lighten, since the dark siblings of a tint do not exist.
   ═══════════════════════════════════════════════════════════════════════════ */
[data-theme="dark"] {
  color-scheme: dark;

  --primary:            #60A5FA;   /* 6.98:1 on --surface */
  --primary-hover:      #93C5FD;
  --primary-active:     #BFDBFE;
  --primary-light:      #1E3A8A;
  --primary-tint:       #172554;
  --primary-contrast:   #0B1120;
  --primary-ring:       rgba(96, 165, 250, .38);
  --brand-gradient:     linear-gradient(135deg, #1D4ED8 0%, #4338CA 100%);
  --brand-gradient-soft:linear-gradient(135deg, #172554 0%, #1E1B4B 100%);

  --background:         #0B1120;
  --surface:            #111827;
  --surface-secondary:  #1E293B;
  --surface-tertiary:   #263344;
  --surface-inverse:    #F8FAFC;
  --chrome:             #070C16;   /* one step below --background so the rail
                                      still reads as chrome against the page */
  --chrome-2:           #111827;
  --chrome-accent:      #60A5FA;
  --on-chrome:          #F1F5F9;
  --on-chrome-muted:    #94A3B8;
  --surface-inverse-2:  #E2E8F0;
  --overlay:            rgba(2, 6, 16, .68);

  --text-primary:       #F1F5F9;   /* 16.0:1 on --surface */
  --text-secondary:     #94A3B8;   /* 6.92:1 */
  --text-tertiary:      #94A3B8;
  --text-disabled:      #64748B;
  --text-inverse:       #0F172A;
  --text-on-inverse-2:  #334155;
  --text-link:          #93C5FD;

  --border:             #24303F;
  --border-subtle:      #1B2534;
  --border-strong:      #64748B;   /* 3.73:1 on --surface */
  --border-focus:       #60A5FA;

  --success:      #34D399;  --success-text: #6EE7B7;
  --success-bg:   #052E2B;  --success-border:#065F46; --success-solid:#047857;
  --warning:      #FBBF24;  --warning-text: #FCD34D;
  --warning-bg:   #2E1F05;  --warning-border:#92400E; --warning-solid:#B45309;
  --danger:       #F87171;  --danger-text:  #FCA5A5;
  --danger-bg:    #2E1111;  --danger-border:#991B1B; --danger-solid:#DC2626;
  --info:         #22D3EE;  --info-text:    #67E8F9;
  --info-bg:      #062B31;  --info-border:  #155E75;  --info-solid:  #0E7490;

  --neutral-bg:     #1E293B;
  --neutral-text:   #CBD5E1;
  --neutral-border: #334155;

  /* Elevation by surface + border, not by shadow. */
  --shadow-xs:  0 0 0 1px rgba(255, 255, 255, .04);
  --shadow-sm:  0 1px 3px rgba(0, 0, 0, .5),  0 0 0 1px rgba(255, 255, 255, .04);
  --shadow-md:  0 4px 14px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .05);
  --shadow-lg:  0 14px 32px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 255, 255, .06);
  --shadow-xl:  0 26px 54px rgba(0, 0, 0, .68),0 0 0 1px rgba(255, 255, 255, .07);

  --chart-1: #60A5FA; --chart-2: #22D3EE; --chart-3: #A78BFA;
  --chart-4: #FBBF24; --chart-5: #34D399; --chart-6: #F472B6;
  --chart-7: #94A3B8; --chart-8: #2DD4BF;
  --chart-grid: #24303F;
  --chart-axis: #94A3B8;
}

/* ═══════════════════════════════════════════════════════════════════════════
   SYSTEM THEME
   "System" is the absence of data-theme. Everything above under
   [data-theme="dark"] is repeated here for prefers-color-scheme, scoped so an
   explicit [data-theme="light"] always wins over the OS preference.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --primary:#60A5FA; --primary-hover:#93C5FD; --primary-active:#BFDBFE;
    --primary-light:#1E3A8A; --primary-tint:#172554; --primary-contrast:#0B1120;
    --primary-ring:rgba(96,165,250,.38);
    --brand-gradient:linear-gradient(135deg,#1D4ED8 0%,#4338CA 100%);
    --brand-gradient-soft:linear-gradient(135deg,#172554 0%,#1E1B4B 100%);
    --background:#0B1120; --surface:#111827; --surface-secondary:#1E293B;
    --chrome:#070C16; --chrome-2:#111827; --chrome-accent:#60A5FA;
    --on-chrome:#F1F5F9; --on-chrome-muted:#94A3B8;
    --surface-tertiary:#263344; --surface-inverse:#F8FAFC; --surface-inverse-2:#E2E8F0;
    --overlay:rgba(2,6,16,.68);
    --text-primary:#F1F5F9; --text-secondary:#94A3B8; --text-tertiary:#94A3B8;
    --text-disabled:#64748B; --text-inverse:#0F172A; --text-on-inverse-2:#334155;
    --text-link:#93C5FD;
    --border:#24303F; --border-subtle:#1B2534; --border-strong:#64748B; --border-focus:#60A5FA;
    --success:#34D399; --success-text:#6EE7B7; --success-bg:#052E2B; --success-border:#065F46;
    --warning:#FBBF24; --warning-text:#FCD34D; --warning-bg:#2E1F05; --warning-border:#92400E;
    --danger:#F87171;  --danger-text:#FCA5A5;  --danger-bg:#2E1111;  --danger-border:#991B1B;
    --info:#22D3EE;    --info-text:#67E8F9;    --info-bg:#062B31;    --info-border:#155E75;
    --neutral-bg:#1E293B; --neutral-text:#CBD5E1; --neutral-border:#334155;
    --shadow-xs:0 0 0 1px rgba(255,255,255,.04);
    --shadow-sm:0 1px 3px rgba(0,0,0,.5),0 0 0 1px rgba(255,255,255,.04);
    --shadow-md:0 4px 14px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.05);
    --shadow-lg:0 14px 32px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.06);
    --shadow-xl:0 26px 54px rgba(0,0,0,.68),0 0 0 1px rgba(255,255,255,.07);
    --chart-1:#60A5FA; --chart-2:#22D3EE; --chart-3:#A78BFA; --chart-4:#FBBF24;
    --chart-5:#34D399; --chart-6:#F472B6; --chart-7:#94A3B8; --chart-8:#2DD4BF;
    --chart-grid:#24303F; --chart-axis:#94A3B8;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LEGACY ALIAS BRIDGE
   ───────────────────────────────────────────────────────────────────────────
   theme.css and portal.css reference the old green/gold names 595 times. Rather
   than rewrite 900 lines of CSS in one risky pass, the old names are redefined
   in terms of the new system. Every existing rule keeps working and instantly
   speaks the new language; call sites are migrated view by view in later
   increments, and this block shrinks as they are.

   The mapping is by ROLE, not by hue — it was derived from how each token is
   actually used, measured across portal.css/theme.css/portal.html:
       --green-900  34 uses (19 colour, 13 background) → ink AND dark surface.
                    #0F172A does both jobs correctly, which is why the alias is
                    safe; a lighter navy would have failed as text.
       --gold-500   23 uses (10 border, 8 colour) → this was the accent → primary.
       --gold-300   14 uses, all colour, all on dark → a light blue at 9.9:1.
       --muted      27 uses, all colour → the AA-safe secondary, not #64748B.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --green-950:  var(--text-primary);
  --green-900:  var(--text-primary);
  --green-800:  var(--surface-inverse-2);
  --green-700:  var(--surface-inverse-2);
  --green-600:  var(--primary);

  --gold-500:   var(--primary);
  --gold-400:   var(--primary-hover);
  --gold-300:   #93C5FD;                 /* 9.90:1 on #0F172A */
  --gold-grad:  var(--brand-gradient);

  --white:      var(--surface);
  --ivory:      var(--background);
  --ink:        var(--text-primary);
  --muted:      var(--text-secondary);
  --line:       var(--border);
  --line-solid: var(--border);

  --danger:     var(--danger-solid);     /* used as text, border AND fill — the
                                            solid tone is the only value that is
                                            legible in all three roles */
  --warn:       var(--warning-text);
  --ok:         var(--success-text);
  --info:       var(--info-text);

  --font-display: var(--font-sans);      /* the serif was the brochure voice */
  --font-body:    var(--font-sans);

  --glass-bg:     var(--surface);        /* the brief rules out heavy glass */
  --glass-border: var(--border);
  --shadow-soft:  var(--shadow-md);
  --shadow-lift:  var(--shadow-lg);
  --radius:       var(--radius-lg);      /* was 18px */
  --ease:         var(--ease-out);
}

[data-theme="dark"] { --gold-300: #93C5FD; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --gold-300: #93C5FD; } }

/* ═══════════════════════════════════════════════════════════════════════════
   CORRECTIVE OVERRIDES
   Four places where the alias bridge alone would produce a legible-but-wrong
   result. Each one is listed with its reason so this block can be deleted rule
   by rule as the call sites are migrated.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. The active nav item painted dark ink on the gold gradient. On a blue
      gradient that is 1.9:1. A tinted rail reads better anyway, and the brief
      asks for "subtle active-state highlighting". */
.nav-item.active {
  background: rgba(96, 165, 250, .16) !important;
  color: #FFFFFF !important;
  font-weight: var(--weight-semibold);
  box-shadow: inset 3px 0 0 var(--primary);
}
.nav-item.active .nav-count,
.nav-item.active .nav-count.gold,
.nav-item.active .nav-count.alert {
  background: rgba(255, 255, 255, .18) !important;
  color: #FFFFFF !important;
}

/* 2. Icons are SVG now (assets/icons.js). The old rule sized a text glyph. */
.nav-item .nav-ic {
  width: 18px; height: 18px; display: inline-flex;
  align-items: center; justify-content: center;
  opacity: .9; flex: 0 0 18px;
}
.nav-item .nav-ic svg { width: 17px; height: 17px; display: block; }
.nav-item.active .nav-ic { opacity: 1; }

/* 3. The audit found no breakpoint above 980px, so content stretched forever
      on 1440/1920/2560 displays. */
.app-main, .view, .view-body { max-width: var(--content-max); }

/* 4. Radius: the 18px default read as consumer software at card size. */
.card, .panel, .data-table, .table-scroll { border-radius: var(--radius-lg); }

/* ═══════════════════════════════════════════════════════════════════════════
   GLOBAL FOUNDATION
   The audit found 3 :focus-visible rules, 2 outline:none, 1 tabindex and no
   skip link across a 19-view keyboard-driven CRM.
   ═══════════════════════════════════════════════════════════════════════════ */

/* One visible, consistent focus ring on everything focusable. :focus-visible
   means a mouse click does not draw it, so this costs the pointer user nothing. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
/* Never remove the indicator without replacing it. */
:where(a, button, input, select, textarea, [tabindex]):focus:not(:focus-visible) {
  outline: none;
}

/* WCAG 2.2 "Target Size (Minimum)" — 24px absolute floor, 44px on touch. */
@media (pointer: coarse) {
  :where(button, [role="button"], a.btn, .nav-item, .mini-btn) {
    min-height: var(--touch-target);
  }
}

/* Keyboard users should not tab through 19 nav items to reach the table. */
.skip-link {
  position: absolute; left: var(--space-2); top: var(--space-2);
  z-index: var(--z-tooltip);
  padding: var(--space-2) var(--space-4);
  background: var(--surface); color: var(--text-primary);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  font: var(--weight-semibold) var(--text-base)/1 var(--font-sans);
  box-shadow: var(--shadow-lg);
  transform: translateY(-200%);
  transition: transform var(--duration-fast) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

/* Selection and scrollbars in the brand, quietly. */
::selection { background: var(--primary-light); color: var(--text-primary); }
* { scrollbar-color: var(--border-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: var(--radius-full);
  border: 2px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); background-clip: content-box; }

/* Theme switching should not animate the entire page. */
html.theme-switching, html.theme-switching * {
  transition: background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out) !important;
}

/* PHASE 26 — honoured globally, not per-component. Motion is decoration here;
   nothing in this CMS conveys meaning through movement alone. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SHELL CONTROLS INTRODUCED BY THE FOUNDATION
   Only the pieces this increment adds. The full component library (Button,
   Card, Table, Modal, Drawer, Tabs, Skeleton, EmptyState …) lands in the next
   increment; nothing here pre-empts it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The header sits on a light surface, so this control is built from surface
   tokens rather than white-alpha. The first draft used rgba(255,255,255,.7) —
   correct on a dark header, invisible on this one. Caught in visual QA. */
.topbar {
  background: var(--surface) !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  border-bottom: 1px solid var(--border) !important;
}

/* Theme switcher — a segmented control, not a single toggle, because "system"
   is a real third choice and a two-state switch cannot express it. */
.theme-switch {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 3px;
  background: var(--surface-tertiary);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
}
.theme-opt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  border: 0; border-radius: var(--radius-full);
  background: transparent; color: var(--text-secondary);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}
.theme-opt:hover { background: var(--surface); color: var(--text-primary); }
.theme-opt.active {
  background: var(--surface); color: var(--primary);
  box-shadow: var(--shadow-xs);
}
.theme-opt svg { display: block; }
@media (max-width: 560px) { .theme-switch { display: none; } }

.user-caret { display: inline-flex; align-items: center; opacity: .65; }
.user-caret svg { display: block; }
.menu-toggle { display: inline-flex; align-items: center; justify-content: center; }
.menu-toggle svg { display: block; }

/* ═══════════════════════════════════════════════════════════════════════════
   TYPOGRAPHY BASELINE
   Applying the scale at the root, so every unmigrated rule inherits sane
   defaults immediately instead of waiting for its own rewrite.
   ═══════════════════════════════════════════════════════════════════════════ */
body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  background: var(--background);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-sans);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
}

/* Numbers in a CRM are read in columns, so they must line up. This one
   declaration is why 1,248 and 9,830 no longer jitter against each other. */
.data-table td, .data-table th,
.kpi-value, .stat-value, .num, [data-numeric] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CONTROL SURFACES — portal-scoped overrides of theme.css
   ───────────────────────────────────────────────────────────────────────────
   theme.css is SHARED with the walk-in kiosk, which keeps the SRIDIRA green +
   gold identity. It cannot be edited without re-skinning a customer-facing
   screen, so the internal CRM overrides it here instead. tokens.css is loaded
   only by the portal, so the kiosk never sees a single rule below.

   Everything in this block was written because dark-mode visual QA showed it
   broken: inputs painted rgba(255,255,255,.88) stayed white on a #0B1120 page,
   the select chevron had #0d2b1e baked into its data-URI, and .mini-btn put
   near-white text on a near-white pill.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Text inputs, selects, textareas ────────────────────────────────────── */
.field input, .field select, .field textarea,
.search-input, .filter-select {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  color: var(--text-primary);
  background: var(--surface);
  border: 1px solid var(--border-strong);   /* 3.91:1 — a UI boundary, not a hairline */
  border-radius: var(--radius-md);
  transition: var(--transition-colors), box-shadow var(--duration-fast) var(--ease-out);
}
.field input::placeholder, .search-input::placeholder, .field textarea::placeholder {
  color: var(--text-tertiary); opacity: 1;   /* Firefox dims placeholders by default */
}
.field input:hover, .field select:hover, .field textarea:hover,
.search-input:hover, .filter-select:hover { border-color: var(--text-secondary); }

.field input:focus, .field select:focus, .field textarea:focus,
.search-input:focus, .filter-select:focus {
  border-color: var(--border-focus);
  box-shadow: var(--shadow-focus);
  outline: none;                              /* replaced, not removed */
}
.field input:disabled, .field select:disabled, .field textarea:disabled {
  background: var(--surface-tertiary);
  color: var(--text-disabled);
  border-color: var(--border);
}
.field input.invalid, .field select.invalid, .field textarea.invalid {
  border-color: var(--danger-solid);
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .18);
}

/* The chevron was a data-URI with #0d2b1e stroked into it — invisible on a dark
   field. currentColor cannot cross into a background-image, so the mark is
   drawn with a border instead and simply inherits the field's text colour. */
.field select, .filter-select { background-image: none !important; padding-right: 38px; }
.select-wrap { position: relative; display: inline-flex; }
.field select, select.filter-select {
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%) !important;
  background-position: right 18px center, right 13px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat, no-repeat;
}

/* ── Small buttons ──────────────────────────────────────────────────────── */
.mini-btn {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: var(--transition-colors);
}
.mini-btn:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-tint); }
.mini-btn.gold {
  background: var(--primary); border-color: var(--primary);
  color: var(--primary-contrast);
}
.mini-btn.gold:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: var(--primary-contrast); }
.mini-btn.danger { color: var(--danger-text); border-color: var(--danger-border); background: var(--surface); }
.mini-btn.danger:hover { background: var(--danger-solid); border-color: var(--danger-solid); color: #fff; }

/* ── Primary button ─────────────────────────────────────────────────────── */
.btn-primary {
  font-family: var(--font-sans);
  background: var(--primary) !important;
  color: var(--primary-contrast) !important;
  border: 1px solid var(--primary) !important;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
  transition: var(--transition-colors), transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}
.btn-primary:hover {
  background: var(--primary-hover) !important; border-color: var(--primary-hover) !important;
  transform: translateY(-1px);                /* was -2px with a 34px shadow */
  box-shadow: var(--shadow-md);
}
.btn-primary:active { transform: translateY(0); background: var(--primary-active) !important; }
.btn-primary::after { display: none; }        /* the sweeping gloss belonged to the brochure */
.btn-primary.danger { background: var(--danger-solid) !important; border-color: var(--danger-solid) !important; }
.btn-primary.danger:hover { background: var(--danger-text) !important; border-color: var(--danger-text) !important; }
.btn-outline {
  background: var(--surface); color: var(--text-primary);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  transition: var(--transition-colors);
}
.btn-outline:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-tint); }

/* ── Chips (radio groups) ───────────────────────────────────────────────── */
.chip span {
  font-family: var(--font-sans);
  color: var(--text-primary);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  transition: var(--transition-colors), box-shadow var(--duration-fast) var(--ease-out);
}
.chip:hover span { border-color: var(--primary); }
.chip input:checked + span {
  background: var(--primary); color: var(--primary-contrast);
  border-color: var(--primary); box-shadow: var(--shadow-sm);
  transform: none;                             /* the 1px lift made grids shimmer */
}
.chip input:focus-visible + span { box-shadow: var(--shadow-focus); }
.check-inline { color: var(--text-primary); }
.check-inline.small { color: var(--text-secondary); }
.check-inline input { accent-color: var(--primary); }
.check-inline.consent { background: var(--success-bg); border: 1px dashed var(--success-border); }
.check-inline.invalid { border-color: var(--danger-solid); background: var(--danger-bg); }
.cond-block { background: var(--surface-secondary); border: 1px solid var(--border); border-radius: var(--radius-lg); }

/* ── Surfaces ───────────────────────────────────────────────────────────── */
.glass, .card, .panel, .login-card {
  background: var(--surface) !important;
  border: 1px solid var(--border);
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  box-shadow: var(--shadow-sm);
}
body { background: var(--background); }
.content { background: var(--background); }

/* ── Tables ─────────────────────────────────────────────────────────────── */
.data-table { background: var(--surface); color: var(--text-primary); }
.data-table th {
  background: var(--surface-secondary);
  color: var(--text-secondary);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  border-bottom: 1px solid var(--border);
}
.data-table td { border-bottom: 1px solid var(--border-subtle); color: var(--text-primary); }
.data-table tbody tr:hover td { background: var(--surface-secondary); }
.data-table .cell-strong { color: var(--text-primary); font-weight: var(--weight-semibold); }
.data-table .cell-sub, .data-table small { color: var(--text-secondary); }
.table-scroll { background: var(--surface); border: 1px solid var(--border); }

/* ── Badges ─────────────────────────────────────────────────────────────── */
.badge {
  font-family: var(--font-sans); font-size: var(--text-2xs);
  font-weight: var(--weight-semibold); border-radius: var(--radius-full);
  border: 1px solid transparent;
}
.badge.green { background: var(--success-bg); color: var(--success-text); border-color: var(--success-border); }
.badge.gold  { background: var(--primary-tint); color: var(--primary); border-color: var(--primary-light); }
.badge.red, .badge.danger { background: var(--danger-bg); color: var(--danger-text); border-color: var(--danger-border); }
.badge.warn, .badge.amber { background: var(--warning-bg); color: var(--warning-text); border-color: var(--warning-border); }
.badge.blue, .badge.info  { background: var(--info-bg); color: var(--info-text); border-color: var(--info-border); }
.badge.grey, .badge.muted { background: var(--neutral-bg); color: var(--neutral-text); border-color: var(--neutral-border); }

/* ── View headings ──────────────────────────────────────────────────────── */
.view-head h2 { font-family: var(--font-sans); font-size: var(--text-2xl); font-weight: var(--weight-bold); }
.view-sub { color: var(--text-secondary); font-size: var(--text-base); }
.kpi-val { font-family: var(--font-sans); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.login-title { font-family: var(--font-sans); font-weight: var(--weight-bold); }

/* ═══════════════════════════════════════════════════════════════════════════
   OVERFLOW CONTAINMENT
   Found by the responsive sweep: at 320px the admin dashboard pushed the whole
   page 16px sideways. The cause is the default `min-width: auto` on a grid/flex
   item — a chart canvas refuses to shrink below its intrinsic width and drags
   the document with it. Phase 36 does not allow horizontal page scroll at any
   width, so this is contained globally rather than patched per panel.
   ═══════════════════════════════════════════════════════════════════════════ */
.panel, .card, .kpi, .view, .view-body, .grid > *, .cards > * { min-width: 0; }
canvas { max-width: 100% !important; height: auto; }
.panel.wide { max-width: 100%; overflow: hidden; }
img, svg, video { max-width: 100%; }
/* A last line of defence: nothing should ever scroll the document sideways. */
html, body { overflow-x: hidden; }
/* Long unbroken strings wrap rather than widen their container — but NOT in
   table cells. Mobile QA at 390px showed `anywhere` breaking a timestamp into
   "04:2 / 8 PM" and a phone number across two lines, which is worse than the
   horizontal scroll the wrapper already provides. Data cells keep their values
   intact and scroll; prose wraps. */
.view-sub, .callout, .ai-bubble, .ud-head { overflow-wrap: anywhere; }
.data-table td, .data-table th { overflow-wrap: normal; }
.data-table td.wrap, .data-table td[data-wrap] { overflow-wrap: anywhere; }
/* Values that must never be split across lines. */
.data-table td:first-child, .data-table .num, .data-table [data-numeric],
.data-table .cell-time, .data-table .cell-mobile { white-space: nowrap; }

/* The 16px was here: `minmax(320px, 1fr)` forces a 320px track even when the
   content box is 288px, so the grid — and the document — grew sideways. The
   min() form keeps the identical desktop behaviour and simply lets the track
   collapse when the viewport is narrower than the minimum. */
.panel-grid, .kpi-grid, .cards, .card-grid {
  grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE DUAL-ROLE TRAP — every remaining case, fixed together
   ───────────────────────────────────────────────────────────────────────────
   theme.css paints eleven BACKGROUNDS with what the alias bridge has to treat
   as INK (--green-900 / --gold-300 / --green-950). A single variable cannot be
   dark-for-a-background and light-for-text in the same theme, so in dark mode
   each of these became light-on-light or dark-on-dark.

   The computed-contrast sweep caught three of them at 1.65:1. Rather than fix
   the three that happened to be on screen, all eleven are corrected here —
   the other eight are on modals, toasts and hover states the sweep could not
   reach, and they would have failed identically the moment a user opened one.

   theme.css itself stays untouched: it is shared with the kiosk.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The monogram: a filled brand tile, legible in both themes. */
.brand-mark, .brand-mark.sm {
  background: var(--primary) !important;
  color: var(--primary-contrast) !important;   /* 5.17:1 light · 7.41:1 dark */
  border: 1px solid transparent !important;
  box-shadow: var(--shadow-xs);
  font-family: var(--font-sans);
}
/* The chip that says a record is verified and locked. */
.badge.locked { background: var(--chrome) !important; color: var(--on-chrome) !important; }   /* 17.06:1 */
/* The red count on a sidebar item. White is the only value legible on both
   #DC2626 (light) and #F87171 (dark) — the previous --surface flipped dark. */
.nav-count.alert { background: var(--danger-solid) !important; color: #FFFFFF !important; }
.nav-count.gold  { background: var(--primary) !important; color: var(--primary-contrast) !important; }
.nav-item .nav-count { background: rgba(255,255,255,.14); color: var(--on-chrome); }
.nav-item.active .nav-count { background: rgba(255,255,255,.22) !important; color: #FFFFFF !important; }
/* Outline button hover, selected chip, toast, and the two remaining
   gold-gradient fills. */
.btn-outline:hover { background: var(--primary-tint) !important; color: var(--primary) !important; }
#toast    { background: var(--chrome) !important; color: var(--on-chrome) !important;
            border: 1px solid var(--chrome-2); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); }
#toast.ok { background: var(--success-solid) !important; color: #FFFFFF !important; border-color: transparent; }
#toast.err, #toast.error { background: var(--danger-solid) !important; color: #FFFFFF !important; border-color: transparent; }
.badge.gold { background: var(--primary-tint) !important; color: var(--primary) !important;
              border: 1px solid var(--primary-light) !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   OFF-CANVAS SIDEBAR — KEYBOARD CORRECTNESS
   Below 900px the sidebar is pushed off screen with translateX(-100%). A
   transform moves pixels, not focus: all 19 nav buttons stayed in the tab
   order while invisible, so a keyboard or screen-reader user on a phone tabbed
   through nineteen controls they could not see before reaching the page.

   Found when the functional smoke test could not click a nav item at 390px —
   Playwright reported "element is outside of the viewport", which is precisely
   what a sighted keyboard user experiences as focus vanishing.

   `visibility: hidden` removes it from the accessibility tree and the tab
   order, and unlike `display:none` it still animates.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .sidebar {
    visibility: hidden;
    transition: transform var(--duration-slow) var(--ease-out),
                visibility 0s linear var(--duration-slow);
  }
  .sidebar.open {
    visibility: visible;
    transition: transform var(--duration-slow) var(--ease-out),
                visibility 0s linear 0s;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SHELL  (v3.1 · Increment B)
   Collapsible rail · bottom navigation · global search · view transitions.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── View transitions · PHASE 26 ──────────────────────────────────────────
   One entrance, 200ms, 4px of travel. The global prefers-reduced-motion rule
   in this file already collapses it to nothing for those who asked. */
.view.active { animation: cms-view-in var(--duration-base) var(--ease-out); }
@keyframes cms-view-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* ── Collapsible sidebar (desktop only) ─────────────────────────────────── */
.sidebar { transition: width var(--duration-base) var(--ease-out); }
.nav-item { position: relative; }
.nav-collapse-btn {
  margin-top: 8px; display: none;
  align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: 36px; padding: 8px;
  border: 1px solid rgba(255, 255, 255, .12); border-radius: var(--radius-md);
  background: rgba(255, 255, 255, .06); color: var(--on-chrome-muted);
  font: var(--weight-medium) var(--text-xs)/1 var(--font-sans);
  letter-spacing: var(--tracking-wide); cursor: pointer;
  transition: var(--transition-colors);
}
.nav-collapse-btn:hover { background: rgba(255, 255, 255, .12); color: var(--on-chrome); }
.nav-collapse-btn svg { flex-shrink: 0; }

@media (min-width: 901px) {
  .nav-collapse-btn { display: inline-flex; }
  body.nav-collapsed { --side-w: 68px; }
  body.nav-collapsed .sidebar { padding: 16px 10px; overflow-x: hidden; }
  body.nav-collapsed .nav-item { justify-content: center; gap: 0; padding: 11px 0; }
  body.nav-collapsed .nav-item > span:not([class]) { display: none; }   /* the label */
  body.nav-collapsed .nav-item .nav-count {
    position: absolute; top: 2px; right: 4px; margin: 0;
    font-size: .58rem; padding: 1px 5px;
  }
  /* Group headings become dividers: a 10px caption cannot survive a 48px rail,
     but the grouping itself still should. */
  body.nav-collapsed .nav-group {
    font-size: 0; padding: 6px 8px;
    border-top: 1px solid rgba(255, 255, 255, .10); margin-top: 6px;
  }
  body.nav-collapsed .nav-group:first-child { border-top: 0; margin-top: 0; }
  body.nav-collapsed .sidebar-foot span { display: none; }
}

/* The shared, fixed-position tooltip: the sidebar is a scrollbox (overflow-y:
   auto clips x too), so anything positioned inside it would be cut at the
   rail's right edge. */
.nav-tip {
  position: fixed; z-index: var(--z-tooltip);
  transform: translateY(-50%);
  padding: 6px 10px; border-radius: var(--radius-sm);
  background: var(--chrome); color: var(--on-chrome);
  border: 1px solid rgba(255, 255, 255, .14);
  font: var(--weight-medium) var(--text-xs)/1.2 var(--font-sans);
  box-shadow: var(--shadow-md);
  opacity: 0; pointer-events: none; white-space: nowrap;
  transition: opacity var(--duration-fast) var(--ease-out);
}
.nav-tip.show { opacity: 1; }

/* ── Header search trigger ──────────────────────────────────────────────── */
.topbar-search {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 36px; padding: 7px 12px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--surface); color: var(--text-secondary);
  font: var(--weight-normal) var(--text-sm)/1 var(--font-sans);
  cursor: pointer; transition: var(--transition-colors);
}
.topbar-search:hover { border-color: var(--primary); color: var(--primary); }
.topbar-search svg { flex-shrink: 0; }
.gsb-kbd {
  font: var(--weight-medium) var(--text-2xs)/1 var(--font-mono);
  padding: 3px 6px; border-radius: var(--radius-xs);
  background: var(--surface-tertiary); border: 1px solid var(--border);
  color: var(--text-secondary);
}
@media (max-width: 768px) {
  .topbar-search { padding: 7px 9px; }
  .gsb-label, .gsb-kbd { display: none; }
}

/* ── Global search palette ──────────────────────────────────────────────── */
body.cmdk-lock { overflow: hidden; }
.cmdk { position: fixed; inset: 0; z-index: var(--z-modal); }
.cmdk-backdrop { position: absolute; inset: 0; background: var(--overlay); }
.cmdk-panel {
  position: relative; margin: clamp(48px, 12vh, 120px) auto 0;
  width: min(620px, calc(100vw - 32px));
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: cms-cmdk-in var(--duration-fast) var(--ease-out);
}
@keyframes cms-cmdk-in {
  from { opacity: 0; transform: translateY(-6px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.cmdk-input-row {
  display: flex; align-items: center; gap: 10px;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
#cmdkInput {
  flex: 1; border: 0; outline: none; background: transparent;
  font: var(--weight-normal) var(--text-md)/1.4 var(--font-sans);
  color: var(--text-primary);
}
#cmdkInput::placeholder { color: var(--text-tertiary); }
.cmdk-esc {
  border: 1px solid var(--border); border-radius: var(--radius-xs);
  background: var(--surface-secondary); color: var(--text-secondary);
  font: var(--weight-medium) var(--text-2xs)/1 var(--font-mono);
  padding: 4px 7px; cursor: pointer;
}
.cmdk-results { max-height: min(46vh, 420px); overflow-y: auto; padding: var(--space-2); }
.cmdk-group {
  font: var(--weight-semibold) var(--text-2xs)/1 var(--font-sans);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--text-secondary); padding: var(--space-3) var(--space-3) var(--space-2);
}
.cmdk-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: var(--space-2) var(--space-3); min-height: 44px;
  border: 0; border-radius: var(--radius-md);
  background: transparent; color: var(--text-primary);
  font: var(--weight-normal) var(--text-base)/1.35 var(--font-sans);
  text-align: left; cursor: pointer;
}
.cmdk-item.selected { background: var(--primary-tint); color: var(--primary); }
.cmdk-item.selected .cmdk-meta, .cmdk-item.selected small { color: var(--primary); opacity: .8; }
.cmdk-ic { display: inline-flex; width: 18px; color: var(--text-secondary); flex: 0 0 18px; }
.cmdk-item.selected .cmdk-ic { color: var(--primary); }
.cmdk-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.cmdk-main small { font-size: var(--text-xs); color: var(--text-secondary); }
.cmdk-main, .cmdk-main small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk-meta { font-size: var(--text-xs); color: var(--text-tertiary); flex-shrink: 0; }
.cmdk-empty { padding: var(--space-4); color: var(--text-secondary); font-size: var(--text-sm); }
.cmdk-spin {
  display: inline-block; width: 12px; height: 12px; vertical-align: -1px;
  border: 2px solid var(--border); border-top-color: var(--primary);
  border-radius: var(--radius-full); animation: cms-spin .7s linear infinite;
}
@keyframes cms-spin { to { transform: rotate(360deg); } }
.cmdk-foot {
  display: flex; gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border);
  font-size: var(--text-2xs); color: var(--text-secondary);
  background: var(--surface-secondary);
}
.cmdk-foot kbd {
  font: var(--weight-medium) var(--text-2xs)/1 var(--font-mono);
  padding: 2px 5px; margin-right: 3px;
  border: 1px solid var(--border); border-radius: var(--radius-xs);
  background: var(--surface);
}

/* ── Bottom navigation (phones) · PHASE 8 ───────────────────────────────── */
.bottom-nav { display: none; }
@media (max-width: 768px) {
  .bottom-nav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0;
    z-index: var(--z-sidebar);
    height: calc(var(--bottomnav-height) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--surface); border-top: 1px solid var(--border);
    box-shadow: 0 -2px 12px rgba(15, 23, 42, .06);
  }
  .bottom-nav[hidden] { display: none; }
  .bn-item {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 3px; min-height: var(--touch-target);
    border: 0; background: none; cursor: pointer;
    color: var(--text-secondary); padding: 6px 2px 5px;
  }
  .bn-item .bn-ic { display: inline-flex; }
  .bn-item .bn-ic svg { width: 21px; height: 21px; }
  .bn-label {
    font: var(--weight-medium) var(--text-2xs)/1 var(--font-sans);
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .bn-item.active { color: var(--primary); }
  .bn-item.active .bn-label { font-weight: var(--weight-semibold); }

  /* Room for the bar: content, the AI launcher and the toast all sat exactly
     where the bar now lives. */
  .content { padding-bottom: calc(var(--bottomnav-height) + env(safe-area-inset-bottom, 0px) + 24px); }
  .ai-fab { bottom: calc(var(--bottomnav-height) + env(safe-area-inset-bottom, 0px) + 14px) !important; }
  #toast  { bottom: calc(var(--bottomnav-height) + env(safe-area-inset-bottom, 0px) + 16px) !important; }
  /* The off-canvas drawer must clear the bar so More's menu is fully usable. */
  .sidebar { z-index: calc(var(--z-sidebar) + 2); }
  .sidebar-backdrop { z-index: calc(var(--z-sidebar) + 1); }
}

/* ── AI launcher — same dual-role fix as the rest of the chrome ──────────
   Its gradient was built from --green-900/--green-700, which the alias bridge
   maps to INK: correct in light mode, light-on-light in dark mode. Spotted in
   the dark-mode screenshots from Increment A's QA. */
.ai-fab {
  background: var(--chrome) !important;
  color: var(--on-chrome) !important;
  border: 1px solid var(--chrome-2) !important;
}
