/* PublonCore UI — hyper-param design system. KEEP MINIMAL (≤1,000 LOC).
 * See PublonCore.md → "Keeping css / ui / bindings / services lean".
 * Layers: 1 hp roots · 2 derived tokens · 3 dark · 4 reset · 5 utilities
 *         · 6 component skins · 7 themes · 8 responsive + animations */

/* ---- 1. Hyper-parameters — font stacks + every dial. */
:root {
    /* Sans-serif stacks */
    --hp-font-stack-system:    ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --hp-font-stack-inter:     "Inter", "Inter var", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --hp-font-stack-manrope:   "Manrope", "Inter", ui-sans-serif, system-ui, sans-serif;
    --hp-font-stack-dmsans:    "DM Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
    --hp-font-stack-grotesk:   "Space Grotesk", "Söhne", "HK Grotesk", "Inter", ui-sans-serif, sans-serif;
    --hp-font-stack-ibmplex:   "IBM Plex Sans", "Helvetica Neue", system-ui, sans-serif;
    --hp-font-stack-jakarta:   "Plus Jakarta Sans", "Inter", system-ui, sans-serif;
    --hp-font-stack-public:    "Public Sans", "Inter", system-ui, sans-serif;
    --hp-font-stack-albert:    "Albert Sans", "Inter", system-ui, sans-serif;
    --hp-font-stack-outfit:    "Outfit", "Inter", system-ui, sans-serif;
    --hp-font-stack-work:      "Work Sans", "Inter", system-ui, sans-serif;
    --hp-font-stack-roboto:    "Roboto", "Helvetica Neue", system-ui, sans-serif;
    --hp-font-stack-helvetica: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
    --hp-font-stack-humanist:  "Optima", "Lucida Sans", "Trebuchet MS", "Verdana", "Tahoma", system-ui, sans-serif;
    --hp-font-stack-geometric: "Avenir Next", "Avenir", "Futura", "Century Gothic", "URW Gothic", system-ui, sans-serif;
    --hp-font-stack-rounded:   "SF Pro Rounded", "Hiragino Maru Gothic ProN", -apple-system, ui-rounded, system-ui, sans-serif;
    --hp-font-stack-nunito:    "Nunito", "Quicksand", "Comfortaa", system-ui, sans-serif;
    --hp-font-stack-comfortaa: "Comfortaa", "Quicksand", "Nunito", system-ui, sans-serif;
    --hp-font-stack-quicksand: "Quicksand", "Nunito", system-ui, sans-serif;
    --hp-font-stack-condensed: "Roboto Condensed", "Archivo Narrow", "Helvetica Neue Condensed", "Arial Narrow", sans-serif;
    --hp-font-stack-narrow:    "Archivo Narrow", "Roboto Condensed", "Arial Narrow", sans-serif;
    --hp-font-stack-oswald:    "Oswald", "Bebas Neue", "Archivo Narrow", "Impact", sans-serif;
    --hp-font-stack-bebas:     "Bebas Neue", "Oswald", "Impact", sans-serif;
    --hp-font-stack-display:   "SF Pro Display", "Inter", "Helvetica Neue", system-ui, sans-serif;

    /* Serif stacks */
    --hp-font-stack-serif:     ui-serif, "New York", "Iowan Old Style", "Charter", Georgia, "Times New Roman", serif;
    --hp-font-stack-ibmplexs:  "IBM Plex Serif", "Source Serif Pro", Georgia, serif;
    --hp-font-stack-source:    "Source Serif 4", "Source Serif Pro", Georgia, serif;
    --hp-font-stack-georgia:   Georgia, "Iowan Old Style", "Times New Roman", Times, serif;
    --hp-font-stack-times:     "Times New Roman", Times, "Liberation Serif", serif;
    --hp-font-stack-garamond:  "EB Garamond", "Garamond", "Hoefler Text", "Palatino", serif;
    --hp-font-stack-cormorant: "Cormorant Garamond", "EB Garamond", "Hoefler Text", serif;
    --hp-font-stack-caslon:    "Libre Caslon Text", "Big Caslon", "Hoefler Text", serif;
    --hp-font-stack-palatino:  "Palatino", "Palatino Linotype", "Book Antiqua", "URW Palladio L", serif;
    --hp-font-stack-merri:     "Merriweather", "Source Serif Pro", Georgia, serif;
    --hp-font-stack-lora:      "Lora", "Merriweather", Georgia, serif;
    --hp-font-stack-playfair:  "Playfair Display", "Source Serif Pro", "Times New Roman", serif;
    --hp-font-stack-slab:      "Roboto Slab", "Rockwell", Georgia, serif;

    /* Handwritten / display */
    --hp-font-stack-caveat:    "Caveat", "Brush Script MT", cursive;
    --hp-font-stack-kalam:     "Kalam", "Caveat", "Brush Script MT", cursive;

    /* Monospace stacks */
    --hp-font-stack-mono:      ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    --hp-font-stack-jetbrains: "JetBrains Mono", "Fira Code", ui-monospace, "SF Mono", Menlo, monospace;
    --hp-font-stack-fira:      "Fira Code", "Source Code Pro", ui-monospace, Menlo, monospace;
    --hp-font-stack-ibmmono:   "IBM Plex Mono", "JetBrains Mono", ui-monospace, Menlo, monospace;
    --hp-font-stack-source-code: "Source Code Pro", "Fira Code", ui-monospace, Menlo, monospace;
    --hp-font-stack-roboto-mono: "Roboto Mono", "JetBrains Mono", ui-monospace, Menlo, monospace;
    --hp-font-stack-courier:   "Courier New", Courier, "Liberation Mono", monospace;

    /* Defaults */
    --hp-font-body:    var(--hp-font-stack-dmsans);
    --hp-font-heading: var(--hp-font-stack-outfit);
    --hp-font-mono:    var(--hp-font-stack-mono);
    --hp-weight-body:    300;
    --hp-weight-heading: 600;
    --hp-text-scale: 0.82;        /* multiplies every text size */
    --hp-text-ratio: 1.18;        /* type scale (modular) */
    --hp-line-height: 1.5;
    --hp-density: 1;              /* spacing multiplier */
    --hp-pad-base: 0.5rem;        /* base padding unit */
    --hp-margin-base: 0.5rem;     /* base margin unit */
    --hp-search-input-max-w: 14rem; /* sensible cap for short queries (~12 chars) */
    --hp-modal-max-w: 600px;        /* modal max width (clamped by 90vw at runtime) */
    --hp-drawer-w: 420px;           /* drawer width (clamped by 90vw at runtime) */
    --hp-avatar-size: 2.4rem;       /* default avatar circle */
    --hp-active-marker-w: 3px;      /* left/top stripe on selected list/card rows */
    --hp-tab-underline-w: 2px;      /* tab bottom border (active + transparent reserve) */
    --hp-on-primary-muted: rgba(255,255,255,0.92);   /* subtitle / inactive text on dark surface — WCAG AA: ≥0.9 keeps small text ≥4.5:1 on the darkened primary */
    --hp-on-primary-bg-soft: rgba(255,255,255,0.10); /* hover overlay on dark surface */
    --hp-on-primary-bg-strong: rgba(255,255,255,0.18); /* active overlay / brand-mark on dark */
    --hp-on-primary-divider: rgba(255,255,255,0.25); /* subtle 1px line on dark surface */
    --hp-corner-scale: 0;
    --hp-border-scale: 0.4;
    --hp-shadow-intensity: 0.12;
    --hp-transition-speed: 1;
    --hp-hue-1: 205; --hp-sat-1: 88%; --hp-l-shift-1: 0%;
    --hp-hue-2: 35;  --hp-sat-2: 78%; --hp-l-shift-2: 0%;
    --hp-hue-accent: 350; --hp-sat-accent: 78%; --hp-l-shift-accent: 0%;
    --hp-hue-danger:  4;   --hp-sat-danger:  82%;
    --hp-hue-warn:    32;  --hp-sat-warn:    94%;
    --hp-hue-success: 142; --hp-sat-success: 72%;
    --hp-hue-info:    200; --hp-sat-info:    82%;
    --hp-l-bg: 100%; --hp-l-fg: 12%; --hp-l-muted: 40%; --hp-l-border: 88%;
    --hp-l-elev: 99%; --hp-l-sunken: 96%;
}

/* ---- 2. Derived tokens — redeclared on every theme anchor so child
 * overrides of --hp-* re-derive --ui-* tokens (substitution freezes at
 * the declaration site). See ./README.md §Hyper-parameter system. */
:root, [class*="ui-theme-"], [data-theme] {
    --ui-font-body:    var(--hp-font-body);
    --ui-font-heading: var(--hp-font-heading);
    --ui-font-mono:    var(--hp-font-mono);

    --ui-text-2xs: calc(0.65rem  * var(--hp-text-scale));
    --ui-text-xs:  calc(0.75rem  * var(--hp-text-scale));
    --ui-text-sm:  calc(0.875rem * var(--hp-text-scale));
    --ui-text-base:calc(1rem     * var(--hp-text-scale));
    --ui-text-lg:  calc(1rem     * var(--hp-text-scale) * var(--hp-text-ratio));
    --ui-text-xl:  calc(var(--ui-text-lg)  * var(--hp-text-ratio));
    --ui-text-2xl: calc(var(--ui-text-xl)  * var(--hp-text-ratio));
    --ui-text-3xl: calc(var(--ui-text-2xl) * var(--hp-text-ratio));
    --ui-text-4xl: calc(var(--ui-text-3xl) * var(--hp-text-ratio));

    --ui-font-thin:       100;
    --ui-font-extralight: 200;
    --ui-font-light:      300;
    --ui-font-normal:     400;
    --ui-font-medium:     500;
    --ui-font-semibold:   600;
    --ui-font-bold:       700;
    --ui-font-extrabold:  800;
    --ui-font-black:      900;
    --ui-weight-body:    var(--hp-weight-body);
    --ui-weight-heading: var(--hp-weight-heading);

    --ui-line-height: var(--hp-line-height);

    /* Spacing scale — multiples of pad-base × density */
    --ui-space-0: 0;
    --ui-space-1: calc(var(--hp-pad-base) * 0.5  * var(--hp-density));
    --ui-space-2: calc(var(--hp-pad-base) * 1    * var(--hp-density));
    --ui-space-3: calc(var(--hp-pad-base) * 1.5  * var(--hp-density));
    --ui-space-4: calc(var(--hp-pad-base) * 2    * var(--hp-density));
    --ui-space-5: calc(var(--hp-pad-base) * 2.5  * var(--hp-density));
    --ui-space-6: calc(var(--hp-pad-base) * 3    * var(--hp-density));
    --ui-space-7: calc(var(--hp-pad-base) * 4    * var(--hp-density));
    --ui-space-8: calc(var(--hp-pad-base) * 5    * var(--hp-density));
    --ui-space-10:calc(var(--hp-pad-base) * 6    * var(--hp-density));
    --ui-search-input-max-w: var(--hp-search-input-max-w);
    --ui-modal-max-w: var(--hp-modal-max-w);
    --ui-drawer-w: var(--hp-drawer-w);
    --ui-avatar-size: var(--hp-avatar-size);
    --ui-active-marker-w: var(--hp-active-marker-w);
    --ui-tab-underline-w: var(--hp-tab-underline-w);
    --ui-on-primary-muted: var(--hp-on-primary-muted);
    --ui-on-primary-bg-soft: var(--hp-on-primary-bg-soft);
    --ui-on-primary-bg-strong: var(--hp-on-primary-bg-strong);
    --ui-on-primary-divider: var(--hp-on-primary-divider);

    --ui-radius-none: 0;
    --ui-radius-sm:  calc(0.25rem  * var(--hp-corner-scale));
    --ui-radius-md:  calc(0.375rem * var(--hp-corner-scale));
    --ui-radius-lg:  calc(0.5rem   * var(--hp-corner-scale));
    --ui-radius-xl:  calc(0.75rem  * var(--hp-corner-scale));
    --ui-radius-2xl: calc(1rem     * var(--hp-corner-scale));
    --ui-radius-full: 9999px;

    --ui-border-width: calc(1px * var(--hp-border-scale));

    --ui-shadow-none: none;
    --ui-shadow-sm:  0 1px 2px rgba(15,23,42, calc(var(--hp-shadow-intensity) * 1.5));
    --ui-shadow-md:  0 4px 8px rgba(15,23,42, calc(var(--hp-shadow-intensity) * 2));
    --ui-shadow-lg:  0 8px 22px rgba(15,23,42, calc(var(--hp-shadow-intensity) * 2.5));
    --ui-shadow-xl:  0 18px 40px rgba(15,23,42, calc(var(--hp-shadow-intensity) * 3));
    --ui-shadow-inner: inset 0 1px 2px rgba(15,23,42, calc(var(--hp-shadow-intensity) * 2));

    --ui-transition-fast:  calc(120ms * var(--hp-transition-speed));
    --ui-transition-base:  calc(200ms * var(--hp-transition-speed));
    --ui-transition-slow:  calc(380ms * var(--hp-transition-speed));
    --ui-ease: cubic-bezier(0.4, 0, 0.2, 1);

    /* Colour scales — derived from HSL hyper-params + per-color lightness shift */
    --ui-primary-50:  hsl(var(--hp-hue-1), var(--hp-sat-1), calc(97% + var(--hp-l-shift-1)));
    --ui-primary-100: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(92% + var(--hp-l-shift-1)));
    --ui-primary-200: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(83% + var(--hp-l-shift-1)));
    --ui-primary-300: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(70% + var(--hp-l-shift-1)));
    --ui-primary-400: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(60% + var(--hp-l-shift-1)));
    --ui-primary-500: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(52% + var(--hp-l-shift-1)));
    --ui-primary-600: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(45% + var(--hp-l-shift-1)));
    --ui-primary-700: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(38% + var(--hp-l-shift-1)));
    --ui-primary-800: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(28% + var(--hp-l-shift-1)));
    --ui-primary-900: hsl(var(--hp-hue-1), var(--hp-sat-1), calc(18% + var(--hp-l-shift-1)));
    --ui-primary: var(--ui-primary-600);
    --ui-on-primary: #fff;

    --ui-secondary-100: hsl(var(--hp-hue-2), var(--hp-sat-2), calc(92% + var(--hp-l-shift-2)));
    --ui-secondary-500: hsl(var(--hp-hue-2), var(--hp-sat-2), calc(52% + var(--hp-l-shift-2)));
    --ui-secondary-600: hsl(var(--hp-hue-2), var(--hp-sat-2), calc(45% + var(--hp-l-shift-2)));
    --ui-secondary-700: hsl(var(--hp-hue-2), var(--hp-sat-2), calc(38% + var(--hp-l-shift-2)));
    --ui-secondary: var(--ui-secondary-600);
    --ui-on-secondary: #fff;

    --ui-accent-500: hsl(var(--hp-hue-accent), var(--hp-sat-accent), calc(50% + var(--hp-l-shift-accent)));
    --ui-accent-600: hsl(var(--hp-hue-accent), var(--hp-sat-accent), calc(42% + var(--hp-l-shift-accent)));
    --ui-accent: var(--ui-accent-600);

    --ui-danger-100: hsl(var(--hp-hue-danger), var(--hp-sat-danger), 95%);
    --ui-danger-500: hsl(var(--hp-hue-danger), var(--hp-sat-danger), 50%);
    --ui-danger-600: hsl(var(--hp-hue-danger), var(--hp-sat-danger), 45%);
    --ui-danger: var(--ui-danger-600);
    --ui-warn-100:    hsl(var(--hp-hue-warn), var(--hp-sat-warn), 95%);
    --ui-warn-500:    hsl(var(--hp-hue-warn), var(--hp-sat-warn), 50%);
    /* WCAG AA: white text on these as pill/badge backgrounds needs ≥4.5:1, so
       the solid tones are darkened (warn 45→36, success 38→32). */
    --ui-warn:        hsl(var(--hp-hue-warn), var(--hp-sat-warn), 36%);
    --ui-success-100: hsl(var(--hp-hue-success), var(--hp-sat-success), 94%);
    --ui-success-500: hsl(var(--hp-hue-success), var(--hp-sat-success), 42%);
    --ui-success:     hsl(var(--hp-hue-success), var(--hp-sat-success), 29%);
    --ui-info-100:    hsl(var(--hp-hue-info), var(--hp-sat-info), 94%);
    --ui-info-500:    hsl(var(--hp-hue-info), var(--hp-sat-info), 50%);
    --ui-info:        hsl(var(--hp-hue-info), var(--hp-sat-info), 42%);

    /* Greys derived from primary hue (low sat) */
    --ui-gray-50:  hsl(var(--hp-hue-1), 12%, 98%);
    --ui-gray-100: hsl(var(--hp-hue-1), 12%, 96%);
    --ui-gray-200: hsl(var(--hp-hue-1), 10%, 90%);
    --ui-gray-300: hsl(var(--hp-hue-1), 10%, 82%);
    --ui-gray-400: hsl(var(--hp-hue-1), 8%,  64%);
    --ui-gray-500: hsl(var(--hp-hue-1), 8%,  48%);
    --ui-gray-600: hsl(var(--hp-hue-1), 8%,  36%);
    --ui-gray-700: hsl(var(--hp-hue-1), 8%,  26%);
    --ui-gray-800: hsl(var(--hp-hue-1), 8%,  18%);
    --ui-gray-900: hsl(var(--hp-hue-1), 8%,  10%);

    --ui-bg:       hsl(var(--hp-hue-1), 12%, var(--hp-l-bg));
    --ui-bg-elev:  hsl(var(--hp-hue-1), 12%, var(--hp-l-elev));
    --ui-bg-sunken:hsl(var(--hp-hue-1), 12%, var(--hp-l-sunken));
    --ui-bg-muted: var(--ui-gray-100);
    --ui-fg:       hsl(var(--hp-hue-1), 18%, var(--hp-l-fg));
    --ui-fg-muted: hsl(var(--hp-hue-1), 10%, var(--hp-l-muted));
    --ui-border:   hsl(var(--hp-hue-1), 12%, var(--hp-l-border));
    --ui-border-strong: hsl(var(--hp-hue-1), 12%, 78%);
}

/* ---- 3. Dark theme — declarations duplicated in @media so users get
 * either explicit `data-theme="dark"` or system preference. */
[data-theme="dark"], .ui-theme-dark {
    --hp-l-bg: 8%; --hp-l-elev: 12%; --hp-l-sunken: 5%;
    --hp-l-fg: 95%; --hp-l-muted: 65%; --hp-l-border: 22%;
    --hp-shadow-intensity: 0.45;
    --ui-gray-50:  hsl(var(--hp-hue-1), 12%, 12%);
    --ui-gray-100: hsl(var(--hp-hue-1), 12%, 16%);
    --ui-gray-200: hsl(var(--hp-hue-1), 10%, 22%);
    --ui-gray-300: hsl(var(--hp-hue-1), 10%, 32%);
    --ui-bg-muted: var(--ui-gray-100);
    --ui-border-strong: hsl(var(--hp-hue-1), 12%, 32%);
    /* Semantic 100-level (alert/callout/badge) backgrounds need to darken
     * too, otherwise we get pale-on-light text. */
    --ui-primary-100: hsl(var(--hp-hue-1), 35%, 18%);
    --ui-secondary-100: hsl(var(--hp-hue-2), 35%, 18%);
    --ui-info-100:    hsl(var(--hp-hue-info), 35%, 18%);
    --ui-danger-100:  hsl(var(--hp-hue-danger), 35%, 18%);
    --ui-warn-100:    hsl(var(--hp-hue-warn), 35%, 18%);
    --ui-success-100: hsl(var(--hp-hue-success), 35%, 18%);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --hp-l-bg: 8%; --hp-l-elev: 12%; --hp-l-sunken: 5%;
        --hp-l-fg: 95%; --hp-l-muted: 65%; --hp-l-border: 22%;
        --hp-shadow-intensity: 0.45;
        --ui-gray-50:  hsl(var(--hp-hue-1), 12%, 12%);
        --ui-gray-100: hsl(var(--hp-hue-1), 12%, 16%);
        --ui-gray-200: hsl(var(--hp-hue-1), 10%, 22%);
        --ui-gray-300: hsl(var(--hp-hue-1), 10%, 32%);
        --ui-bg-muted: var(--ui-gray-100);
        --ui-border-strong: hsl(var(--hp-hue-1), 12%, 32%);
        --ui-primary-100: hsl(var(--hp-hue-1), 35%, 18%);
        --ui-secondary-100: hsl(var(--hp-hue-2), 35%, 18%);
        --ui-info-100:    hsl(var(--hp-hue-info), 35%, 18%);
        --ui-danger-100:  hsl(var(--hp-hue-danger), 35%, 18%);
        --ui-warn-100:    hsl(var(--hp-hue-warn), 35%, 18%);
        --ui-success-100: hsl(var(--hp-hue-success), 35%, 18%);
    }
}

/* ---- 4. Reset + base ---- */
*, *::before, *::after { box-sizing: border-box; }
body {
    margin: 0; font-family: var(--ui-font-body); font-size: var(--ui-text-base);
    font-weight: var(--ui-weight-body);
    line-height: var(--ui-line-height); color: var(--ui-fg); background: var(--ui-bg);
    -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 { font-family: var(--ui-font-heading); margin: 0 0 var(--ui-space-3); font-weight: var(--ui-weight-heading); line-height: 1.2; letter-spacing: -0.01em; }
h1 { font-size: var(--ui-text-3xl); }
h2 { font-size: var(--ui-text-2xl); }
h3 { font-size: var(--ui-text-xl); }
h4 { font-size: var(--ui-text-lg); }
h5, h6 { font-size: var(--ui-text-base); }
p { margin: 0 0 var(--ui-space-3); }
a { color: var(--ui-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
code, kbd, pre, samp { font-family: var(--ui-font-mono); font-size: 0.92em; }

/* ---- 5. Utilities ---- */
.ui-hidden { display: none !important; }
.ui-flex, .ui-d-flex { display: flex; }
.ui-flex-col { flex-direction: column; }
.ui-flex-wrap { flex-wrap: wrap; }
.ui-flex-1 { flex: 1 1 auto; min-width: 0; min-height: 0; }
.ui-flex-2 { flex: 2 1 0; min-width: 0; min-height: 0; }
.ui-flex-3 { flex: 3 1 0; min-width: 0; min-height: 0; }
.ui-grid { display: grid; }
.ui-grid-auto { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.ui-grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ui-grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ui-grid-cols-1-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.ui-grid-cols-2-1 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.ui-readable { max-width: 64rem; width: 100%; }   /* constrain long-form prose to a readable column */
.ui-h-tall { height: clamp(20rem, 50vh, 36rem); }
.ui-h-medium { height: clamp(14rem, 35vh, 24rem); }
.ui-items-center { align-items: center; }
.ui-items-start { align-items: flex-start; }
.ui-items-end { align-items: flex-end; }
.ui-justify-between { justify-content: space-between; }
.ui-justify-center { justify-content: center; }
.ui-justify-end { justify-content: flex-end; }
.ui-min-h-0 { min-height: 0; }
.ui-min-w-0 { min-width: 0; }
.ui-overflow-auto { overflow: auto; }
.ui-overflow-hidden { overflow: hidden; }
.ui-h-full { height: 100%; }
.ui-w-full { width: 100%; }
.ui-col { display: flex; flex-direction: column; }
.ui-relative { position: relative; }

.ui-gap-1 { gap: var(--ui-space-1); } .ui-gap-2 { gap: var(--ui-space-2); }
.ui-gap-3 { gap: var(--ui-space-3); } .ui-gap-4 { gap: var(--ui-space-4); }
.ui-gap-5 { gap: var(--ui-space-5); } .ui-gap-6 { gap: var(--ui-space-6); }

.ui-p-0 { padding: 0; } .ui-p-1 { padding: var(--ui-space-1); }
.ui-p-2 { padding: var(--ui-space-2); } .ui-p-3 { padding: var(--ui-space-3); }
.ui-p-4 { padding: var(--ui-space-4); } .ui-p-5 { padding: var(--ui-space-5); }
.ui-p-6 { padding: var(--ui-space-6); }
.ui-px-2 { padding-inline: var(--ui-space-2); } .ui-px-3 { padding-inline: var(--ui-space-3); }
.ui-px-4 { padding-inline: var(--ui-space-4); } .ui-px-5 { padding-inline: var(--ui-space-5); }
.ui-py-1 { padding-block: var(--ui-space-1); } .ui-py-2 { padding-block: var(--ui-space-2); }
.ui-py-3 { padding-block: var(--ui-space-3); } .ui-py-4 { padding-block: var(--ui-space-4); }
.ui-m-0 { margin: 0; } .ui-mt-1 { margin-top: var(--ui-space-1); }
.ui-mt-2 { margin-top: var(--ui-space-2); } .ui-mt-3 { margin-top: var(--ui-space-3); }
.ui-mt-4 { margin-top: var(--ui-space-4); } .ui-mb-2 { margin-bottom: var(--ui-space-2); }
.ui-mb-3 { margin-bottom: var(--ui-space-3); } .ui-mb-4 { margin-bottom: var(--ui-space-4); }

.ui-text-xs { font-size: var(--ui-text-xs); } .ui-text-sm { font-size: var(--ui-text-sm); }
.ui-text-base { font-size: var(--ui-text-base); } .ui-text-lg { font-size: var(--ui-text-lg); }
.ui-text-xl { font-size: var(--ui-text-xl); } .ui-text-2xl { font-size: var(--ui-text-2xl); }
.ui-text-3xl { font-size: var(--ui-text-3xl); }
.ui-font-thin       { font-weight: var(--ui-font-thin); }
.ui-font-extralight { font-weight: var(--ui-font-extralight); }
.ui-font-light      { font-weight: var(--ui-font-light); }
.ui-font-normal     { font-weight: var(--ui-font-normal); }
.ui-font-medium     { font-weight: var(--ui-font-medium); }
.ui-font-semibold   { font-weight: var(--ui-font-semibold); }
.ui-font-bold       { font-weight: var(--ui-font-bold); }
.ui-font-extrabold  { font-weight: var(--ui-font-extrabold); }
.ui-font-black      { font-weight: var(--ui-font-black); }
.ui-font-mono { font-family: var(--ui-font-mono); }
.ui-font-heading { font-family: var(--ui-font-heading); }
.ui-font-body { font-family: var(--ui-font-body); }
.ui-text-muted { color: var(--ui-fg-muted); }
.ui-text-success { color: var(--ui-success); }
.ui-text-danger  { color: var(--ui-danger); }
.ui-text-warn    { color: var(--ui-warn); }
.ui-text-center { text-align: center; }
.ui-text-right  { text-align: right; }
.ui-list-plain  { list-style: none; padding-left: 0; }
.ui-truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-clickable { cursor: pointer; }

.ui-bg-muted { background: var(--ui-bg-muted); }
.ui-bg-elev  { background: var(--ui-bg-elev); }
.ui-bg-primary { background: var(--ui-primary); color: var(--ui-on-primary); }
.ui-color-primary { color: var(--ui-primary); }
.ui-color-primary-fade { color: var(--ui-primary-700); opacity: 0.85; }
.ui-color-danger  { color: var(--ui-danger); }
.ui-color-success { color: var(--ui-success); }
.ui-color-warn    { color: var(--ui-warn); }

.ui-radius-sm { border-radius: var(--ui-radius-sm); }
.ui-radius-md { border-radius: var(--ui-radius-md); }
.ui-radius-lg { border-radius: var(--ui-radius-lg); }
.ui-shadow-sm { box-shadow: var(--ui-shadow-sm); }
.ui-shadow-md { box-shadow: var(--ui-shadow-md); }
.ui-shadow-lg { box-shadow: var(--ui-shadow-lg); }
.ui-border { border: var(--ui-border-width) solid var(--ui-border); }

/* Status colour helpers used by chips/badges */
.ui-gray-50 { background: var(--ui-gray-50); } .ui-gray-100 { background: var(--ui-gray-100); }
.ui-gray-200 { background: var(--ui-gray-200); } .ui-gray-300 { background: var(--ui-gray-300); }

/* ---- 6. Component skins ---- */

/* Buttons */
.ui-btn {
    display: inline-flex; align-items: center; gap: var(--ui-space-2); white-space: nowrap;
    padding: var(--ui-space-1) var(--ui-space-3);
    border: var(--ui-border-width) solid var(--ui-border);
    border-radius: var(--ui-radius-md); background: var(--ui-bg-elev);
    color: var(--ui-fg); font: inherit; font-weight: var(--ui-font-medium);
    cursor: pointer; transition: background var(--ui-transition-fast) var(--ui-ease),
        border-color var(--ui-transition-fast) var(--ui-ease),
        transform var(--ui-transition-fast) var(--ui-ease),
        box-shadow var(--ui-transition-fast) var(--ui-ease);
    line-height: 1.2; text-decoration: none; user-select: none;
}
.ui-btn:hover { background: var(--ui-gray-100); border-color: var(--ui-border-strong); }
.ui-btn:active { transform: translateY(1px); }
.ui-btn:disabled, .ui-btn[disabled] { opacity: 0.55; cursor: not-allowed; }
.ui-btn-primary {
    background: var(--ui-primary);
    color: var(--ui-on-primary); border-color: var(--ui-primary);
}
.ui-btn-primary:hover {
    background: var(--ui-primary-700); border-color: var(--ui-primary-700);
}
.ui-btn-danger  { background: var(--ui-danger);  color: #fff; border-color: var(--ui-danger); }
.ui-btn-danger:hover  { background: var(--ui-danger-600); }
.ui-btn-ghost { background: transparent; border-color: transparent; color: var(--ui-fg-muted); }
.ui-btn-ghost:hover { background: var(--ui-bg-muted); color: var(--ui-fg); }
.ui-btn-sm { padding: var(--ui-space-1) var(--ui-space-3); font-size: var(--ui-text-sm); }
.ui-btn-lg { padding: var(--ui-space-3) var(--ui-space-5); font-size: var(--ui-text-lg); }
.ui-ml-auto { margin-left: auto; }
.ui-btn-icon, .ui-icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2rem; height: 2rem; border: var(--ui-border-width) solid transparent;
    border-radius: var(--ui-radius-md); background: transparent; color: inherit; cursor: pointer;
    transition: background var(--ui-transition-fast) var(--ui-ease);
}
.ui-btn-icon:hover, .ui-icon-btn:hover { background: var(--ui-bg-muted); }

/* Inputs */
.ui-input, .ui-select, .ui-textarea {
    display: block; width: 100%;
    padding: var(--ui-space-1) var(--ui-space-2);
    font: inherit; font-size: var(--ui-text-sm); color: var(--ui-fg); background: var(--ui-bg-elev);
    border: var(--ui-border-width) solid var(--ui-border);
    border-radius: var(--ui-radius-md);
    transition: border-color var(--ui-transition-fast), box-shadow var(--ui-transition-fast);
}
.ui-input:focus, .ui-select:focus, .ui-textarea:focus {
    outline: none; border-color: var(--ui-primary);
    box-shadow: 0 0 0 3px hsl(var(--hp-hue-1) var(--hp-sat-1) 60% / 0.20);
}
.ui-textarea { resize: vertical; min-height: calc(var(--ui-space-4) * 4); }
.ui-datetime { display: flex; gap: var(--ui-space-2); align-items: center; }
.ui-datetime-date { flex: 3 1 0; min-width: 0; }
.ui-datetime-time { flex: 2 1 0; min-width: 0; }
.ui-tablesearch { display: flex; gap: var(--ui-space-1); align-items: center; }
.ui-tablesearch-btn { flex: 1; text-align: left; cursor: pointer; }
.ui-tablesearch-btn.ui-tablesearch-empty { color: var(--ui-fg-muted); }
.ui-tablesearch-clear { background: none; border: 0; cursor: pointer; color: var(--ui-fg-muted); padding: 0 var(--ui-space-1); }
.ui-tablesearch-btn.ui-tablesearch-invalid { border-color: var(--ui-danger); }
.ui-input-search { padding-left: var(--ui-space-7); background-position: var(--ui-space-2) center; background-repeat: no-repeat; }

.ui-checkbox { display: inline-flex; align-items: center; gap: var(--ui-space-2); cursor: pointer; user-select: none; }
.ui-checkbox input { position: absolute; opacity: 0; pointer-events: none; }
.ui-checkbox-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.1rem; height: 1.1rem; border-radius: var(--ui-radius-sm);
    border: var(--ui-border-width) solid var(--ui-border-strong); background: var(--ui-bg-elev);
    transition: background var(--ui-transition-fast), border-color var(--ui-transition-fast);
}
.ui-checkbox input:checked + .ui-checkbox-mark { background: var(--ui-primary); border-color: var(--ui-primary); color: #fff; }
.ui-checkbox input:checked + .ui-checkbox-mark::after { content: "✓"; font-size: 0.85rem; line-height: 1; color: #fff; }

.ui-switch { display: inline-flex; align-items: center; gap: var(--ui-space-2); cursor: pointer; }
.ui-switch input { position: absolute; opacity: 0; }
.ui-switch-knob {
    position: relative; width: 2.4rem; height: 1.3rem; border-radius: var(--ui-radius-full);
    background: var(--ui-gray-300); transition: background var(--ui-transition-base);
}
.ui-switch-knob::after {
    content: ""; position: absolute; top: 0.15rem; left: 0.15rem;
    width: 1rem; height: 1rem; border-radius: var(--ui-radius-full);
    background: #fff; box-shadow: var(--ui-shadow-sm);
    transition: left var(--ui-transition-base) var(--ui-ease);
}
.ui-switch input:checked + .ui-switch-knob { background: var(--ui-primary); }
.ui-switch input:checked + .ui-switch-knob::after { left: 1.25rem; }

/* Containers */
.ui-card {
    background: var(--ui-bg-elev); border: var(--ui-border-width) solid var(--ui-border);
    border-radius: var(--ui-radius-lg); padding: var(--ui-space-4);
    box-shadow: var(--ui-shadow-md);
    transition: box-shadow var(--ui-transition-base) var(--ui-ease),
                transform var(--ui-transition-base) var(--ui-ease);
    position: relative; overflow: hidden;
}
.ui-card:hover { box-shadow: var(--ui-shadow-lg); transform: translateY(-1px); }
/* Nested cards — suppress shadow + border + heavy padding so a card-within-a-card
   looks like a section, not a separate floating element. The outer card already
   carries the visual weight. */
.ui-card .ui-card {
    box-shadow: none; border-color: var(--ui-border-faint, var(--ui-gray-100));
    padding: var(--ui-space-3);
}
.ui-card .ui-card:hover { box-shadow: none; transform: none; }
.ui-card .ui-card .ui-card { border: none; padding: var(--ui-space-2); background: transparent; }

.ui-bind-entity-card:hover { border-left-color: var(--ui-primary-300); }
.ui-bind-entity-card.ui-active {
    border-left-color: var(--ui-primary);
    background: var(--ui-primary-50);
    box-shadow: var(--ui-shadow-lg);
}
.ui-bind-entity-card.ui-deselected { border-left-color: var(--ui-border); }
/* Vivid header variants — use behind .ui-card for emphasis. */
.ui-card-primary   { background: var(--ui-primary-100);   border-color: var(--ui-primary-200); border-left: 4px solid var(--ui-primary); }
.ui-card-secondary { background: var(--ui-secondary-100); border-color: var(--ui-secondary-100); border-left: 4px solid var(--ui-secondary); }
.ui-card-success   { background: var(--ui-success-100);   border-color: var(--ui-success-100);   border-left: 4px solid var(--ui-success); }
.ui-card-warn      { background: var(--ui-warn-100);      border-color: var(--ui-warn-100);      border-left: 4px solid var(--ui-warn); }
.ui-card-danger    { background: var(--ui-danger-100);    border-color: var(--ui-danger-100);    border-left: 4px solid var(--ui-danger); }
.ui-card-flat::before { display: none; }
/* flat = genuinely flat: object chips (theme cards, launcher rows) carry a
   hairline only — no elevation, no hover lift. */
.ui-card-flat, .ui-card-flat:hover { box-shadow: none; transform: none; }
.ui-card-header { display: flex; flex-direction: column; gap: var(--ui-space-1); margin-bottom: var(--ui-space-2); }
.ui-card-icon { font-size: var(--ui-text-xl); line-height: 1; }
.ui-card-title { display: block; font-size: var(--ui-text-lg); font-weight: var(--ui-font-semibold); color: var(--ui-fg); }
.ui-card-subtitle { display: block; color: var(--ui-fg-muted); font-size: var(--ui-text-sm); }
.ui-card-body { color: var(--ui-fg); }
.ui-card-footer { margin-top: var(--ui-space-3); padding-top: var(--ui-space-2); border-top: var(--ui-border-width) solid var(--ui-border); }
.ui-container { display: block; }
/* Centered readable-width column — for standalone forms / single-record views
   that should not sprawl across a wide viewport. Token-overridable max-width. */
.ui-readable { max-width: var(--ui-readable-max-w, 44rem); margin-inline: auto; width: 100%; }
.ui-grid { gap: var(--ui-space-4); }
/* Dense aligned settings/editor grid — snug label column + content fields,
   left-packed, tight gap, vertically centred. For label · value · value rows. */
.ui-grid-settings { display: grid; grid-template-columns: minmax(7rem, 10rem) minmax(0, 18rem) minmax(0, 18rem);
    gap: var(--ui-space-1) var(--ui-space-3); align-items: center; }
.ui-divider { height: 1px; background: var(--ui-border); margin: var(--ui-space-3) 0; border: 0; }

/* Modal + drawer share overlay */
.ui-overlay {
    position: fixed; inset: 0; background: rgba(15,23,42,0.45);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity var(--ui-transition-base);
    z-index: 100;
}
.ui-overlay.ui-overlay-visible, .ui-overlay.ui-active { opacity: 1; pointer-events: auto; }
.ui-modal {
    background: var(--ui-bg-elev); border-radius: var(--ui-radius-xl);
    box-shadow: var(--ui-shadow-xl); padding: var(--ui-space-5);
    max-width: min(90vw, var(--ui-modal-max-w)); max-height: 90vh; overflow: auto;
    transform: scale(0.96); transition: transform var(--ui-transition-base) var(--ui-ease);
}
.ui-overlay.ui-active .ui-modal, .ui-overlay.ui-overlay-visible .ui-modal { transform: scale(1); }
.ui-drawer {
    position: fixed; top: 0; right: 0; height: 100vh; width: min(90vw, var(--ui-drawer-w));
    background: var(--ui-bg-elev); box-shadow: var(--ui-shadow-xl); padding: var(--ui-space-5);
    transform: translateX(100%); transition: transform var(--ui-transition-base) var(--ui-ease);
    z-index: 110; overflow: auto;
}
.ui-drawer.ui-active { transform: translateX(0); }

/* Badges + chips */
.ui-badge {
    display: inline-flex; align-items: center; gap: var(--ui-space-1);
    padding: 2px var(--ui-space-2); font-size: var(--ui-text-xs); font-weight: var(--ui-font-semibold);
    background: var(--ui-primary); color: var(--ui-on-primary);
    border-radius: var(--ui-radius-full); line-height: 1.4;
    text-transform: uppercase; letter-spacing: 0.04em;
    box-shadow: var(--ui-shadow-sm);
}
.ui-badge-muted   { background: var(--ui-bg-muted); color: var(--ui-fg-muted); text-transform: none; letter-spacing: 0; box-shadow: none; }
.ui-badge-info    { background: var(--ui-info);    color: #fff; }
.ui-badge-success { background: var(--ui-success); color: #fff; }
.ui-badge-warn    { background: var(--ui-warn);    color: #fff; }
.ui-badge-danger  { background: var(--ui-danger);  color: #fff; }
.ui-chipset { display: flex; flex-wrap: wrap; gap: var(--ui-space-2); }
.ui-chip {
    display: inline-flex; align-items: center; gap: var(--ui-space-1);
    padding: var(--ui-space-1) var(--ui-space-3); font-size: var(--ui-text-sm);
    background: var(--ui-bg-muted); border-radius: var(--ui-radius-full);
    cursor: pointer; transition: background var(--ui-transition-fast), color var(--ui-transition-fast);
    user-select: none;
}
.ui-chip-on { background: var(--ui-primary); color: var(--ui-on-primary); }
.ui-chip-off { background: var(--ui-bg-muted); color: var(--ui-fg-muted); }
.ui-chip:hover { filter: brightness(0.96); }
.ui-chip-sm { padding: 0 var(--ui-space-2); font-size: var(--ui-text-xs); }

/* Alerts + callouts */
.ui-alert, .ui-callout {
    display: flex; gap: var(--ui-space-3); padding: var(--ui-space-3) var(--ui-space-4);
    border-left: 4px solid var(--ui-info); background: var(--ui-info-100); color: var(--ui-fg);
    border-radius: var(--ui-radius-md); margin-bottom: var(--ui-space-3);
}
.ui-callout-danger, .ui-alert-danger, .ui-alert-error { border-color: var(--ui-danger); background: var(--ui-danger-100); }
.ui-callout-warning, .ui-alert-warning { border-color: var(--ui-warn); background: var(--ui-warn-100); }
.ui-callout-success, .ui-alert-success { border-color: var(--ui-success); background: var(--ui-success-100); }
.ui-alert-icon, .ui-callout-icon { flex-shrink: 0; font-size: var(--ui-text-lg); }
.ui-alert-title, .ui-callout-title { font-weight: var(--ui-font-semibold); margin: 0 0 var(--ui-space-1); }
.ui-alert-body, .ui-callout-body { flex: 1 1 auto; min-width: 0; line-height: var(--ui-leading-normal); }
.ui-alert-body > p:first-child, .ui-callout-body > p:first-child { margin-top: 0; }
.ui-alert-body > p:last-child, .ui-callout-body > p:last-child { margin-bottom: 0; }
.ui-alert-close { margin-left: auto; cursor: pointer; opacity: 0.6; }
.ui-alert-close:hover { opacity: 1; }

/* Spinner + progress */
.ui-spinner {
    display: inline-block; width: 1.25rem; height: 1.25rem;
    border: 2px solid var(--ui-border); border-top-color: var(--ui-primary);
    border-radius: 50%; animation: ui-spin 0.8s linear infinite;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
.ui-progress {
    height: 6px; background: var(--ui-bg-muted); border-radius: var(--ui-radius-full); overflow: hidden;
}
.ui-progress-bar {
    height: 100%; background: var(--ui-primary); border-radius: inherit;
    transition: width var(--ui-transition-base) var(--ui-ease);
}
.ui-progress-bar.ui-bar-good { background: var(--ui-success); }
.ui-progress-bar.ui-bar-warn { background: var(--ui-warn); }
.ui-progress-bar.ui-bar-bad  { background: var(--ui-danger); }

/* Circular progress ring (progress shape:'ring' + report cards) */
.ui-progress-ring { position: relative; width: 64px; height: 64px; flex: 0 0 64px; }
.ui-ring-svg   { width: 100%; height: 100%; transform: rotate(-90deg); }
.ui-ring-track { fill: none; stroke: var(--ui-bg-muted); stroke-width: 6; }
.ui-ring-fill  { fill: none; stroke-width: 6; stroke-linecap: round; transition: stroke-dashoffset var(--ui-transition-base) var(--ui-ease); }
.ui-ring-good  { stroke: var(--ui-success); }
.ui-ring-warn  { stroke: var(--ui-warn); }
.ui-ring-bad   { stroke: var(--ui-danger); }
.ui-ring-label { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: var(--ui-text-sm); font-weight: var(--ui-font-bold); }

/* Status pill */
.ui-pill         { display: inline-block; padding: 0 var(--ui-space-2); border-radius: var(--ui-radius-full); font-size: var(--ui-text-xs); font-weight: var(--ui-font-semibold); background: var(--ui-bg-muted); color: var(--ui-fg-muted); white-space: nowrap; }
.ui-pill-success { background: var(--ui-success); color: #fff; }
.ui-pill-warn    { background: var(--ui-warn);    color: #fff; }
.ui-pill-danger  { background: var(--ui-danger);  color: #fff; }

/* Report card — header (title + pill), optional ring, metric chips + value bars */
.ui-reportcard        { display: flex; flex-direction: column; gap: var(--ui-space-2); padding: var(--ui-space-3); background: var(--ui-bg); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-lg); box-shadow: var(--ui-shadow-sm); }
.ui-reportcard-head   { display: flex; align-items: flex-start; gap: var(--ui-space-2); }
.ui-reportcard-titles { flex: 1; min-width: 0; }
.ui-reportcard-title  { font-weight: var(--ui-font-bold); }
.ui-reportcard-sub    { font-size: var(--ui-text-sm); color: var(--ui-fg-muted); }
.ui-reportcard-body   { display: flex; gap: var(--ui-space-3); align-items: center; }
.ui-reportcard-detail { flex: 1; min-width: 0; max-width: 40rem; display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-reportcard-metrics { display: flex; flex-wrap: wrap; gap: var(--ui-space-2); }
.ui-reportcard-metrics .ui-metric-chip { flex: 0 0 auto; }
.ui-reportcard-bar    { display: flex; align-items: center; gap: var(--ui-space-2); }
.ui-reportcard-bar-label { flex: 0 0 9rem; font-size: var(--ui-text-sm); color: var(--ui-fg-muted); }
.ui-reportcard-bar .ui-progress { flex: 1; }
.ui-reportcard-bar-val   { flex: 0 0 3rem; text-align: right; font-size: var(--ui-text-sm); font-weight: var(--ui-font-semibold); }
.ui-reportcard-foot   { font-size: var(--ui-text-xs); color: var(--ui-fg-muted); }

/* Avatar + metric chip + empty state */
/* Avatar — fixed-size circle. Image fills via object-fit; initials centred. */
.ui-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--ui-avatar-size); height: var(--ui-avatar-size); flex: 0 0 auto;
    padding: 0; border-radius: var(--ui-radius-full); overflow: hidden;
    background: var(--ui-primary-600); color: #fff;
    font-weight: var(--ui-font-bold); font-size: var(--ui-text-sm); line-height: 1;
    box-shadow: var(--ui-shadow-sm);
    border: 2px solid var(--ui-bg-elev);
    user-select: none;
}
.ui-avatar-sm { width: 2rem; height: 2rem; font-size: var(--ui-text-xs); }
.ui-avatar-lg { width: 3.2rem; height: 3.2rem; font-size: var(--ui-text-lg); }
.ui-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Tone variants — colour an avatar by semantic status (people initials,
   status markers). Reusable across systems. */
.ui-avatar-success { background: var(--ui-success); }
.ui-avatar-warn    { background: var(--ui-warn); }
.ui-avatar-danger  { background: var(--ui-danger); }
.ui-avatar-info    { background: var(--ui-info); }
/* 8-shade hash cycle, 60-30-10 across primary/secondary/accent. */
.ui-avatar-c0 { background: hsl(calc(var(--hp-hue-1) - 30)      calc(var(--hp-sat-1) * 0.7) 45%); }
.ui-avatar-c1 { background: hsl(var(--hp-hue-1)                  calc(var(--hp-sat-1) * 0.8) 42%); }
.ui-avatar-c2 { background: hsl(calc(var(--hp-hue-1) + 25)      calc(var(--hp-sat-1) * 0.7) 40%); }
.ui-avatar-c3 { background: hsl(calc(var(--hp-hue-1) + 50)      calc(var(--hp-sat-1) * 0.6) 42%); }
.ui-avatar-c4 { background: hsl(calc(var(--hp-hue-2) - 20)      calc(var(--hp-sat-2) * 0.7) 45%); }
.ui-avatar-c5 { background: hsl(var(--hp-hue-2)                  calc(var(--hp-sat-2) * 0.8) 42%); }
.ui-avatar-c6 { background: hsl(calc(var(--hp-hue-2) + 30)      calc(var(--hp-sat-2) * 0.7) 40%); }
.ui-avatar-c7 { background: hsl(var(--hp-hue-accent)             calc(var(--hp-sat-accent) * 0.85) 38%); }
.ui-avatar-success { background: var(--ui-success); }
.ui-avatar-warn    { background: var(--ui-warn); }
.ui-avatar-danger  { background: var(--ui-danger); }
.ui-avatar-info    { background: var(--ui-info); }
/* Text-first metrics (design/ §5): bold number + muted label, hairline
   separators — a metric must never be SHAPED like a control. Tones express
   through the number colour only. */
.ui-metric-chip {
    display: inline-flex; align-items: baseline; gap: var(--ui-space-1);
    padding: 0 var(--ui-space-3) 0 0; font-size: var(--ui-text-sm);
    color: var(--ui-fg-muted);
}
.ui-metric-chip + .ui-metric-chip { border-left: var(--ui-border-width) solid var(--ui-border); padding-left: var(--ui-space-3); }
.ui-metric-chip strong { color: var(--ui-fg); font-weight: var(--ui-font-semibold); font-size: var(--ui-text-base); }
.ui-metric-chip-secondary strong { color: var(--ui-secondary-700); }
.ui-metric-chip-accent strong    { color: var(--ui-accent); }
/* Empty-state chip: data hasn't loaded yet. Muted so it reads as "slot
 * waiting" rather than "loaded with em-dash" — see UIBind.renderMetricChip
 * which applies this modifier when the value is '—'. */
.ui-metric-chip-empty strong    { color: var(--ui-gray-400); font-weight: var(--ui-font-normal); }
.ui-metric-chip-empty .ui-metric-chip-icon { opacity: 0.4; }
.ui-empty {
    text-align: center; padding: var(--ui-space-7) var(--ui-space-4);
    color: var(--ui-fg-muted); font-size: var(--ui-text-sm);
}
.ui-empty-icon { font-size: var(--ui-text-3xl); margin-bottom: var(--ui-space-3); opacity: 0.45; }

/* Tooltip */
.ui-tooltip {
    position: absolute; z-index: 200; padding: var(--ui-space-1) var(--ui-space-2);
    background: var(--ui-gray-800); color: #fff; border-radius: var(--ui-radius-sm);
    font-size: var(--ui-text-xs); pointer-events: none; box-shadow: var(--ui-shadow-md);
    opacity: 0; transition: opacity var(--ui-transition-fast);
}
.ui-tooltip.ui-active { opacity: 1; }

/* Tabs / accordion / ribbon / nav / stepper / segmented / sidebar */
.ui-tabs { display: flex; flex-direction: column; min-height: 0; }
.ui-tabs-bar {
    display: flex; gap: var(--ui-space-1); border-bottom: var(--ui-border-width) solid var(--ui-border);
    padding: 0 var(--ui-space-2); overflow-x: auto;
}
.ui-tabs-head, .ui-ribbon-item, .ui-segmented-item, .ui-navbar-item, .ui-sidebar-item, .ui-stepper-item {
    display: inline-flex; align-items: center; gap: var(--ui-space-1);
    padding: var(--ui-space-1) var(--ui-space-2); cursor: pointer;
    color: var(--ui-fg-muted); font-weight: var(--ui-font-medium);
    border-radius: var(--ui-radius-md); transition: background var(--ui-transition-fast), color var(--ui-transition-fast);
    user-select: none; white-space: nowrap; border: none; background: transparent;
}
.ui-tabs-head:hover, .ui-ribbon-item:hover, .ui-navbar-item:hover, .ui-sidebar-item:hover, .ui-segmented-item:hover { background: var(--ui-bg-muted); color: var(--ui-fg); }
.ui-tabs-head { padding-bottom: var(--ui-space-2); border-radius: 0; border-bottom: var(--ui-tab-underline-w) solid transparent; margin-bottom: -1px; }
.ui-tabs-head.ui-active { color: var(--ui-primary); border-bottom-color: var(--ui-primary); background: transparent; }
.ui-ribbon { display: flex; gap: var(--ui-space-2); padding: var(--ui-space-2); background: var(--ui-bg-muted); border-radius: var(--ui-radius-md); }
.ui-ribbon-item.ui-active, .ui-segmented-item.ui-active, .ui-navbar-item.ui-active { background: var(--ui-primary); color: var(--ui-on-primary); }
.ui-segmented { display: inline-flex; padding: 2px; background: var(--ui-bg-muted); border-radius: var(--ui-radius-md); }
.ui-navbar { display: flex; gap: var(--ui-space-1); padding: var(--ui-space-2); background: var(--ui-bg-elev); border-bottom: var(--ui-border-width) solid var(--ui-border); }
.ui-stepper { display: flex; gap: var(--ui-space-2); padding: var(--ui-space-2); }
.ui-stepper-step { display: flex; align-items: center; gap: var(--ui-space-2); flex: 1; }
.ui-stepper-step::after { content: ""; flex: 1; height: 2px; background: var(--ui-border); }
.ui-stepper-step:last-child::after { display: none; }
.ui-stepper-item.ui-active { color: var(--ui-primary); }

.ui-sidebar { display: flex; flex-direction: column; padding: var(--ui-space-3); gap: var(--ui-space-1); background: var(--ui-bg-elev); border-right: var(--ui-border-width) solid var(--ui-border); }
.ui-sidebar-item { padding: var(--ui-space-2) var(--ui-space-3); justify-content: flex-start; }
.ui-sidebar-item.ui-active { background: var(--ui-primary-100); color: var(--ui-primary-700); border-left: 3px solid var(--ui-primary); padding-left: calc(var(--ui-space-3) - 3px); font-weight: var(--ui-font-semibold); }
.ui-sidebar-section { margin-top: var(--ui-space-3); }
.ui-sidebar-section-title { font-size: var(--ui-text-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--ui-fg-muted); padding: var(--ui-space-2) var(--ui-space-3); }

.ui-accordion { display: flex; flex-direction: column; border: var(--ui-border-width) solid var(--ui-border); border-radius: var(--ui-radius-md); overflow: hidden; background: var(--ui-bg-elev); }
.ui-accordion-item + .ui-accordion-item, .ui-accordion-entry + .ui-accordion-entry { border-top: var(--ui-border-width) solid var(--ui-border); }
.ui-accordion-head { display: flex; align-items: center; gap: var(--ui-space-2); padding: var(--ui-space-1) var(--ui-space-3); cursor: pointer; font-size: var(--ui-text-xs); font-weight: var(--ui-font-semibold); text-transform: uppercase; letter-spacing: 0.04em; background: var(--ui-bg-muted); color: var(--ui-fg-muted); transition: background var(--ui-transition-fast), color var(--ui-transition-fast); }
.ui-accordion-head:hover { background: var(--ui-bg-sunken); }
.ui-accordion-head::after { content: "›"; margin-left: auto; font-size: 1.2em; line-height: 1; transition: transform var(--ui-transition-fast); }
.ui-accordion-item.ui-active > .ui-accordion-head::after, .ui-accordion-entry.ui-active > .ui-accordion-head::after { transform: rotate(90deg); }
.ui-accordion-item.ui-active > .ui-accordion-head, .ui-accordion-entry.ui-active > .ui-accordion-head { background: var(--ui-primary); color: var(--ui-on-primary); }
.ui-accordion-body, .ui-accordion-content { padding: var(--ui-space-2); }
.ui-accordion-item:not(.ui-active) > .ui-accordion-body { display: none; }

.ui-breadcrumbs { display: flex; gap: var(--ui-space-2); font-size: var(--ui-text-sm); color: var(--ui-fg-muted); }
.ui-breadcrumbs > * + *::before { content: "›"; margin-right: var(--ui-space-2); color: var(--ui-fg-muted); }

/* ───────────────────────────────────────────────────────────────────────────
   PAGE-FRAME LAYOUT — DOCUMENT-FLOW STICKY FOOTER (read before changing this).

   Want: the footer sits at the bottom of the VIEWPORT when the page is short,
   and is PUSHED DOWN by the content (the whole PAGE scrolls) when the content is
   tall. This is the classic CSS sticky footer — NOT an app-shell with an
   internally-scrolling body. The footer comes after the body in normal flow.

   Three load-bearing rules:
     1. Frame = flex COLUMN, MIN-height of one viewport:
          display:flex; flex-direction:column; min-height:100dvh;
        `min-height` (NOT `height`) is the whole point — the frame must be free to
        grow taller than the screen so the footer travels down with the content.
     2. Header + footer size to content, never grow/shrink:  flex:0 0 auto;
     3. Body grows to fill the gap (pinning the footer to the viewport bottom when
        the page is short) but is NEVER capped and does NOT scroll internally:
          flex:1 0 auto;
        No overflow/min-height:0 on the body — that would trap scrolling inside it
        and pin the footer (the app-shell behaviour we explicitly do NOT want).

   The PAGE is the scroll container. Do NOT reintroduce height:100dvh on the frame
   or overflow:auto on the body — that pins the footer and reads as "fixed", the
   recurring regression. If ONE pane genuinely needs its own scroll, cap THAT pane
   (max-height + overflow) — never the frame.
   ─────────────────────────────────────────────────────────────────────────── */
.ui-pageframe { display: flex; flex-direction: column; min-height: 100dvh; background: var(--ui-bg); }
.ui-pageframe-header, .ui-pageframe-footer { flex: 0 0 auto; }
/* The content region between header and footer: grow to push the footer down; the page scrolls. */
.ui-pageframe > :not(.ui-pageframe-header):not(.ui-pageframe-footer) { flex: 1 0 auto; }
.ui-pageframe .ui-pageframe { min-height: 0; }   /* a nested frame flows; it does not start a 2nd viewport */
/* `.ui-pageframe-app` / `.ui-pageframe-fill` were opt-in "fixed shell" modifiers;
   removed — they trapped scrolling in nested flex containers. The frame is
   document-flow only (above): the page scrolls, the footer is pushed down. */
.ui-pageframe-header {
    display: flex; align-items: center; gap: var(--ui-space-5);
    padding: var(--ui-space-3) var(--ui-space-5);
    background: var(--ui-primary);
    color: var(--ui-on-primary);
    border-bottom: 0;
    box-shadow: var(--ui-shadow-md);
    /* Sticky so the nav stays visible while the PAGE scrolls (document-flow
       sticky-footer model — see the PAGE-FRAME LAYOUT block above). */
    flex-shrink: 0; position: sticky; top: 0; z-index: 5;
}
.ui-pageframe-header .ui-pageframe-title    { color: var(--ui-on-primary); }
.ui-pageframe-header .ui-pageframe-subtitle { color: var(--ui-on-primary-muted); }
.ui-pageframe-header .ui-pageframe-brand-mark {
    background: var(--ui-on-primary-bg-strong); color: var(--ui-on-primary);
    box-shadow: inset 0 0 0 1px var(--ui-on-primary-divider);
}
.ui-pageframe-action {
    color: var(--ui-on-primary); border-radius: var(--ui-radius-md);
    width: 2rem; height: 2rem;
}
.ui-pageframe-action:hover { background: var(--ui-on-primary-bg-strong); color: var(--ui-on-primary); }
.ui-pageframe-action > i.fa { font-size: 1rem; line-height: 1; }
.ui-sidebar-item .fa, .ui-tabs-head .fa { font-size: 0.95em; opacity: 0.85; margin-right: 0.25rem; }
.ui-pageframe-brand { display: flex; align-items: center; gap: var(--ui-space-3); line-height: 1.2; min-width: 0; }
.ui-pageframe-brand-mark {
    display: inline-flex; align-items: center; justify-content: center;
    aspect-ratio: 1; padding: var(--ui-space-2);
    border-radius: var(--ui-radius-md);
    background: var(--ui-primary);
    color: #fff; font-weight: var(--ui-font-bold);
    font-size: var(--ui-text-lg); line-height: 1;
    box-shadow: var(--ui-shadow-sm);
    flex-shrink: 0;
}
.ui-pageframe-brand-stack { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.ui-pageframe-title { font-size: var(--ui-text-lg); font-weight: var(--ui-font-semibold); color: var(--ui-fg); white-space: nowrap; }
.ui-pageframe-subtitle { color: var(--ui-fg-muted); font-size: var(--ui-text-xs); }
.ui-pageframe-actions { margin-left: auto; display: flex; gap: var(--ui-space-2); align-items: center; }
/* In-header tabs — translucent white on primary background. */
.ui-pageframe-tabs { flex: 0 1 auto; background: transparent; display: flex; min-width: 0; }
.ui-pageframe-tabs > * { min-width: 0; }
.ui-pageframe-tabs .ui-tabs-bar { border-bottom: 0; padding: 0; gap: var(--ui-space-2); }
.ui-pageframe-tabs .ui-tabs-head {
    color: var(--ui-on-primary-muted); border-bottom: var(--ui-tab-underline-w) solid transparent;
    margin-bottom: 0;
}
.ui-pageframe-tabs .ui-tabs-head:hover { color: var(--ui-on-primary); background: var(--ui-on-primary-bg-soft); }
.ui-pageframe-tabs .ui-tabs-head.ui-active {
    color: var(--ui-on-primary); background: transparent;
    border-bottom-color: var(--ui-on-primary);
}
/* Padding only — fill/scroll comes from the catch-all rule above (the single
   content child gets flex:1 1 auto; min-height:0; overflow:auto). Do NOT add a
   flex value with `0` shrink here; it re-breaks the sticky footer. */
/* ONE padding level only. The frame nests body > stage and content mounts into
   the stage (or the hub-page-host inside it), so padding BOTH stacked 32px of
   dead space at the top/bottom. Pad the stage; the body is a bare flex wrapper. */
.ui-pageframe-stage { padding: var(--ui-space-4); }

/* Login shield host (mounted between header and footer by System._mountShield).
   Layout + visuals only. The card's WIDTH is set inline by System._mountShield
   from `config.shield.width` so a designer can override per-system without
   fighting CSS specificity. */
.ui-system-shield-host {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    flex: 1 0 auto;
    padding: var(--ui-space-7) var(--ui-space-4);
    min-height: 60vh;
    background: var(--ui-bg-sunken, transparent);
}
.ui-system-shield-host > .ui {
    width: 100%;
    background: var(--ui-bg-elev);
    border: 1px solid var(--ui-border);
    border-radius: var(--ui-radius-lg);
    padding: var(--ui-space-6) var(--ui-space-5);
    box-shadow: var(--ui-shadow-md);
}
.ui-pageframe-footer {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--ui-space-5);
    padding: var(--ui-space-5) var(--ui-space-5);
    background: var(--ui-gray-900);
    color: var(--ui-on-primary-muted);
    border-top: 3px solid var(--ui-accent);
    font-size: var(--ui-text-sm); flex-shrink: 0;
}
.ui-pageframe-footer-column { display: flex; flex-direction: column; gap: var(--ui-space-1); min-width: 0; }
.ui-pageframe-footer-title { font-weight: var(--ui-font-semibold); color: var(--ui-on-primary); margin-bottom: var(--ui-space-2); font-size: var(--ui-text-sm); text-transform: uppercase; letter-spacing: 0.06em; }
.ui-pageframe-footer-link { color: rgba(255,255,255,0.7); transition: color var(--ui-transition-fast); }
.ui-pageframe-footer-link:hover { color: #fff; }
.ui-pageframe-footer-text { color: rgba(255,255,255,0.72); font-size: var(--ui-text-sm); }
.ui-pageframe-footer-copyright { grid-column: 1 / -1; padding-top: var(--ui-space-3); border-top: 1px solid rgba(255,255,255,0.12); margin-top: var(--ui-space-3); text-align: center; font-size: var(--ui-text-xs); color: rgba(255,255,255,0.55); }
.ui-footer { display: flex; align-items: center; gap: var(--ui-space-3); padding: var(--ui-space-3); background: var(--ui-bg-muted); }

/* Control-stage — fluid grid, content sizes the control column. */
.ui-control-stage {
    display: grid;
    grid-template-columns: minmax(min-content, 1fr) 3fr;
    gap: var(--ui-space-4);
}
.ui-control-stage-control { display: flex; flex-direction: column; gap: var(--ui-space-3); min-height: 0; overflow: auto; }
.ui-control-stage-stage   { display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: auto; }
@media (max-width: 800px) { .ui-control-stage { grid-template-columns: 1fr; } }

/* Forms */
/* §4b — a form is a narrow input column, never a full-stage stretch. Capped at
   the primitive so an over-wide host can't produce an unreadable 900px+ form;
   the cap only limits, so forms already in a 1/3 rail or modal are unaffected. */
.ui-form { display: flex; flex-direction: column; gap: var(--ui-space-2); max-width: 34rem; }
/* A form deliberately filling a wide grid cell opts out with .ui-form-wide. */
.ui-form-wide { max-width: none; }
/* §4b — a form column in a split (brief/read beside form): bases at a readable
   ~1/3 width, shrinks if needed, never collapses to content. */
.ui-form-col { flex: 0 1 32rem; min-width: 0; }
.ui-form-field { display: flex; flex-direction: column; gap: 2px; }
.ui-form-label { font-size: var(--ui-text-xs); font-weight: var(--ui-font-medium); color: var(--ui-fg-muted); }
.ui-form-buttons { display: flex; gap: var(--ui-space-2); margin-top: var(--ui-space-2); }

/* Data list / cards / tree / pager */
.ui-data { display: flex; flex-direction: column; min-height: 0; gap: var(--ui-space-2); }
.ui-data-list-content { display: flex; flex-direction: column; min-height: 0; overflow: auto; }
.ui-data-list-row, .ui-bind-list-row {
    display: flex; align-items: center; gap: var(--ui-space-3); padding: var(--ui-space-2) var(--ui-space-3);
    border-bottom: var(--ui-border-width) solid var(--ui-border); cursor: pointer;
    transition: background var(--ui-transition-fast), border-left-color var(--ui-transition-fast);
    border-left: var(--ui-active-marker-w) solid transparent;
}
.ui-data-list-row:hover, .ui-bind-list-row:hover { background: var(--ui-bg-muted); border-left-color: var(--ui-primary-300); }
.ui-data-list-row.ui-active, .ui-bind-list-row.ui-active { background: var(--ui-primary-100); color: var(--ui-primary-700); border-left-color: var(--ui-primary); }
/* WCAG AA: on a selected row the teal-100 tint lifts the background luminance,
   so plain fg-muted (tuned for white) drops below 4.5:1. Inherit the row's
   dark-teal selection colour for muted descendants — the designed 700-on-100 pair. */
.ui-data-list-row.ui-active .ui-text-muted, .ui-bind-list-row.ui-active .ui-text-muted { color: var(--ui-primary-700); }
/* Compact list rows — the DEFAULT for bindSelector/bindSelectEditor lists: dense
   reading-pane rows with a smaller avatar, tighter padding/gap, title + subtitle on
   one line, no row growth. Opt OUT with {compact:false} for roomy two-line rows. */
.ui-bind-list--compact .ui-bind-list-row { padding: 2px var(--ui-space-2); gap: var(--ui-space-2); }
.ui-bind-list--compact .ui-avatar { width: 1.35rem; height: 1.35rem; font-size: 0.58rem; flex: 0 0 auto; }
.ui-bind-list--compact .ui-bind-list-head { flex-direction: row; align-items: baseline; gap: var(--ui-space-2); min-width: 0; }
.ui-bind-list--compact .ui-bind-list-head .ui-text-sm { font-size: var(--ui-text-xs); }
.ui-bind-list--compact .ui-bind-list-pills { transform: scale(0.85); transform-origin: right center; }
.ui-bind-list--compact .ui-icon-btn { padding: 0; width: 1.25rem; height: 1.25rem; }
/* Cards grid — items live in a sub-host (.ui-bind-cards-items) so toolbar
   + pager can sit above/below as flex siblings of the grid, not as grid
   items. auto-fill keeps every column the same width on the last row. */
.ui-bind-cards { display: flex; flex-direction: column; gap: var(--ui-space-2); padding: var(--ui-space-2); }
.ui-data-cards-grid, .ui-bind-cards-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--ui-space-3); align-items: stretch; align-content: start; }
.ui-data-cards-grid > .ui-card, .ui-bind-cards-items > .ui-card { min-width: 0; display: flex; flex-direction: column; }
.ui-data-card, .ui-bind-entity-card {
    background: var(--ui-bg-elev); border: var(--ui-border-width) solid var(--ui-border);
    border-left: var(--ui-active-marker-w) solid transparent;
    border-radius: var(--ui-radius-lg); padding: var(--ui-space-3);
    cursor: pointer; transition: box-shadow var(--ui-transition-fast), transform var(--ui-transition-fast);
}
.ui-data-card:hover, .ui-bind-entity-card:hover { box-shadow: var(--ui-shadow-md); transform: translateY(-1px); }
.ui-bind-entity-card-header, .ui-bind-entity-card-title { font-weight: var(--ui-font-semibold); margin-bottom: var(--ui-space-1); }
.ui-bind-entity-card-body { color: var(--ui-fg-muted); font-size: var(--ui-text-sm); }
.ui-bind-entity-card-actions, .ui-bind-list-actions { display: flex; gap: var(--ui-space-2); margin-top: var(--ui-space-2); }
/* Layout root — minimal. Each template adds its own display rules.
   Charts size themselves from CONTENT (Plotly height = N_bars × bar_height).
   Kanban lanes size themselves from card count. Don't force container
   heights — that squashes bars when N is large or wastes space when N
   is small. */
.ui-bind-layout { min-height: 0; }
/* A chart layout (calendar/kanban/graph…) needs a resolved height to draw into;
   without this the block-level bind-layout collapses to content height and the
   chart's height:100% resolves to ~0. Scoped to chart hosts so list/table/form
   layouts (content-height by design) are untouched. */
.ui-bind-layout:has(> .ui-chart) { height: 100%; }
.ui-paginator { display: flex; align-items: center; gap: var(--ui-space-3); padding: var(--ui-space-2) var(--ui-space-3); border-top: var(--ui-border-width) solid var(--ui-border); font-size: var(--ui-text-sm); }
.ui-paginator-controls { display: flex; gap: var(--ui-space-1); margin-left: auto; }
.ui-paginator-btn { padding: var(--ui-space-1) var(--ui-space-2); border: var(--ui-border-width) solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-bg-elev); cursor: pointer; }
.ui-paginator-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.ui-paginator-btn:hover:not(:disabled) { background: var(--ui-bg-muted); }
.ui-paginator-info { color: var(--ui-fg-muted); }

.ui-tree { display: flex; flex-direction: column; gap: var(--ui-space-1); padding: var(--ui-space-2); }
.ui-tree-node, .ui-bind-tree-node {
    position: relative;
    display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--ui-space-1); row-gap: 0;
    padding: 1px var(--ui-space-2); border-radius: var(--ui-radius-sm); font-size: var(--ui-text-sm);
    cursor: pointer; transition: background var(--ui-transition-fast),
                                  border-color var(--ui-transition-fast);
    padding-left: calc(var(--ui-space-2) + var(--ui-tree-depth, 0) * var(--ui-space-4));
    border-left: var(--ui-active-marker-w) solid transparent;
}
.ui-tree-node:hover, .ui-bind-tree-node:hover {
    background: var(--ui-bg-muted);
    border-left-color: var(--ui-primary-300);
}
.ui-tree-node.ui-active, .ui-bind-tree-node.ui-active {
    background: var(--ui-primary-100); color: var(--ui-primary-700);
    border-left-color: var(--ui-primary); font-weight: var(--ui-font-semibold);
}
.ui-bind-tree-node.ui-deselected { border-left-color: var(--ui-border); }
.ui-tree-bullet { color: var(--ui-fg-muted); font-size: var(--ui-text-xs); }
/* L5.6 — per-table colour for chain renderers (tree nodes + chips). Hash 0-7 reuses the avatar palette HSL spec. */
[data-table-hash="0"] { --ui-table-color: hsl(calc(var(--hp-hue-1) - 30) calc(var(--hp-sat-1) * 0.7) 45%); }
[data-table-hash="1"] { --ui-table-color: hsl(var(--hp-hue-1) calc(var(--hp-sat-1) * 0.8) 42%); }
[data-table-hash="2"] { --ui-table-color: hsl(calc(var(--hp-hue-1) + 25) calc(var(--hp-sat-1) * 0.7) 40%); }
[data-table-hash="3"] { --ui-table-color: hsl(calc(var(--hp-hue-1) + 50) calc(var(--hp-sat-1) * 0.6) 42%); }
[data-table-hash="4"] { --ui-table-color: hsl(calc(var(--hp-hue-2) - 20) calc(var(--hp-sat-2) * 0.7) 45%); }
[data-table-hash="5"] { --ui-table-color: hsl(var(--hp-hue-2) calc(var(--hp-sat-2) * 0.8) 42%); }
[data-table-hash="6"] { --ui-table-color: hsl(calc(var(--hp-hue-2) + 30) calc(var(--hp-sat-2) * 0.7) 40%); }
[data-table-hash="7"] { --ui-table-color: hsl(var(--hp-hue-accent) calc(var(--hp-sat-accent) * 0.85) 38%); }
.ui-bind-tree-node[data-table-hash] { border-left-color: var(--ui-table-color); border-left-width: 3px; }
.ui-bind-tree-node[draggable="true"] { cursor: move; }
.ui-bind-tree-node[draggable="true"]:hover { outline: 2px dashed var(--ui-primary-300); outline-offset: -2px; }
.ui-chip[data-table-hash] { color: var(--ui-table-color); border: 1px solid var(--ui-table-color); }

/* Tree — twisty + folder/leaf icon in front of the label. */
.ui-bind-tree { padding: var(--ui-space-2); }
.ui-bind-tree-rows { display: flex; flex-direction: column; gap: 1px; }
.ui-bind-tree-twisty { display: inline-flex; align-items: center; justify-content: center; width: 0.9rem; height: 0.9rem; flex-shrink: 0; color: var(--ui-fg-muted); font-size: 0.65rem; line-height: 1; }
.ui-bind-tree-twisty-active { cursor: pointer; }
.ui-bind-tree-twisty-active:hover { color: var(--ui-primary); }
.ui-bind-tree-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 1rem; height: 1rem; line-height: 1; font-size: var(--ui-text-sm); }
.ui-bind-tree-icon-branch { color: var(--ui-primary); }
.ui-bind-tree-icon-leaf   { color: var(--ui-fg-muted); }
.ui-bind-tree-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Charts */
.ui-chart, .ui-chart-frame { display: flex; flex-direction: column; min-height: 0; height: 100%; background: var(--ui-bg-elev); border-radius: var(--ui-radius-lg); border: var(--ui-border-width) solid var(--ui-border); padding: var(--ui-space-3); position: relative; }
.ui-chart-frame.ui-chart-fixed-h { height: var(--ui-chart-h); }
.ui-chart-frame.ui-chart-flex { flex: 0 0 auto; min-height: 0; height: var(--ui-chart-min-h, clamp(200px, 26vh, 300px)); overflow: hidden; }
.ui-chart-panel { background: var(--ui-bg-elev); border: var(--ui-border-width) solid var(--ui-border); border-radius: var(--ui-radius-lg); }
.ui-chart-panel .ui-chart-panel-header { padding: var(--ui-space-2) var(--ui-space-3); border-bottom: var(--ui-border-width) solid var(--ui-border); font-weight: var(--ui-font-semibold); }
.ui-chart-panel .ui-chart-panel-body { padding: var(--ui-space-3); height: var(--ui-chart-panel-h, 300px); }
.ui-chart-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--ui-space-2); }
.ui-chart-panel-body { flex: 1 1 auto; min-height: 0; }
.ui-chart-title { font-weight: var(--ui-font-semibold); }
.ui-chart-empty { display: flex; align-items: center; justify-content: center; min-height: 12rem; color: var(--ui-fg-muted); font-size: var(--ui-text-sm); }

/* Kanban + timeline */
.ui-kanban { display: flex; gap: var(--ui-space-3); padding: var(--ui-space-2); align-items: stretch; overflow-x: auto; min-height: 14rem; }
/* As a pane's central element the board fills the pane — lanes run to the
   bottom edge instead of ending at card height over a white void. min-height
   covers the non-flex .ui-pane-scroll parent (scrollport content height). */
.ui-pane .ui-kanban { flex: 1 1 auto; min-height: 100%; }
/* Lanes grow when few; shrink to a still-readable floor (8.5rem) so a fixed
   board (6-stage pipeline) fits without hidden scroll, then scroll past that. */
.ui-kanban-lane { flex: 1 1 11rem; min-width: 8.5rem; display: flex; flex-direction: column; background: var(--ui-bg-muted); border-radius: var(--ui-radius-lg); padding: var(--ui-space-2); }
.ui-kanban-lane-header { font-weight: var(--ui-font-semibold); padding: var(--ui-space-2); display: flex; justify-content: space-between; gap: var(--ui-space-2); }
.ui-kanban-lane-body { display: flex; flex-direction: column; gap: var(--ui-space-2); flex: 1 1 auto; }
.ui-kanban-card:active { cursor: grabbing; }
/* Proportion bar — a stacked allocation strip (funding split, effort mix).
   Segments size via --seg-pct and colour via --seg-color (the chip/dot
   custom-property pattern, not inline layout). Promoted to the shared layer
   per design §10 — reusable wherever parts sum to a whole. */
.ui-proportion-bar { display: flex; height: 8px; margin-top: var(--ui-space-1); border-radius: var(--ui-radius-full); overflow: hidden; background: var(--ui-bg-muted); }
.ui-proportion-seg { width: var(--seg-pct, 0); background: var(--seg-color, var(--ui-primary)); }
.ui-proportion-gap { background: repeating-linear-gradient(45deg, var(--ui-gray-200), var(--ui-gray-200) 4px, transparent 4px, transparent 8px); }
.ui-kanban-card { background: var(--ui-bg-elev); border-radius: var(--ui-radius-md); padding: var(--ui-space-3); box-shadow: var(--ui-shadow-sm); cursor: grab; transition: transform var(--ui-transition-fast); border-left: 3px solid var(--ui-kanban-card-accent, var(--ui-primary)); }
.ui-kanban-card:hover { transform: translateY(-1px); box-shadow: var(--ui-shadow-md); }
.ui-kanban-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--ui-kanban-dot-color, var(--ui-primary)); }
.ui-timeline { display: flex; flex-direction: column; gap: var(--ui-space-3); padding-left: var(--ui-space-4); border-left: 2px solid var(--ui-border); }
.ui-timeline-item { display: flex; flex-direction: column; gap: var(--ui-space-1); position: relative; }
.ui-timeline-item::before { content: ""; position: absolute; left: calc(-1 * var(--ui-space-4) - 5px); top: 0.4rem; width: 8px; height: 8px; border-radius: 50%; background: var(--ui-primary); }
.ui-timeline-time { font-size: var(--ui-text-xs); color: var(--ui-fg-muted); }
.ui-timeline-body { font-size: var(--ui-text-sm); }

/* Datatables.net integration */
.ui-data-table { width: 100%; border-collapse: collapse; font-size: var(--ui-text-sm); }
.ui-data-table th, .ui-data-table td { padding: var(--ui-space-2) var(--ui-space-3); border-bottom: var(--ui-border-width) solid var(--ui-border); text-align: left; }
.ui-data-table thead th { background: var(--ui-bg-muted); font-weight: var(--ui-font-semibold); color: var(--ui-fg); }
.ui-data-table tbody tr:hover { background: var(--ui-bg-muted); }
.ui-dt-toolbar, .ui-dt-foot { display: flex; align-items: center; gap: var(--ui-space-3); padding: var(--ui-space-2) 0; }
.ui-dt-search input { padding: var(--ui-space-1) var(--ui-space-3); border: var(--ui-border-width) solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-bg-elev); }
.ui-dt-buttons { display: flex; gap: var(--ui-space-2); }
.ui-dt-paging { margin-left: auto; display: flex; gap: var(--ui-space-1); }
.ui-dt-info { color: var(--ui-fg-muted); font-size: var(--ui-text-xs); }
.ui-dt-length, .ui-dt-body { display: flex; align-items: center; gap: var(--ui-space-2); }

/* Misc */
.ui-print-label-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--ui-space-3); padding: var(--ui-space-2); }
.ui-print-label-sheet { page-break-after: always; break-after: page; }
.ui-print-label-sheet + .ui-print-label-sheet { margin-top: var(--ui-space-4); padding-top: var(--ui-space-3); border-top: 1px dashed var(--ui-border); }
@media print { .ui-print-label-sheet + .ui-print-label-sheet { margin-top: 0; padding-top: 0; border-top: none; } }
.ui-print-label { display: flex; align-items: center; gap: var(--ui-space-3); padding: var(--ui-space-3); border: 1px solid var(--ui-border-strong); border-radius: var(--ui-radius-sm); background: var(--ui-bg-elev); font-size: var(--ui-text-sm); }
.ui-print-label-qr { width: 5rem; height: 5rem; flex-shrink: 0; background: var(--ui-bg-muted); display: flex; align-items: center; justify-content: center; font-family: var(--ui-font-mono); font-size: var(--ui-text-2xs); border-radius: var(--ui-radius-sm); }
.ui-map-marker { width: 1.1rem; height: 1.1rem; border-radius: 50%; background: var(--ui-marker-color, var(--ui-primary)); border: 2px solid var(--ui-bg-elev); box-shadow: var(--ui-shadow-md); cursor: pointer; transition: transform 120ms ease, box-shadow 120ms ease; }
.ui-map-marker:hover { transform: scale(1.2); }
.ui-map-marker.ui-map-marker-highlight { transform: scale(1.4); box-shadow: 0 0 0 3px var(--ui-primary-bg-soft), var(--ui-shadow-lg); z-index: 2; }
.ui-chart.ui-chart-map { height: clamp(22rem, 65vh, 44rem); }
.ui-chart.ui-chart-map .ui-chart-frame { flex: 1 1 auto; min-height: 0; }
.ui-chart.ui-chart-graph { height: clamp(20rem, 55vh, 36rem); }
.ui-chart.ui-chart-graph .ui-chart-frame { flex: 1 1 auto; min-height: 0; }
.ui-chart-host { display: flex; flex-direction: column; flex: 1 1 auto; min-height: clamp(18rem, 50vh, 32rem); }
.ui-toggle-grid-table { border-collapse: collapse; }
.ui-toggle-grid-table th, .ui-toggle-grid-table td { padding: var(--ui-space-1) var(--ui-space-2); }
.ui-toggle-grid-rowlabel { font-weight: var(--ui-font-semibold); }
.ui-toggle-grid-cell { text-align: center; cursor: pointer; min-width: 2rem; border-radius: var(--ui-radius-sm); transition: background var(--ui-transition-fast); }
.ui-toggle-grid-cell:hover { background: var(--ui-bg-muted); }
.ui-toggle-grid-cell.ui-active { background: var(--ui-primary); color: var(--ui-on-primary); }
.ui-aggregate-metrics { display: flex; flex-wrap: wrap; gap: var(--ui-space-2); padding: var(--ui-space-2); }
.ui-bind-list-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ui-space-2); margin-bottom: var(--ui-space-2); }
.ui-bind-list-bar input[type=search] { max-width: none; }
/* Compact search field with an integrated + adder — one unit, not a separate
   text button. The input loses its right radius/border; the + button caps it. */
.ui-bind-search { display: flex; align-items: stretch; flex: 1 1 auto; width: 100%; }
.ui-bind-search .ui-input { flex: 1 1 auto; min-width: 0; max-width: none; padding: var(--ui-space-1) var(--ui-space-2); font-size: var(--ui-text-sm); border-right: 0; border-radius: var(--ui-radius-md) 0 0 var(--ui-radius-md); }
.ui-bind-search-add { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; padding: 0 var(--ui-space-2); border: var(--ui-border-width) solid var(--ui-border); border-radius: 0 var(--ui-radius-md) var(--ui-radius-md) 0; background: var(--ui-bg-elev); color: var(--ui-fg-muted); cursor: pointer; font-size: var(--ui-text-sm); transition: background var(--ui-transition-fast), color var(--ui-transition-fast); }
.ui-bind-search-add:hover { background: var(--ui-primary); color: var(--ui-on-primary); border-color: var(--ui-primary); }
.ui-bind-search-add-solo { border-radius: var(--ui-radius-md); padding: var(--ui-space-1) var(--ui-space-2); }
.ui-bind-collection { display: flex; flex-wrap: wrap; gap: var(--ui-space-3); padding: var(--ui-space-2); align-items: flex-start; }

/* ── Labeller-surface decorators + selector templates ─────────────── */
.ui-pill { display: inline-flex; align-items: center; padding: 1px var(--ui-space-2); border-radius: var(--ui-radius-full); font-size: var(--ui-text-2xs); font-weight: var(--ui-font-semibold); text-transform: uppercase; letter-spacing: 0.04em; color: #fff; background: var(--ui-pill-color, var(--ui-fg-muted)); }
/* Tone classes need to win over the variable-based .ui-pill above (which is
   declared later than the tone rules near .ui-pill-success); use compound
   selectors for higher specificity so the tone colour applies. */
.ui-pill.ui-pill-success { background: var(--ui-success); color: #fff; }
.ui-pill.ui-pill-warn    { background: var(--ui-warn);    color: #fff; }
.ui-pill.ui-pill-danger  { background: var(--ui-danger);  color: #fff; }
.ui-pill.ui-pill-primary { background: var(--ui-primary); color: #fff; }
.ui-bind-list { padding: var(--ui-space-2); }
.ui-bind-list-rows { display: flex; flex-direction: column; }
/* §5c — fixed row anatomy: avatar | head(flex, clipped) | pills(capped). The
   row never wraps; each text line is clipped to one line; pills can't dominate. */
.ui-bind-list-row { position: relative; min-width: 0; display: flex; flex-wrap: nowrap; align-items: center; column-gap: var(--ui-space-2); row-gap: 0; }
.ui-bind-list-head { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; line-height: 1.25; }
/* Clip title + subtitle to one line each, regardless of the call-site shape. */
.ui-bind-list-head > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.ui-bind-list-pills, .ui-bind-card-pills, .ui-bind-tree-pills { display: flex; flex-wrap: nowrap; gap: var(--ui-space-1); margin-left: auto; align-items: center; flex: 0 0 auto; min-width: 0; max-width: 55%; overflow: hidden; }
/* A pill takes the space its label needs and no more; long labels clip. */
.ui-bind-list-pills > *, .ui-bind-tree-pills > * { flex: 0 1 auto; min-width: 0; max-width: 16ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-bind-list-progress { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--ui-bg-muted); }
.ui-bind-list-progress::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--ui-progress-pct, 0%); background: var(--ui-progress-color, var(--ui-primary)); transition: width var(--ui-transition-base) var(--ui-ease); }
.ui-bind-entity-card-header { display: flex; align-items: center; gap: var(--ui-space-3); margin-bottom: var(--ui-space-2); }
.ui-bind-card-progress { margin-top: var(--ui-space-3); }
.ui-bind-entity-card-actions { position: absolute; top: var(--ui-space-2); right: var(--ui-space-2); display: flex; gap: var(--ui-space-1); opacity: 0; transition: opacity var(--ui-transition-fast); }
.ui-bind-entity-card:hover .ui-bind-entity-card-actions { opacity: 1; }
.ui-bind-dropdown { width: 100%; }
.ui-bind-chips     { display: flex; flex-wrap: wrap; gap: var(--ui-space-1); padding: var(--ui-space-2); }
.ui-bind-chips .ui-chip { cursor: pointer; }
.ui-bind-gallery   { display: flex; flex-direction: column; gap: var(--ui-space-2); padding: var(--ui-space-2); }
.ui-bind-gallery-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--ui-space-3); align-content: start; }
.ui-bind-gallery-tile { display: flex; flex-direction: column; cursor: pointer; border-radius: var(--ui-radius-lg); overflow: hidden; background: var(--ui-bg-elev); border: var(--ui-border-width) solid var(--ui-border); box-shadow: var(--ui-shadow-sm); transition: box-shadow var(--ui-transition-base), transform var(--ui-transition-base); }
.ui-bind-gallery-tile:hover { transform: translateY(-2px); box-shadow: var(--ui-shadow-md); }
.ui-bind-gallery-tile.ui-active { outline: 3px solid var(--ui-primary); outline-offset: -3px; }
.ui-bind-gallery-image { aspect-ratio: 1; background-size: cover; background-position: center; background-color: var(--ui-bg-muted); display: flex; align-items: center; justify-content: center; }
.ui-bind-gallery-image-initials .ui-avatar { width: 50%; height: 50%; }
.ui-bind-gallery-caption { padding: var(--ui-space-2) var(--ui-space-3) var(--ui-space-3); }
.ui-bind-compact   { padding: var(--ui-space-1); }
.ui-bind-compact-row { display: flex; align-items: center; gap: var(--ui-space-2); padding: var(--ui-space-1) var(--ui-space-2); cursor: pointer; border-radius: var(--ui-radius-sm); transition: background var(--ui-transition-fast); }
.ui-bind-compact-row:hover { background: var(--ui-bg-muted); }
.ui-bind-compact-row.ui-active { background: var(--ui-primary-100); color: var(--ui-primary-700); font-weight: var(--ui-font-semibold); }
.ui-bind-segmented { display: inline-flex; padding: 2px; gap: 2px; background: var(--ui-bg-muted); border-radius: var(--ui-radius-md); margin: var(--ui-space-2); }
.ui-bind-segmented .ui-segmented-item { cursor: pointer; white-space: nowrap; }
.ui-bind-segmented .ui-segmented-item.ui-active { background: var(--ui-bg-elev); box-shadow: var(--ui-shadow-sm); color: var(--ui-fg); }
.ui-metric-chip-icon { display: inline-flex; align-items: center; }
/* Row actions sit on their OWN line BELOW the label (flex-basis:100% wraps
   them under the item) rather than overlaying it — on a narrow tree/list the
   icons no longer smother the text. The actions div is a direct child of the
   row, so moving the cursor down onto a button keeps the row :hover (no
   disappear-trap); they also stay shown on the active row. Collapsed to zero
   height when idle so quiescent rows keep their single-line density. */
/* Row actions OVERLAY the right edge of the row on hover/active — same line,
   over the text, never a second line and never reflowing the label. The
   overlay carries the row's own background (inherit) so the text it covers is
   masked, and a left fade blends it into the label underneath. */
.ui-bind-list-row, .ui-bind-tree-node { position: relative; }
.ui-bind-list-actions {
    position: absolute; top: 0; bottom: 0; right: 0; order: 0;
    display: flex; align-items: center; gap: var(--ui-space-1);
    margin: 0; padding: 0 var(--ui-space-2) 0 var(--ui-space-5);
    max-height: none; overflow: visible; background: inherit;
    opacity: 0; pointer-events: none; transition: opacity var(--ui-transition-fast);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--ui-space-4));
            mask-image: linear-gradient(to right, transparent, #000 var(--ui-space-4));
}
.ui-bind-list-row:hover > .ui-bind-list-actions, .ui-bind-list-row.ui-active > .ui-bind-list-actions,
.ui-bind-tree-node:hover > .ui-bind-list-actions, .ui-bind-tree-node.ui-active > .ui-bind-list-actions { opacity: 1; pointer-events: auto; }
.ui-bind-tree-node .ui-icon-btn, .ui-bind-list-row .ui-icon-btn { width: 1.1rem; height: 1.1rem; padding: 0; font-size: 0.7rem; }
.ui-mgr-pane { flex: 1 1 0; min-width: clamp(220px, 20vw, 320px); }
.ui-drilldown-list { display: flex; flex-direction: column; }
.ui-drilldown-row { display: flex; justify-content: space-between; padding: var(--ui-space-2) var(--ui-space-3); cursor: pointer; border-bottom: var(--ui-border-width) solid var(--ui-border); transition: background var(--ui-transition-fast); }
.ui-drilldown-row:hover { background: var(--ui-bg-muted); }
.ui-stepper-done { color: var(--ui-success); }
.ui-wizard-body { padding: var(--ui-space-4); min-height: 8rem; }
.ui-wizard-footer { display: flex; justify-content: space-between; padding: var(--ui-space-3); border-top: var(--ui-border-width) solid var(--ui-border); }
.ui-action { display: inline-flex; gap: var(--ui-space-1); }
/* Hyper-parameter live-edit panel (gallery) */
.ui-hp-controls { display: flex; flex-direction: column; gap: var(--ui-space-3); padding: var(--ui-space-3) var(--ui-space-4); margin-bottom: var(--ui-space-3); background: var(--ui-bg-elev); border: var(--ui-border-width) solid var(--ui-border); border-radius: var(--ui-radius-lg); }
.ui-hp-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ui-space-3); }
.ui-hp-row-label { font-size: var(--ui-text-xs); font-weight: var(--ui-font-semibold); color: var(--ui-fg-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.ui-hp-chips { display: flex; flex-wrap: wrap; gap: var(--ui-space-1); }
.ui-hp-chips .ui-chip { font-size: var(--ui-text-xs); padding: var(--ui-space-1) var(--ui-space-2); }
.ui-hp-font   { width: auto; min-width: 9rem; padding: var(--ui-space-1) var(--ui-space-2); font-size: var(--ui-text-sm); }
.ui-hp-weight { width: auto; min-width: 7rem; padding: var(--ui-space-1) var(--ui-space-2); font-size: var(--ui-text-sm); }
.ui-hp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--ui-space-3); padding-top: var(--ui-space-2); border-top: var(--ui-border-width) solid var(--ui-border); }
.ui-hp-cell { display: flex; flex-direction: column; gap: var(--ui-space-1); font-size: var(--ui-text-xs); color: var(--ui-fg-muted); }
.ui-hp-cell label { display: flex; align-items: center; justify-content: space-between; gap: var(--ui-space-2); font-weight: var(--ui-font-medium); color: var(--ui-fg); text-transform: uppercase; letter-spacing: 0.04em; font-size: var(--ui-text-2xs); }
.ui-hp-readout { font-family: var(--ui-font-mono); font-size: var(--ui-text-xs); color: var(--ui-fg-muted); padding: 0 var(--ui-space-1); background: var(--ui-bg-muted); border-radius: var(--ui-radius-sm); }
.ui-hp-slider { width: 100%; appearance: none; height: 4px; background: var(--ui-bg-muted); border-radius: var(--ui-radius-full); outline: none; }
.ui-hp-slider::-webkit-slider-thumb { appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--ui-primary); cursor: pointer; box-shadow: var(--ui-shadow-sm); transition: transform var(--ui-transition-fast); }
.ui-hp-slider::-webkit-slider-thumb:hover { transform: scale(1.15); }
.ui-hp-slider::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--ui-primary); cursor: pointer; box-shadow: var(--ui-shadow-sm); }
.ui-hp-cell-color { gap: 4px; }
.ui-hp-color-row { display: flex; align-items: center; gap: var(--ui-space-2); }
.ui-hp-color-row > span { font-family: var(--ui-font-mono); font-size: var(--ui-text-2xs); color: var(--ui-fg-muted); width: 0.9rem; flex-shrink: 0; }
.ui-hp-color-row .ui-hp-slider { flex: 1; }
.ui-hp-swatch { display: inline-block; width: 1.1rem; height: 1.1rem; border-radius: var(--ui-radius-sm); border: var(--ui-border-width) solid var(--ui-border); }
.ui-hp-snippet { display: flex; flex-direction: column; gap: var(--ui-space-2); padding-top: var(--ui-space-2); border-top: var(--ui-border-width) solid var(--ui-border); }
.ui-hp-snippet-bar { display: flex; align-items: center; gap: var(--ui-space-2); }
.ui-hp-snippet-name { flex: 0 1 12rem; font-family: var(--ui-font-mono); font-size: var(--ui-text-sm); }
.ui-hp-snippet-text { width: 100%; font-family: var(--ui-font-mono); font-size: var(--ui-text-xs); line-height: 1.5; resize: vertical; background: var(--ui-bg-sunken); }

/* ---- 7a. Bundled theme variants ---- comprehensive (font/weight/spacing/
 * corner/border/shadow/motion/hue) at the top, scoped tweaks at the bottom. */
.ui-theme-apple {
    --hp-font-heading: var(--hp-font-stack-display); --hp-font-body: var(--hp-font-stack-system); --hp-font-mono: var(--hp-font-stack-mono);
    --hp-weight-heading: 700; --hp-weight-body: 400; --hp-density: 1.15; --hp-pad-base: 0.55rem;
    --hp-text-ratio: 1.22; --hp-line-height: 1.5; --hp-corner-scale: 1.5; --hp-border-scale: 0.5;
    --hp-shadow-intensity: 0.08; --hp-transition-speed: 0.9;
    --hp-hue-1: 211; --hp-sat-1: 100%; --hp-hue-2: 268; --hp-sat-2: 70%; --hp-hue-accent: 32; --hp-sat-accent: 95%;
}
.ui-theme-metro {
    --hp-font-heading: var(--hp-font-stack-system); --hp-font-body: var(--hp-font-stack-system); --hp-font-mono: var(--hp-font-stack-mono);
    --hp-weight-heading: 300; --hp-weight-body: 400; --hp-density: 0.95; --hp-pad-base: 0.45rem;
    --hp-line-height: 1.4; --hp-corner-scale: 0; --hp-border-scale: 0; --hp-shadow-intensity: 0; --hp-transition-speed: 1.2;
    --hp-hue-1: 200; --hp-sat-1: 100%; --hp-hue-2: 14; --hp-sat-2: 92%; --hp-hue-accent: 130; --hp-sat-accent: 90%;
}
.ui-theme-material {
    --hp-font-heading: var(--hp-font-stack-roboto); --hp-font-body: var(--hp-font-stack-roboto); --hp-font-mono: var(--hp-font-stack-roboto-mono);
    --hp-weight-heading: 500; --hp-weight-body: 400; --hp-corner-scale: 0.5; --hp-border-scale: 0.7;
    --hp-shadow-intensity: 0.22;
    --hp-hue-1: 207; --hp-sat-1: 90%; --hp-hue-2: 174; --hp-sat-2: 70%; --hp-hue-accent: 36; --hp-sat-accent: 100%;
}
.ui-theme-brutalist {
    --hp-font-heading: var(--hp-font-stack-bebas); --hp-font-body: var(--hp-font-stack-mono); --hp-font-mono: var(--hp-font-stack-mono);
    --hp-weight-heading: 400; --hp-weight-body: 500; --hp-density: 1.05; --hp-text-scale: 1.05; --hp-text-ratio: 1.4;
    --hp-line-height: 1.4; --hp-corner-scale: 0; --hp-border-scale: 2.5; --hp-shadow-intensity: 0; --hp-transition-speed: 0.5;
    --hp-hue-1: 0; --hp-sat-1: 0%; --hp-hue-2: 0; --hp-sat-2: 0%; --hp-hue-accent: 50; --hp-sat-accent: 100%;
}
.ui-theme-newspaper {
    --hp-font-heading: var(--hp-font-stack-playfair); --hp-font-body: var(--hp-font-stack-source); --hp-font-mono: var(--hp-font-stack-source-code);
    --hp-weight-heading: 700; --hp-density: 0.95; --hp-text-ratio: 1.3; --hp-line-height: 1.65;
    --hp-corner-scale: 0.2; --hp-border-scale: 0.5; --hp-shadow-intensity: 0.04;
    --hp-hue-1: 24; --hp-sat-1: 60%; --hp-hue-2: 15; --hp-sat-2: 35%; --hp-hue-accent: 0; --hp-sat-accent: 70%;
}
.ui-theme-mono {
    --hp-font-heading: var(--hp-font-stack-jetbrains); --hp-font-body: var(--hp-font-stack-jetbrains); --hp-font-mono: var(--hp-font-stack-jetbrains);
    --hp-weight-heading: 600; --hp-text-scale: 0.94; --hp-line-height: 1.55;
    --hp-corner-scale: 0.4; --hp-border-scale: 0.8; --hp-shadow-intensity: 0.06;
    --hp-hue-1: 152; --hp-sat-1: 55%; --hp-hue-2: 25; --hp-sat-2: 70%; --hp-hue-accent: 268; --hp-sat-accent: 70%;
}
.ui-theme-serif {
    --hp-font-heading: var(--hp-font-stack-garamond); --hp-font-body: var(--hp-font-stack-garamond); --hp-font-mono: var(--hp-font-stack-mono);
    --hp-weight-heading: 600; --hp-density: 1.05; --hp-text-scale: 1.05; --hp-text-ratio: 1.28; --hp-line-height: 1.6;
    --hp-corner-scale: 0.4; --hp-border-scale: 0.5; --hp-shadow-intensity: 0.06;
    --hp-hue-1: 212; --hp-sat-1: 45%; --hp-hue-2: 14; --hp-sat-2: 50%; --hp-hue-accent: 33; --hp-sat-accent: 70%;
}
/* Scoped tweaks — colour-only or density-only. */
.ui-theme-emerald  { --hp-hue-1: 158; --hp-sat-1: 70%; --hp-hue-2: 195; }
.ui-theme-amber    { --hp-hue-1: 32;  --hp-sat-1: 92%; --hp-hue-2: 14;  --hp-sat-2: 80%; }
.ui-theme-violet   { --hp-hue-1: 268; --hp-sat-1: 70%; --hp-hue-2: 320; --hp-sat-2: 65%; }
.ui-theme-slate    { --hp-hue-1: 215; --hp-sat-1: 18%; --hp-hue-2: 215; --hp-sat-2: 18%; --hp-shadow-intensity: 0.05; }
.ui-theme-compact  { --hp-density: 0.8; --hp-text-scale: 0.92; --hp-corner-scale: 0.8; }
.ui-theme-spacious { --hp-density: 1.2; --hp-text-scale: 1.06; --hp-corner-scale: 1.2; }
.ui-theme-rounded  { --hp-corner-scale: 1.6; }
.ui-theme-sharp    { --hp-corner-scale: 0; --hp-border-scale: 1.2; }

/* ---- 7b. Animations ---- */
@keyframes ui-fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes ui-fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.ui-pageframe-stage > *, .ui-pageframe-body > * { animation: ui-fade-in var(--ui-transition-base) var(--ui-ease) both; }
.ui-modal { animation: ui-fade-up var(--ui-transition-base) var(--ui-ease) both; }

/* ---- 8. Responsive ---- */
@media (max-width: 640px) {
    :root { --hp-density: 0.9; --hp-text-scale: 0.94; }
    .ui-pageframe-header { flex-wrap: wrap; }
    .ui-pageframe-actions { margin-left: 0; }
    .ui-data-cards-grid > *, .ui-bind-cards > * { flex-basis: 100%; max-width: 100%; }
    .ui-modal { padding: var(--ui-space-4); border-radius: var(--ui-radius-lg); }
}
@media (min-width: 1600px) { :root { --hp-text-scale: 1.05; } }

/* Heatmap — service-agnostic band-coloured grid (used by Accreditation matrix). */
.ui-heatmap { border-collapse: collapse; width: 100%; font-size: var(--ui-text-sm); }
.ui-heatmap th, .ui-heatmap td { border: 1px solid var(--ui-gray-200); padding: var(--ui-space-2); text-align: center; min-width: 64px; }
.ui-heatmap td:first-child, .ui-heatmap th:first-child { text-align: left; font-weight: var(--ui-font-semibold); }
.ui-band { cursor: pointer; transition: opacity var(--ui-transition-fast); }
.ui-band:hover { opacity: 0.85; outline: 2px solid var(--ui-primary); outline-offset: -2px; }
.ui-band-emerging   { background: var(--ui-danger-100);  color: var(--ui-danger); }
.ui-band-developed  { background: var(--ui-warn-100);    color: var(--ui-warn); }
.ui-band-exit-level { background: var(--ui-success-100); color: var(--ui-success); }
.ui-band-exceeds    { background: var(--ui-primary-100); color: var(--ui-primary); }
.ui-band-not-met    { background: var(--ui-gray-100);    color: var(--ui-text-muted); }
.ui-band-empty      { color: var(--ui-text-muted); }

/* Trend bar — cross-scan metric strip (PerformanceScanner.renderMetricTrend). */
.ui-trend-bar { flex: 1; height: 12px; background: var(--ui-gray-100); border-radius: var(--ui-radius-sm); position: relative; overflow: hidden; }
.ui-trend-bar::after { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: var(--w, 0%); background: var(--ui-primary); }

/* uiMcq — multiple-choice question primitive (class.ui.js). One ruleset
   per concept; lean. Hyper-tokens used directly so themes can re-skin. */
.ui-mcq { background: var(--ui-bg-elevated); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-lg); padding: var(--ui-space-4) var(--ui-space-5); }
.ui-mcq-tag { text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--ui-text-xs); font-weight: 600; color: var(--ui-text-muted); margin-bottom: var(--ui-space-2); }
.ui-mcq-statement { font-size: var(--ui-text-base); margin-bottom: var(--ui-space-3); }
.ui-mcq-options { display: flex; flex-direction: column; gap: var(--ui-space-2); margin-bottom: var(--ui-space-2); }
.ui-mcq-option { display: flex; align-items: flex-start; gap: var(--ui-space-3); padding: var(--ui-space-2) var(--ui-space-3); background: var(--ui-bg); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-md); cursor: pointer; text-align: left; font-size: var(--ui-text-sm); transition: border-color 100ms, background 100ms; }
.ui-mcq-option:hover:not(:disabled) { border-color: var(--ui-primary); }
.ui-mcq-option:disabled { cursor: default; }
.ui-mcq-letter { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--ui-radius-full, 999px); background: var(--ui-bg-muted); color: var(--ui-text-muted); font-weight: 700; font-size: var(--ui-text-xs); flex-shrink: 0; }
.ui-mcq-label { flex: 1; }
.ui-mcq-label > p:first-child { margin-top: 0; }
.ui-mcq-correct { background: var(--ui-success-100); border-color: var(--ui-success); }
.ui-mcq-correct .ui-mcq-letter { background: var(--ui-success); color: white; }
.ui-mcq-wrong { background: var(--ui-danger-100); border-color: var(--ui-danger); }
.ui-mcq-wrong .ui-mcq-letter { background: var(--ui-danger); color: white; }
.ui-mcq-result { font-weight: 600; margin-top: var(--ui-space-2); }
.ui-mcq-result-correct { color: var(--ui-success); }
.ui-mcq-result-wrong { color: var(--ui-danger); }
.ui-mcq-solution { background: var(--ui-bg); padding: var(--ui-space-3); border-radius: var(--ui-radius-md); border-left: 3px solid var(--ui-primary); margin-top: var(--ui-space-2); }

/* Casework Workbench — two-region layout. A fixed-width side panel (selected-
   case detail stacked above the category rail) and a wide kanban board that
   takes all remaining width so lanes aren't forced thin. Wraps on narrow. */
.ui-cw-workbench { flex-wrap: wrap; }
.ui-cw-pane { display: flex; flex-direction: column; min-width: 0; gap: var(--ui-space-3); }
.ui-cw-pane-side  { flex: 0 0 22rem; }
.ui-cw-pane-board { flex: 1 1 0; min-width: 0; }
.ui-cw-detail { display: flex; flex-direction: column; gap: var(--ui-space-3); }
@media (max-width: 60rem) {
    .ui-cw-pane-side, .ui-cw-pane-board { flex: 1 1 100%; }
}

/* Chart canvas — responsive container for embedded Plotly plots
   (used by Runbook, Ordering, Specimen, etc.). clamp() scales with viewport
   while keeping a usable minimum + maximum so Plotly always has size. */
.ui-rb-chart { width: 100%; height: clamp(16rem, 40vh, 28rem); }

/* Workload utilization bars — width driven by --util-pct CSS variable set
   inline (data-bound, not layout). Colour band via modifier class. */
.ui-wl-util-row   { display: flex; align-items: center; gap: var(--ui-space-2); margin: var(--ui-space-1) 0; }
.ui-wl-util-label { flex: 0 0 9rem; font-size: var(--ui-text-sm); color: var(--ui-fg-muted); }
.ui-wl-util-bar   { flex: 1 1 auto; height: 10px; background: var(--ui-bg-muted); border-radius: var(--ui-radius-sm); overflow: hidden; min-width: 4rem; }
.ui-wl-util-fill  { display: block; height: 100%; background: var(--ui-success); transition: width 0.3s; width: var(--util-pct, 0%); }
.ui-wl-util-fill.ui-wl-util-amber { background: var(--ui-warning); }
.ui-wl-util-fill.ui-wl-util-red   { background: var(--ui-danger); }
.ui-wl-util-fill.ui-wl-util-dark  { background: var(--ui-danger-700); }
.ui-wl-util-pct   { flex: 0 0 3.5rem; text-align: right; font-variant-numeric: tabular-nums; font-size: var(--ui-text-sm); }

/* Analytics KPI cards — flex grid of selectable cards, each with a large
   value number, target context, status badge, and trend arrow. Card colour
   accent and trend glyph are data-driven via the AN_STATUS_* maps in code. */
.ui-an-kpi-grid   { display: flex; flex-wrap: wrap; gap: var(--ui-space-3); }
.ui-an-kpi-card   { flex: 1 1 14rem; min-width: 12rem; padding: var(--ui-space-3); border-radius: var(--ui-radius-lg); background: var(--ui-bg-elev); border: 1px solid var(--ui-border); cursor: pointer; transition: box-shadow var(--ui-transition-fast); display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-an-kpi-card:hover { box-shadow: var(--ui-shadow-md); }
.ui-an-kpi-card.ui-active { border-color: var(--ui-primary); box-shadow: 0 0 0 2px var(--ui-primary-100); }
.ui-an-kpi-name   { font-size: var(--ui-text-sm); color: var(--ui-fg-muted); font-weight: var(--ui-font-semibold); }
.ui-an-kpi-value  { font-size: 2rem; font-weight: var(--ui-font-bold); font-variant-numeric: tabular-nums; line-height: 1.1; }
.ui-an-kpi-unit   { font-size: var(--ui-text-sm); color: var(--ui-fg-muted); margin-left: var(--ui-space-1); }
.ui-an-kpi-meta   { display: flex; align-items: center; justify-content: space-between; gap: var(--ui-space-2); font-size: var(--ui-text-xs); color: var(--ui-fg-muted); }
.ui-an-kpi-trend  { font-variant-numeric: tabular-nums; }
.ui-an-kpi-trend.ui-an-up    { color: var(--ui-success); }
.ui-an-kpi-trend.ui-an-down  { color: var(--ui-danger); }
.ui-an-kpi-trend.ui-an-flat  { color: var(--ui-fg-muted); }

/* Specimen / Chain-of-Custody — bespoke visuals for the LIMS service. */
.ui-spc-tree { display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-spc-tree-node { padding: var(--ui-space-2) var(--ui-space-3); border-left: 3px solid var(--ui-border); border-radius: var(--ui-radius-md); background: var(--ui-bg); margin-left: calc(var(--tree-depth, 0) * var(--ui-space-4)); }
.ui-spc-tree-selected { border-left-color: var(--ui-primary); background: var(--ui-primary-50); }
.ui-spc-tree-head { display: flex; align-items: center; gap: var(--ui-space-2); flex-wrap: wrap; }
.ui-spc-tree-bullet { color: var(--ui-fg-muted); }

.ui-spc-timeline { display: flex; flex-direction: column; gap: var(--ui-space-2); }
.ui-spc-timeline-row { display: flex; gap: var(--ui-space-3); padding: var(--ui-space-2) var(--ui-space-3); border-left: 3px solid var(--ui-primary); background: var(--ui-bg); border-radius: var(--ui-radius-md); }
.ui-spc-timeline-icon { font-size: 1.25rem; flex: 0 0 1.5rem; }
.ui-spc-timeline-body { flex: 1 1 auto; min-width: 0; }

.ui-spc-fill { background: var(--ui-bg-muted); border-radius: var(--ui-radius-full); overflow: hidden; height: 0.5rem; margin-top: var(--ui-space-2); }
.ui-spc-fill-bar { height: 100%; transition: width 200ms ease; width: var(--fill-pct, 0%); }
.ui-spc-fill-success { background: var(--ui-success); }
.ui-spc-fill-warn    { background: var(--ui-warn); }
.ui-spc-fill-danger  { background: var(--ui-danger); }

.ui-spc-chips { display: flex; flex-wrap: wrap; gap: var(--ui-space-1); margin-top: var(--ui-space-2); }
.ui-spc-chip { display: inline-flex; align-items: center; padding: 1px var(--ui-space-2); border-radius: var(--ui-radius-sm); font-size: var(--ui-text-xs); background: var(--ui-bg-muted); color: var(--ui-fg); border: 1px solid var(--ui-border); }
.ui-spc-chip-more { background: transparent; font-style: italic; color: var(--ui-fg-muted); }

.ui-spc-due { padding: var(--ui-space-2) var(--ui-space-3); border-radius: var(--ui-radius-md); border-left: 3px solid var(--ui-warn); background: var(--ui-bg); font-size: var(--ui-text-sm); }
.ui-spc-due-past { border-left-color: var(--ui-danger); background: var(--ui-danger-50); }

.ui-btn-tiny { padding: 0 var(--ui-space-2); font-size: var(--ui-text-xs); height: auto; line-height: 1.4; }
.ui-btn-success { background: var(--ui-success); color: #fff; border-color: var(--ui-success); }
.ui-btn-success:hover { filter: brightness(0.95); }

/* Procedure — scope-of-accreditation matrix (analytes × matrices). */
.ui-proc-scope { display: flex; flex-direction: column; gap: 2px; }
.ui-proc-scope-row { display: flex; gap: 2px; }
.ui-proc-scope-cell { flex: 1 1 0; min-width: 6rem; padding: var(--ui-space-2); border-radius: var(--ui-radius-sm); background: var(--ui-bg); border: 1px solid var(--ui-border); font-size: var(--ui-text-sm); display: flex; flex-wrap: wrap; gap: var(--ui-space-1); align-items: center; }
.ui-proc-scope-corner { background: var(--ui-bg-muted); font-style: italic; color: var(--ui-fg-muted); justify-content: center; }
.ui-proc-scope-mhead { background: var(--ui-bg-muted); font-weight: var(--ui-font-semibold); justify-content: center; }
.ui-proc-scope-ahead { background: var(--ui-bg-muted); font-weight: var(--ui-font-semibold); }
.ui-proc-scope-success { background: var(--ui-success-50); border-color: var(--ui-success); }
.ui-proc-scope-warn    { background: var(--ui-warn-50); border-color: var(--ui-warn); }
.ui-proc-scope-danger  { background: var(--ui-danger-50); border-color: var(--ui-danger); }
.ui-proc-scope-muted   { background: var(--ui-bg-muted); }
.ui-proc-scope-pill { display: inline-flex; padding: 1px var(--ui-space-2); border-radius: var(--ui-radius-sm); font-size: var(--ui-text-xs); background: var(--ui-bg); border: 1px solid var(--ui-border); cursor: help; }
.ui-proc-scope-pill.ui-proc-scope-success { background: var(--ui-success); color: #fff; border-color: var(--ui-success); }
.ui-proc-scope-pill.ui-proc-scope-warn    { background: var(--ui-warn); color: #fff; border-color: var(--ui-warn); }
.ui-proc-scope-pill.ui-proc-scope-danger  { background: var(--ui-danger); color: #fff; border-color: var(--ui-danger); }

.ui-proc-req-list { display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-proc-req-item { padding: var(--ui-space-1) var(--ui-space-2); border-left: 3px solid var(--ui-primary); background: var(--ui-bg-muted); border-radius: var(--ui-radius-sm); font-size: var(--ui-text-sm); }
.ui-proc-rev-current { border-left-color: var(--ui-success); background: var(--ui-success-50); }

/* Risk — 5×5 likelihood × impact heatmap. */
.ui-rsk-heatmap { display: flex; flex-direction: column; gap: 2px; }
.ui-rsk-heat-row { display: flex; gap: 2px; }
.ui-rsk-heat-cell { flex: 1 1 0; min-width: 6rem; min-height: 5rem; padding: var(--ui-space-2); border-radius: var(--ui-radius-sm); border: 1px solid var(--ui-border); font-size: var(--ui-text-xs); display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-rsk-heat-head    { background: var(--ui-bg-muted); font-weight: var(--ui-font-semibold); justify-content: center; align-items: center; min-height: 2rem; flex-direction: row; }
.ui-rsk-heat-corner  { background: var(--ui-bg-muted); font-style: italic; color: var(--ui-fg-muted); justify-content: center; align-items: center; min-height: 2rem; flex-direction: row; }
.ui-rsk-heat-low        { background: #d1fae5; border-color: #10b981; }
.ui-rsk-heat-mediumLow  { background: #d9f99d; border-color: #84cc16; }
.ui-rsk-heat-mediumHigh { background: #fef3c7; border-color: #f59e0b; }
.ui-rsk-heat-high       { background: #fecaca; border-color: #ef4444; }
.ui-rsk-heat-score { font-weight: var(--ui-font-semibold); font-size: var(--ui-text-sm); }
.ui-rsk-heat-chips { display: flex; flex-wrap: wrap; gap: 2px; }

.ui-rsk-card-success { border-left-color: var(--ui-success) !important; }
.ui-rsk-card-warn    { border-left-color: var(--ui-warn) !important; }
.ui-rsk-card-danger  { border-left-color: var(--ui-danger) !important; }

/* QR — printable label preview (mimics a real sticker, responsive). */
.ui-qr-label { display: flex; flex-direction: column; border: 2px dashed var(--ui-border); padding: var(--ui-space-3); border-radius: var(--ui-radius-md); background: var(--ui-bg-elev); width: 100%; max-width: 28rem; box-shadow: var(--ui-shadow-sm); }
.ui-qr-label-head { font-size: var(--ui-text-xs); font-weight: var(--ui-font-semibold); color: var(--ui-fg-muted); border-bottom: 1px solid var(--ui-border); padding-bottom: var(--ui-space-1); margin-bottom: var(--ui-space-2); text-transform: uppercase; letter-spacing: 0.06em; }
.ui-qr-label-body { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--ui-space-3); align-items: center; }
.ui-qr-canvas { flex: 0 0 auto; aspect-ratio: 1; width: clamp(8rem, 28vw, 12rem); display: flex; align-items: center; justify-content: center; background: var(--ui-bg-sunken); border: 1px solid var(--ui-border); }
.ui-qr-canvas canvas, .ui-qr-canvas img { display: block; max-width: 100%; max-height: 100%; }
.ui-qr-label-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--ui-space-1); }
.ui-qr-label-code { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: var(--ui-text-base); font-weight: var(--ui-font-semibold); word-break: break-all; }
.ui-qr-label-url  { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: var(--ui-text-xs); color: var(--ui-fg-muted); word-break: break-all; }
.ui-qr-fallback   { font-family: ui-monospace, monospace; text-align: center; color: var(--ui-fg-muted); font-size: var(--ui-text-xs); line-height: 1.4; }

/* Chat bubbles — used by MessagesService.renderConversation. Bubbles are
   left-aligned by default; .ui-msg-mine rows align right + use the primary
   colour so the user can tell their own messages apart at a glance. */
.ui-msg-list { display: flex; flex-direction: column; gap: var(--ui-space-2); padding: var(--ui-space-2); background: var(--ui-bg-sunken, var(--ui-gray-50)); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-md); }
.ui-msg-row { display: flex; flex-direction: column; align-items: flex-start; max-width: 78%; }
.ui-msg-row.ui-msg-mine { align-self: flex-end; align-items: flex-end; }
.ui-msg-meta { font-size: var(--ui-text-xs); color: var(--ui-fg-muted); margin-bottom: 2px; }
.ui-msg-bubble { background: var(--ui-bg-elev); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-lg); padding: var(--ui-space-2) var(--ui-space-3); white-space: pre-wrap; word-wrap: break-word; line-height: 1.4; }
.ui-msg-bubble.ui-msg-bubble-mine { background: var(--ui-primary-100); border-color: var(--ui-primary-200); }
.ui-msg-compose { align-items: stretch; }
.ui-msg-input { flex: 1 1 auto; resize: vertical; min-height: 2.4rem; }

/* NotificationService — bell chip + inbox card states. The unread card uses
   a primary-tinted left border so it reads as "needs attention" without
   shouting; read cards drop to the muted border colour. */
.ui-ntf-bell { font-weight: var(--ui-font-semibold); padding: var(--ui-space-1) var(--ui-space-2); }
.ui-ntf-unread { border-left: 3px solid var(--ui-primary) !important; }
.ui-ntf-read   { opacity: 0.85; }

/* ─── Pane work surface — desktop-instrument shell (see design/) ───────────
   Layout regions are PANES: full-bleed, hairline-divided, no shadow/rounding.
   Cards represent objects only. Host: .ui-panes-host wraps panes + statusbar
   and stretches to the viewport remainder via --ui-panes-minh. */
.ui-panes-host { display: flex; flex-direction: column; flex: 1 1 auto; min-height: var(--ui-panes-minh, calc(100dvh - 130px)); }
.ui-pageframe-stage:has(> .ui-panes-host) { padding: 0; }
.ui-pageframe-stage > .ui-panes-host { animation: none; }
/* Grouped tab — a segmented sub-nav above a filling body. Lets one top-level
   tab host several related views so the main nav strip doesn't overcrowd.
   The nested .ui-panes-host fills the body instead of the viewport calc. */
.ui-subnav-host { display: flex; flex-direction: column; flex: 1 1 auto; min-height: var(--ui-panes-minh, calc(100dvh - 130px)); }
.ui-subnav-bar  { flex: 0 0 auto; padding: 0 var(--ui-space-3); border-bottom: var(--ui-border-width) solid var(--ui-border); }
/* Subnav is a tabset, not a pill toggle — small, tight, flat icon+text tabs; a
   quiet primary underline marks the active one (no heavy filled pill). §0 tabs. */
.ui-subnav-bar .ui-segmented { background: transparent; padding: 0; border-radius: 0; gap: 0; }
.ui-subnav-bar .ui-segmented-item { border-radius: 0; padding: var(--ui-space-2) var(--ui-space-2); }
.ui-subnav-bar .ui-segmented-item.ui-active { background: transparent; color: var(--ui-primary); box-shadow: inset 0 -2px 0 var(--ui-primary); font-weight: var(--ui-font-semibold); }
.ui-subnav-bar .ui-segmented-item .fa { font-size: 0.9em; opacity: 0.75; }
.ui-subnav-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.ui-subnav-body > * { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.ui-subnav-body .ui-panes-host { min-height: 0; }
/* Record split — a wide central work surface beside a narrower reference rail.
   The high-dwell facet (a live conversation, the main document) takes the main
   column; low-change facets fold into the rail (an accordion). Design 2c. */
.ui-record-split { display: flex; flex: 1 1 auto; min-height: 0; }
.ui-record-main  { flex: 2 1 0; min-width: 0; display: flex; flex-direction: column; }
.ui-record-rail  { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; overflow: auto; border-left: var(--ui-border-width) solid var(--ui-border); }
.ui-pageframe-stage:has(> .ui-subnav-host) { padding: 0; }
.ui-pageframe-stage > .ui-subnav-host { animation: none; }
.ui-panes { display: flex; flex: 1 1 auto; min-height: 0; background: var(--ui-bg-elev); border-bottom: 0; }
.ui-pane  { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.ui-pane + .ui-pane { border-left: var(--ui-border-width) solid var(--ui-border); }
.ui-pane-fixed { flex: 0 0 300px; }
.ui-pane-fill  { flex: 1 1 auto; }
.ui-pane-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }
/* Responsive (D3): below 640px the side-by-side panes would cramp (titles wrap,
   the rail squeezes), so stack them vertically — the divider moves to the top
   edge and fixed-width panes go full-width. Desktop/tablet (≥640px) unchanged. */
@media (max-width: 640px) {
    .ui-panes { flex-direction: column; }
    .ui-pane-fixed { flex: 0 0 auto; }
    .ui-pane + .ui-pane { border-left: 0; border-top: var(--ui-border-width) solid var(--ui-border); }
    .ui-panes-host { min-height: var(--ui-panes-minh, calc(100dvh - 110px)); }
}
.ui-pane-head { display: flex; align-items: center; gap: var(--ui-space-2); padding: var(--ui-space-2) var(--ui-space-3); border-bottom: var(--ui-border-width) solid var(--ui-border); flex: 0 0 auto; min-height: 2.25rem; }
/* Title owns the head's free width (flex-1); meta yields by truncating —
   otherwise a long meta crushes the title to "B…" and the pane loses its identity. */
.ui-pane-head-title { font-weight: var(--ui-font-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
.ui-pane-head-meta  { color: var(--ui-fg-muted); font-size: var(--ui-text-sm); white-space: nowrap; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ui-toolbar { display: flex; align-items: center; gap: var(--ui-space-2); padding: var(--ui-space-1) var(--ui-space-2); border-bottom: var(--ui-border-width) solid var(--ui-border); flex: 0 0 auto; min-height: 2rem; }
.ui-statusbar { display: flex; align-items: center; gap: var(--ui-space-4); padding: 0 var(--ui-space-3); height: 1.5rem; border-top: var(--ui-border-width) solid var(--ui-border); background: var(--ui-bg-muted); font-size: var(--ui-text-xs); color: var(--ui-fg-muted); flex: 0 0 auto; }
.ui-statusbar strong { color: var(--ui-fg); font-weight: var(--ui-font-semibold); }
/* Record surface — facets in parallel (design/ principle 2b). */
.ui-facet-grid { display: flex; flex: 1 1 auto; min-height: 0; align-items: stretch; }
.ui-facet-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.ui-facet-col + .ui-facet-col { border-left: var(--ui-border-width) solid var(--ui-border); }
.ui-facet { display: flex; flex-direction: column; min-height: 0; border-bottom: var(--ui-border-width) solid var(--ui-border); }
.ui-facet-head { display: flex; align-items: center; gap: var(--ui-space-2); padding: var(--ui-space-1) var(--ui-space-2); border-bottom: var(--ui-border-width) solid var(--ui-border); background: var(--ui-bg-muted); flex: 0 0 auto; }
.ui-facet-title { font-size: var(--ui-text-sm); font-weight: var(--ui-font-semibold); }
.ui-facet-count { font-size: var(--ui-text-xs); color: var(--ui-fg-muted); }
.ui-facet-body { min-height: 0; max-height: 24rem; overflow: auto; }
.ui-facet-tall { max-height: none; flex: 1 1 auto; }
.ui-pane-info { border-top: var(--ui-border-width) solid var(--ui-border); padding: var(--ui-space-2) var(--ui-space-3); flex: 0 0 auto; }
.ui-panes .ui-bind-layout { border: 0; box-shadow: none; border-radius: 0; }
/* Chart frames flatten inside panes too — the card chrome (border/radius/
   padding) is for charts standing alone on a page, not pane central elements. */
.ui-panes .ui-chart, .ui-panes .ui-chart-frame { border: 0; box-shadow: none; border-radius: 0; padding: 0; background: transparent; }
.ui-panes .ui-avatar { display: none; }  /* instrument lists are text-first — no identity circles */
.ui-feed { display: flex; flex-direction: column; }
.ui-feed-row { display: flex; flex-direction: column; gap: var(--ui-space-1); padding: var(--ui-space-2) var(--ui-space-3); border-bottom: var(--ui-border-width) solid var(--ui-border); }
.ui-feed-row-click { cursor: pointer; }
.ui-feed-row-click:hover { background: var(--ui-bg-muted); }
/* Native colour swatch in a toolbar — without a size it collapses to a sliver. */
.ui-input-color { inline-size: 2.5rem; block-size: 1.9rem; padding: 2px; flex: 0 0 auto; }
.ui-form-modal { min-width: min(420px, 80vw); }
.ui-input-invalid { border-color: var(--ui-danger) !important; }
.ui-pill-toggle { cursor: pointer; }
.ui-pill.ui-pill-off { background: var(--ui-bg-muted); color: var(--ui-fg-muted); }
.ui-dot { width: 14px; height: 14px; border-radius: 50%; display: inline-block; flex: 0 0 auto; background: var(--ui-dot-color, var(--ui-fg-muted)); }
.ui-input-compact { width: auto; flex: 0 0 auto; max-width: 8rem; }
.ui-ul-tight { margin: 0; padding-left: 1.2em; }
/* Inside panes the binding's chrome row becomes a flush toolbar strip. */
.ui-panes .ui-bind-list-bar { margin: 0; padding: var(--ui-space-1) var(--ui-space-2); border-bottom: var(--ui-border-width) solid var(--ui-border); }
.ui-panes .ui-bind-list-rows > * { padding-left: var(--ui-space-3); padding-right: var(--ui-space-3); }

/* Opt-in list section header (bindSelector groupBy) — Moodle-style topic band. */
.ui-bind-list-group { padding: 6px 10px 4px; font-size: var(--ui-text-xs, 0.72rem); font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.04em; color: var(--ui-text-muted, #6b7280);
    background: var(--ui-bg-muted, #f4f5f7); border-bottom: 1px solid var(--ui-border, #e5e7eb); }

/* Gradebook grader-report cells (Service.Markbook.renderGradebook) — colour by
   attainment band; no inline styles at the call site (design §35). */
.ui-gradecell        { text-align: center; }
.ui-gradecell-hi     { background: var(--ui-success-100); }
.ui-gradecell-mid    { background: var(--ui-warn-100); }
.ui-gradecell-lo     { background: var(--ui-danger-100); }
.ui-gradecell-empty  { background: var(--ui-gray-100); color: var(--ui-text-muted); }
.ui-gradecell-total  { font-weight: 700; border-left: 2px solid var(--ui-gray-200); }
.ui-grade-name       { font-weight: 600; }
.ui-grade-scroll     { overflow-x: auto; }

/* Metadata export preview (Service.Webbook.renderPublishing) — wrapped, scrollable,
   monospace XML; call site stays inline-style-free (design §35). */
.ui-export-pre { max-height: 320px; overflow: auto; background: var(--ui-gray-50, #f8fafc);
  padding: 12px; border-radius: var(--ui-radius-md, 6px); font-size: var(--ui-text-xs, 0.72rem);
  white-space: pre-wrap; word-break: break-word; }

/* Facet fill + dominance (deep-review pass 2) — a growing facet fills its
   column (its body scrolls), killing the white gulf; main/rail column widths
   make one facet the dominant central element with a subordinate side rail. */
.ui-facet-grow { flex: 1 1 auto; min-height: 0; }
.ui-facet-grow > .ui-facet-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
.ui-facet-col-main { flex: 1.7 1 0; }
.ui-facet-col-rail { flex: 1 1 0; }

/* ── Study-activity tracker (shared component: learning.views renderStudyTracker;
 * used by ScholarCloud + NeuroCraft) — all from --ui-* tokens (CLAUDE.md §35b). */
.sc-heat { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; max-width: 320px; }
.sc-heatcell { width: 14px; height: 14px; border-radius: var(--ui-radius-sm, 3px); background: var(--ui-gray-200, #e5e7eb); }
.sc-heat-pad { background: transparent; }
.sc-heat-0 { background: var(--ui-gray-200, #e5e7eb); }
.sc-heat-1 { background: color-mix(in srgb, var(--ui-primary) 30%, var(--ui-gray-100, #f3f4f6)); }
.sc-heat-2 { background: color-mix(in srgb, var(--ui-primary) 60%, white); }
.sc-heat-3 { background: var(--ui-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ui-primary) 35%, white); }
.sc-ring { width: 56px; height: 56px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: conic-gradient(var(--ui-primary) calc(var(--sc-ring-pct, 0) * 1%), var(--ui-gray-200, #e5e7eb) 0); flex: 0 0 auto; }
.sc-ring-label { width: 42px; height: 42px; border-radius: 50%; background: var(--ui-bg, #fff); display: flex; align-items: center; justify-content: center; font-size: var(--ui-text-xs, 0.72rem); font-weight: 600; color: var(--ui-text, #111); }
.sc-ring-met { --sc-ring-pct: 100; }
.sc-bar { height: 8px; background: var(--ui-gray-200, #e5e7eb); border-radius: var(--ui-radius-sm, 3px); overflow: hidden; }
.sc-bar-fill { height: 100%; width: calc(var(--sc-bar-pct, 0) * 1%); background: var(--ui-primary); border-radius: var(--ui-radius-sm, 3px); }

/* ── uiRatingChip — compact per-concept rating control (UI/class.ui.rating.js).
   Hover reveals the tiny drag-slider; a comment icon opens the comment+anonymity
   popover. All colour from tokens; --rc-accent is the (data-driven) tag colour. */
.ui-ratingchip { display: inline-flex; align-items: center; gap: var(--ui-space-1);
    padding: 1px var(--ui-space-2); border: var(--ui-border-width) solid var(--ui-border);
    border-left: 3px solid var(--rc-accent, var(--ui-primary)); border-radius: var(--ui-radius-full, 999px);
    background: var(--ui-bg); font-size: var(--ui-text-xs); line-height: 1.5; max-width: 100%; }
.ui-ratingchip-label { font-weight: var(--ui-font-semibold); color: var(--ui-fg); white-space: nowrap; }
.ui-ratingchip-agg { display: inline-flex; align-items: center; justify-content: center; min-width: 1.6em;
    padding: 0 4px; border-radius: var(--ui-radius-full, 999px); background: var(--ui-primary-100);
    color: var(--ui-primary-700); font-weight: var(--ui-font-semibold); font-size: var(--ui-text-2xs); }
.ui-ratingchip-slider { display: none; align-items: center; gap: 4px; }
.ui-ratingchip:hover .ui-ratingchip-slider,
.ui-ratingchip-rated .ui-ratingchip-slider { display: inline-flex; }
.ui-ratingchip-range { width: 64px; height: 14px; accent-color: var(--rc-accent, var(--ui-primary)); cursor: pointer; }
.ui-ratingchip-val { min-width: 1.9em; text-align: right; color: var(--ui-fg-muted); font-variant-numeric: tabular-nums; }
.ui-ratingchip-comment { border: 0; background: transparent; color: var(--ui-fg-muted); cursor: pointer; padding: 0 2px; font-size: var(--ui-text-xs); }
.ui-ratingchip-comment:hover { color: var(--ui-primary); }
.ui-ratingchip-row { display: flex; flex-wrap: wrap; gap: var(--ui-space-2); align-items: center; }

/* ── Publon bulletin board — comments + reactions (UIBind.bindPublonComments) */
.ui-publon-comments { display: flex; flex-direction: column; gap: var(--ui-space-2); }
.ui-comment { padding: var(--ui-space-2); border: var(--ui-border-width) solid var(--ui-border);
    border-radius: var(--ui-radius-lg, 6px); background: var(--ui-bg); }
.ui-comment-author { font-weight: var(--ui-font-semibold); color: var(--ui-fg); font-size: var(--ui-text-sm); }
.ui-comment-body { color: var(--ui-fg); margin: 2px 0 var(--ui-space-1); white-space: pre-wrap; }
.ui-comment-reactions { margin-top: 2px; }
.ui-reaction { border: var(--ui-border-width) solid var(--ui-border); background: var(--ui-bg);
    border-radius: var(--ui-radius-full, 999px); padding: 0 var(--ui-space-2); font-size: var(--ui-text-xs);
    line-height: 1.6; cursor: pointer; color: var(--ui-fg-muted); }
.ui-reaction:hover { border-color: var(--ui-primary); color: var(--ui-primary); }
.ui-reaction-on { background: var(--ui-primary-100); color: var(--ui-primary-700); border-color: var(--ui-primary); }

/* ── Open tagging surface (UIBind.bindPublonTags) — taxonomy + folksonomy */
.ui-publon-tags { display: flex; flex-direction: column; }
.ui-tagpill { display: inline-flex; align-items: center; gap: 4px; padding: 1px var(--ui-space-2);
    border: var(--ui-border-width) solid var(--ui-border); border-left: 3px solid var(--tp-accent, var(--ui-primary));
    border-radius: var(--ui-radius-full, 999px); background: var(--ui-bg); font-size: var(--ui-text-xs); line-height: 1.6; }
.ui-tagpill-label { color: var(--ui-fg); white-space: nowrap; }
.ui-tagpill-x { border: 0; background: transparent; color: var(--ui-fg-muted); cursor: pointer; padding: 0 0 0 2px; font-size: var(--ui-text-sm); line-height: 1; }
.ui-tagpill-x:hover { color: var(--ui-danger); }
.ui-tagpill-suggest { cursor: pointer; }
.ui-tagpill-suggest:hover { border-color: var(--ui-primary); }
.ui-tagpill-count { color: var(--ui-fg-muted); font-size: var(--ui-text-2xs); font-variant-numeric: tabular-nums; }
.ui-tagpill-new { cursor: pointer; border-style: dashed; border-left-style: dashed; color: var(--ui-primary); background: transparent; }
.ui-tagpill-new:hover { background: var(--ui-primary-100); }
