/* GENERATED — DO NOT EDIT.
 * Theme custom properties (:root, [data-theme]) come from
 *   resources/bao/tokens/tokens.bao
 * Rule bodies come from
 *   resources/bao/styles/rules/*.css.bao
 * Concatenation order, layering, and manifest-derived utilities come from
 *   packages/bao-compiler/src/tokens/*.ts
 * Edit whichever source owns the rule, then run `bun run compile:bao:all`.
 */

@layer base {
  :root {
    --font-sans: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display', system-ui, 'Segoe UI', sans-serif;
    --font-brand: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, 'Segoe UI', sans-serif;
    --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    --font-weight-normal: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;
    --text-2xs: 0.6875rem;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.25rem;
    --text-2xl: 1.5rem;
    --text-3xl: 1.875rem;
    --text-4xl: 2.25rem;
    --leading-tight: 1.25;
    --leading-snug: 1.375;
    --leading-normal: 1.5;
    --leading-relaxed: 1.625;
    --tracking-tighter: -0.05em;
    --tracking-tight: -0.025em;
    --tracking-normal: 0em;
    --tracking-wide: 0.025em;
    --tracking-wider: 0.05em;
    --tracking-otp: 0.35em;
    --tracking-control: -0.01em;
    --tracking-display: -0.02em;
    --tracking-eyebrow: 0.04em;
    --accent-hover: #1564A8;
    --accent-primary: #1E73BE;
    --accent-primary-content: #ffffff;
    --accent-secondary: #F56E28;
    --accent-secondary-content: #18181b;
    --accent-secondary-hover: #E05A16;
    --annotation-accent: #0369a1;
    --annotation-canvas-bg: #f0f1f3;
    --annotation-grid: #71717a;
    --annotation-grid-major: #71717a;
    --annotation-grid-minor: #dfe1e5;
    --annotation-on-surface: #18181b;
    --annotation-pin: #0369a1;
    --annotation-slide-edge: #ffffff;
    --annotation-stroke: #b91c1c;
    --annotation-stroke-ai-pending: #b45309;
    --annotation-stroke-measurement: #0369a1;
    --annotation-stroke-rejected: #7f1d1d;
    --annotation-stroke-selected: #1E73BE;
    --annotation-surface: #ffffff;
    --bg-app: #F7F8F9;
    --bg-panel: #ffffff;
    --bg-surface: #f0f1f3;
    --bg-surface-hover: #e2e4e7;
    --black: #000000;
    --border-base: #dfe1e5;
    --glass-control-border: rgba(15, 23, 42, 0.08);
    --glass-control-fill: rgba(255, 255, 255, 0.10);
    --glass-edge-fade: rgba(15, 23, 42, 0.06);
    --glass-fill-bg: rgba(255, 255, 255, 0.58);
    --glass-fill-border: rgba(24, 24, 27, 0.10);
    --glass-highlight-clear: rgba(255, 255, 255, 0);
    --glass-highlight-soft: rgba(255, 255, 255, 0.18);
    --glass-highlight-strong: rgba(255, 255, 255, 0.45);
    --glass-illumination-soft: rgba(255, 255, 255, 0.05);
    --glass-illumination-strong: rgba(255, 255, 255, 0.15);
    --glass-mask-source: #ffffff;
    --glass-noise-blend: overlay;
    --glass-noise-size: 128px;
    --glass-noise-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
    --glass-scrim: rgba(0, 0, 0, 0.35);
    --glass-shadow-soft: rgba(15, 23, 42, 0.07);
    --glass-shadow-strong: rgba(15, 23, 42, 0.14);
    --lg-pointer-angle: 135deg;
    --liquid-blur-clear: 8px;
    --liquid-blur-mobile: 8px;
    --liquid-blur-nav: 32px;
    --liquid-blur-none: 0px;
    --liquid-blur-regular: 20px;
    --liquid-blur-small: 12px;
    --liquid-blur-tablet: 16px;
    --liquid-border-clear: rgba(255, 255, 255, 0.10);
    --liquid-border-contrast: rgba(24, 24, 27, 0.65);
    --liquid-border-regular: rgba(24, 24, 27, 0.16);
    --liquid-fill-blocking: 0.88;
    --liquid-fill-clear: 0.22;
    --liquid-fill-control: 0.10;
    --liquid-fill-persistent: 0.58;
    --liquid-fill-persistent-coarse: 0.68;
    --liquid-fill-transient: 0.72;
    --liquid-lens-blur: 1;
    --liquid-lens-frequency: 0.012 0.012;
    --liquid-lens-octaves: 2;
    --liquid-lens-scale: -28;
    --liquid-lens-seed: 7;
    --liquid-light-angle: 135deg;
    --liquid-saturate-clear: 1.35;
    --liquid-saturate-mobile: 1.5;
    --liquid-saturate-regular: 1.8;
    --liquid-saturate-strong: 1.8;
    --liquid-saturate-tablet: 1.65;
    --overlay-backdrop: rgba(0, 0, 0, 0.35);
    --overlay-btn-secondary-bg: rgba(255, 255, 255, 0.12);
    --overlay-btn-secondary-border: rgba(255, 255, 255, 0.25);
    --overlay-grid-line: rgba(255, 255, 255, 0.3);
    --overlay-label-bg: rgba(0, 0, 0, 0.75);
    --overlay-shade: rgba(0, 0, 0, 0.5);
    --overlay-text-dim: rgba(255, 255, 255, 0.7);
    --overlay-text-muted: rgba(255, 255, 255, 0.85);
    --overlay-text-primary: #ffffff;
    --overlay-toolbar-bg: rgba(0, 0, 0, 0.82);
    --overlay-toolbar-border: rgba(255, 255, 255, 0.1);
    --status-error: #b91c1c;
    --status-error-content: #ffffff;
    --status-info: #0369a1;
    --status-info-content: #ffffff;
    --status-success: #065f46;
    --status-success-content: #ffffff;
    --status-warning: #b45309;
    --status-warning-content: #ffffff;
    --text-primary: #18181b;
    --text-secondary: #52525b;
    --text-tertiary: #67676f;
    --white: #ffffff;
    --space-2xs: 0.125rem;
    --space-xs: 0.1875rem;
    --space-page: 1rem;
    --space-section: 0.75rem;
    --space-inline: 0.5rem;
    --space-micro: 0.25rem;
    --space-lg: 1.25rem;
    --space-xl: 1.5rem;
    --space-2xl: 2rem;
    --space-3xl: 3rem;
    --space-empty: 3rem;
    --radius-control: 0.75rem;
    --radius-panel: 1rem;
    --radius-xs: 0.25rem;
    --radius-sm: 0.5rem;
    --radius-md: 0.75rem;
    --radius-lg: 1rem;
    --radius-xl: 1rem;
    --radius-2xl: 1.5rem;
    --radius-full: 9999px;
    --radius-none: 0;
    --touch-target-min: 2.75rem;
    --touch-target-compact: 2.25rem;
    --breakpoint-sm: 640px;
    --breakpoint-md: 768px;
    --breakpoint-lg: 1024px;
    --breakpoint-xl: 1280px;
    --breakpoint-2xl: 1536px;
    --duration-instant: 0ms;
    --duration-fast: 120ms;
    --duration-normal: 200ms;
    --duration-slow: 320ms;
    --duration-glass: 280ms;
    --duration-press: 80ms;
    --duration-focus-pulse: 1.5s;
    --duration-skeleton: 1.8s;
    --duration-progress: 2s;
    --duration-reduced-motion-calm: 15s;
    --easing-standard: cubic-bezier(0.2, 0, 0, 1);
    --easing-entrance: cubic-bezier(0.05, 0.7, 0.1, 1);
    --easing-exit: cubic-bezier(0.3, 0, 0.8, 0.15);
    --easing-glass: cubic-bezier(0.32, 0.72, 0, 1);
    --reduced-motion-min-duration: 0.001ms;
    --entrance-offset: 0.375rem;
    --stagger-step: 30ms;
    --view-transition-new-delay: 80ms;
    --duration-pulse: 800ms;
    --duration-pulse-alpha: 1000ms;
    --pulse-alpha-floor: 0.55;
    --pulse-scale-peak: 1.12;
    --opacity-disabled: 0.5;
    --opacity-muted: 0.55;
    --opacity-soft: 0.65;
    --opacity-medium: 0.75;
    --opacity-strong: 0.85;
    --opacity-near: 0.9;
    --opacity-chrome: 0.92;
    --focus-ring-color: #1E73BE;
    --focus-ring-width: 2px;
    --focus-ring-offset: 2px;
    --focus-panel-ring-alpha: 40%;
    --density-compact-row-height: 2rem;
    --density-compact-font-size: 0.8125rem;
    --density-default-row-height: 2.25rem;
    --density-default-font-size: 0.875rem;
    --density-comfortable-row-height: 2.5rem;
    --density-comfortable-font-size: 0.9375rem;
    --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.04);
    --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.08);
    --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.08);
    --shadow-glass-elevated: 0 8px 24px -4px var(--glass-shadow-strong), 0 2px 6px -2px var(--glass-shadow-soft);
    --shadow-glass-floating: 0 12px 32px -8px var(--glass-shadow-strong), 0 4px 12px -4px var(--glass-shadow-soft);
    --shadow-glass-clear: 0 1px 0 0 var(--liquid-border-clear) inset, 0 4px 16px -6px var(--glass-shadow-soft);
    --shadow-glass-inner: 0 1px 0 0 color-mix(in oklab, var(--glass-highlight-strong) 60%, transparent) inset, 0 -1px 0 0 color-mix(in oklab, var(--glass-shadow-soft) 30%, transparent) inset;
    --shadow-overlay-toolbar: 0 -4px 12px -2px rgb(0 0 0 / 0.20);
    --z-base: 0;
    --z-canvas-base: 1;
    --z-canvas-empty: 20;
    --z-canvas-overlay: 30;
    --z-canvas-interaction: 31;
    --z-content-overlay: 10;
    --z-table-header: 10;
    --z-table-action: 20;
    --z-shell-drawer: 1125;
    --z-shell-drawer-overlay: 1300;
    --z-shell-topbar: 1250;
    --z-dropdown: 1250;
    --z-sticky: 1100;
    --z-overlay: 1200;
    --z-modal: 1300;
    --z-toast: 1400;
    --z-tooltip: 1400;
    --page-standard: 80rem;
    --page-wide: 96rem;
    --page-full: 100%;
    --page-conversation: 48rem;
    --description: 65ch;
    --overlay-panel: 20rem;
    --auth: 28rem;
    --form: 44rem;
    --grid-track-min: 18rem;
    --grid-panel-quarter-min: 12rem;
    --grid-panel-third-min: 16rem;
    --grid-panel-half-min: 24rem;
    --grid-panel-span-min: 38rem;
    --grid-form-min: 16rem;
    --grid-choice-min: 14rem;
    --grid-context-min: 10rem;
    --dock-primary-basis: 32rem;
    --dock-side-basis: 15rem;
    --dock-side-wide-basis: 18rem;
    --selection-column-width: 2.5rem;
    --editor-canvas-basis: 32rem;
    --editor-inspector-basis: 22rem;
    --skeleton-panel-min-height: 7rem;
    --skeleton-media-min-height: 12rem;
    --sheet-panel-width: 20rem;
    --sheet-panel-max-width: 85vw;
    --command-palette-max-width: 48rem;
    --keyboard-shortcuts-max-width: 36rem;
    --settings-dialog-max-width: 56rem;
    --palette-max-height: 16rem;
    --chrome-min-height: 2.25rem;
    --modal-max-height: min(92dvh, 92vh);
    --modal-tablet-max-width: 40rem;
    --modal-scroll-max-height: min(70dvh, 70vh);
    --modal-body-max-height: min(58dvh, 58vh);
    --editor-root-min-height: calc(100dvh - 8rem);
    --editor-canvas-height: clamp(26rem, 58vh, 44rem);
    --canvas-region-min-height: clamp(18rem, 42vh, 30rem);
    --panel-scroll-max-height: min(70dvh, 70vh);
    --panel-body-min-height: 15rem;
    --rail-composer-min-height: 15rem;
    --composer-min-height: 5rem;
    --composer-max-height: 20rem;
    --scroll-pt-shell: 3.5rem;
    --scroll-pt-main: 3rem;
    --scroll-mt-section: 6rem;
    --shell-drawer-width: 20rem;
    --shell-drawer-max-viewport: 88vw;
    --brand-mark-size: 2.5rem;
    --icon-gutter: 2rem;
    --page-gutter-inline: clamp(0.5rem, 1.5vw, 1.25rem);
    --page-gutter-block: clamp(0.5rem, 1.25vw, 1rem);
    --page-gutter-inline-compact: clamp(0.375rem, 1vw, 0.75rem);
    --page-section-gap: clamp(0.75rem, 1.5vw, 1.5rem);
    --scroll-edge-width: 2.5rem;
    --scroll-edge-cue-width: 0.875rem;
    --scroll-edge-cue-opacity: 14%;
    --scroll-edge-cue-opacity-contrast: 55%;
    --toolbar-search-max-width: 7rem;
    --toolbar-spacer-min-width: 0.5rem;
    --toolbar-label-max-width: 8rem;
    --toolbar-label-reveal-max-width: 12rem;
    --toolbar-action-pad-inline: 0.625rem;
    --toolbar-action-min-width: 2.5rem;
    --toast-max-inline-size: 20rem;
    --dock-label-max-width: 4.5rem;
    --space-compact: 0.375rem;
    --tracking-label: 0.01em;
    --annotation-stroke-width-base: 2;
    --annotation-stroke-width-hairline: 1;
    --annotation-stroke-width-volume: 6;
    --annotation-sphere-radius-px: 12;
    --annotation-volume-proxy-size-px: 32;
    --annotation-pen-scale-min: 0.55;
    --annotation-pen-scale-range: 1.45;
    --annotation-grid-spacing-px: 50;
    --annotation-point-radius-px: 4;
    --annotation-label-font-size-px: 18;
    --tooltip-max-width: 20rem;
    --tooltip-viewport-inset: 0.75rem;
    --sidebar-expanded: 16rem;
    --sidebar-collapsed: 3.5rem;
    --session-rail-expanded: 18rem;
    --session-rail-collapsed: 3rem;
    --session-rail-min: 12rem;
    --session-rail-max: 32rem;
    --session-rail-main-min: 36rem;
    --session-rail-resize-step: 1rem;
    --session-rail-resize-step-large: 4rem;
  }

  [data-theme="capture-dx-dark"] {
    --accent-hover: #1E73BE;
    --accent-primary: #41B6E6;
    --accent-primary-content: #0c1017;
    --accent-secondary: #F56E28;
    --accent-secondary-content: #18181b;
    --accent-secondary-hover: #FF8040;
    --annotation-accent: #41B6E6;
    --annotation-canvas-bg: #1e2633;
    --annotation-grid: #71717a;
    --annotation-grid-major: #71717a;
    --annotation-grid-minor: #1e2633;
    --annotation-on-surface: #f4f4f5;
    --annotation-pin: #41B6E6;
    --annotation-slide-edge: #151b24;
    --annotation-stroke: #dc2626;
    --annotation-stroke-ai-pending: #d97706;
    --annotation-stroke-measurement: #41B6E6;
    --annotation-stroke-rejected: #f87171;
    --annotation-stroke-selected: #41B6E6;
    --annotation-surface: #151b24;
    --bg-app: #0c1017;
    --bg-panel: #151b24;
    --bg-surface: #1e2633;
    --bg-surface-hover: #2d3847;
    --black: #000000;
    --border-base: #2d3847;
    --glass-control-border: rgba(255, 255, 255, 0.10);
    --glass-control-fill: rgba(255, 255, 255, 0.14);
    --glass-edge-fade: rgba(0, 0, 0, 0.22);
    --glass-fill-bg: rgba(21, 27, 36, 0.68);
    --glass-fill-border: rgba(255, 255, 255, 0.12);
    --glass-highlight-clear: rgba(255, 255, 255, 0);
    --glass-highlight-soft: rgba(255, 255, 255, 0.09);
    --glass-highlight-strong: rgba(255, 255, 255, 0.22);
    --glass-illumination-soft: rgba(255, 255, 255, 0.03);
    --glass-illumination-strong: rgba(255, 255, 255, 0.08);
    --glass-mask-source: #ffffff;
    --glass-noise-blend: overlay;
    --glass-noise-size: 128px;
    --glass-noise-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.02'/%3E%3C/svg%3E");
    --glass-scrim: rgba(0, 0, 0, 0.35);
    --glass-shadow-soft: rgba(0, 0, 0, 0.20);
    --glass-shadow-strong: rgba(0, 0, 0, 0.34);
    --lg-pointer-angle: 135deg;
    --liquid-blur-clear: 8px;
    --liquid-blur-mobile: 10px;
    --liquid-blur-nav: 36px;
    --liquid-blur-none: 0px;
    --liquid-blur-regular: 24px;
    --liquid-blur-small: 14px;
    --liquid-blur-tablet: 20px;
    --liquid-border-clear: rgba(255, 255, 255, 0.08);
    --liquid-border-contrast: rgba(244, 244, 245, 0.30);
    --liquid-border-regular: rgba(255, 255, 255, 0.18);
    --liquid-fill-blocking: 0.92;
    --liquid-fill-clear: 0.30;
    --liquid-fill-control: 0.14;
    --liquid-fill-persistent: 0.68;
    --liquid-fill-persistent-coarse: 0.76;
    --liquid-fill-transient: 0.80;
    --liquid-lens-blur: 1;
    --liquid-lens-frequency: 0.012 0.012;
    --liquid-lens-octaves: 2;
    --liquid-lens-scale: -28;
    --liquid-lens-seed: 7;
    --liquid-light-angle: 135deg;
    --liquid-saturate-clear: 1.35;
    --liquid-saturate-mobile: 1.5;
    --liquid-saturate-regular: 1.8;
    --liquid-saturate-strong: 1.8;
    --liquid-saturate-tablet: 1.65;
    --overlay-backdrop: rgba(0, 0, 0, 0.35);
    --overlay-btn-secondary-bg: rgba(255, 255, 255, 0.12);
    --overlay-btn-secondary-border: rgba(255, 255, 255, 0.25);
    --overlay-grid-line: rgba(255, 255, 255, 0.3);
    --overlay-label-bg: rgba(0, 0, 0, 0.75);
    --overlay-shade: rgba(0, 0, 0, 0.5);
    --overlay-text-dim: rgba(255, 255, 255, 0.7);
    --overlay-text-muted: rgba(255, 255, 255, 0.85);
    --overlay-text-primary: #ffffff;
    --overlay-toolbar-bg: rgba(0, 0, 0, 0.82);
    --overlay-toolbar-border: rgba(255, 255, 255, 0.1);
    --status-error: #f87171;
    --status-error-content: #0c1017;
    --status-info: #41B6E6;
    --status-info-content: #0c1017;
    --status-success: #059669;
    --status-success-content: #0c1017;
    --status-warning: #d97706;
    --status-warning-content: #18181b;
    --text-primary: #f4f4f5;
    --text-secondary: #a1a1aa;
    --text-tertiary: #8b8b96;
    --white: #ffffff;
  }
}

/* Reduced-motion token clamp: class-level `transition: … var(--duration-*)`
   rules out-specificity the universal `*` clamp, so the durations themselves
   collapse at the variable level. Unlayered beats @layer base :root. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: var(--reduced-motion-min-duration);
    --duration-normal: var(--reduced-motion-min-duration);
    --duration-slow: var(--reduced-motion-min-duration);
    --duration-glass: var(--reduced-motion-min-duration);
    --duration-press: var(--reduced-motion-min-duration);
    --duration-focus-pulse: var(--reduced-motion-min-duration);
    --duration-skeleton: var(--reduced-motion-min-duration);
    --duration-progress: var(--reduced-motion-min-duration);
    --duration-reduced-motion-calm: var(--reduced-motion-min-duration);
    --duration-pulse: var(--reduced-motion-min-duration);
    --duration-pulse-alpha: var(--reduced-motion-min-duration);
    --stagger-step: 0ms;
    --view-transition-new-delay: 0ms;
  }
}

@layer utilities {
  /*
   * Horizontal scroll-edge affordance — ONE owner (design.md: clipped content
   * must be discoverable). background-attachment shadows mark clipped content;
   * local-attached covers ride the scroll position and hide each shadow once
   * its edge is fully revealed. Host sets --scroll-edge-bg to its own surface
   * color; symmetric left/right so RTL needs no mirroring.
   */
  .bao-scroll-edge-x {
    background-image:
      linear-gradient(to right, var(--scroll-edge-bg, var(--color-base-100, var(--bg-panel))) 30%, transparent),
      linear-gradient(to left, var(--scroll-edge-bg, var(--color-base-100, var(--bg-panel))) 30%, transparent),
      linear-gradient(to right, color-mix(in oklab, var(--color-base-content) var(--scroll-edge-cue-opacity, 14%), transparent), transparent),
      linear-gradient(to left, color-mix(in oklab, var(--color-base-content) var(--scroll-edge-cue-opacity, 14%), transparent), transparent);
    background-position: left center, right center, left center, right center;
    background-size: var(--scroll-edge-width, 2.5rem) 100%, var(--scroll-edge-width, 2.5rem) 100%, var(--scroll-edge-cue-width, 0.875rem) 100%, var(--scroll-edge-cue-width, 0.875rem) 100%;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
  }
  /* Contrast modes replace the soft cue — never delete it (design.md §9.3:
     an affordance the user cannot see is a missing affordance). Forced-colors
     drops the gradients (UA palette) and relies on the native scrollbar. */
  @media (prefers-contrast: more) {
    .bao-scroll-edge-x {
      background-image:
        linear-gradient(to right, var(--scroll-edge-bg, var(--color-base-100, var(--bg-panel))) 30%, transparent),
        linear-gradient(to left, var(--scroll-edge-bg, var(--color-base-100, var(--bg-panel))) 30%, transparent),
        linear-gradient(to right, color-mix(in oklab, var(--color-base-content) var(--scroll-edge-cue-opacity-contrast, 55%), transparent), transparent),
        linear-gradient(to left, color-mix(in oklab, var(--color-base-content) var(--scroll-edge-cue-opacity-contrast, 55%), transparent), transparent);
    }
  }
  @media (forced-colors: active) {
    .bao-scroll-edge-x {
      background-image: none;
    }
  }
}


@layer utilities {
  /* Stacking context for sticky filter chrome — named SSOT, not raw Tailwind-only. */
  .isolation-isolate { isolation: isolate; }

  /*
   * Sticky filter host: creates a stacking context so dropdowns/menus from the
   * bar paint above scrolling table bodies without z-index wars.
   */
  .bao-filter-bar-sticky {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky, 10);
    isolation: isolate;
  }

  /* Dense queue row action cluster (annotation-review / clinical queues). */
  .bao-queue-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-micro);
    justify-content: flex-end;
    min-width: 0;
  }

  /* Typography / text colors */
  .text-base-content { color: var(--text-primary); }
  .text-base-content\/70 { color: color-mix(in oklab, var(--text-primary) 70%, var(--text-secondary)); }
  .text-base-content\/60 { color: var(--text-secondary); }
  .text-base-content\/40 { color: var(--text-tertiary); }
  .text-accent-primary { color: var(--accent-primary); }
  .text-accent-hover { color: var(--accent-hover); }
  .text-accent-secondary { color: var(--accent-secondary); }
  .text-accent-secondary-hover { color: var(--accent-secondary-hover); }
  .text-status-success { color: var(--status-success); }
  .text-status-success-content { color: var(--status-success-content); }
  .text-status-error { color: var(--status-error); }
  .text-status-error-content { color: var(--status-error-content); }
  .text-status-warning { color: var(--status-warning); }
  .text-status-warning-content { color: var(--status-warning-content); }
  .text-status-info { color: var(--status-info); }
  .text-status-info-content { color: var(--status-info-content); }
  .text-accent-primary-content { color: var(--accent-primary-content); }
  .text-accent-secondary-content { color: var(--accent-secondary-content); }

  /* Background colors */
  .bg-base-100 { background-color: var(--bg-panel); }
  .bg-base-200 { background-color: var(--bg-surface); }
  .bg-base-300 { background-color: var(--bg-surface-hover); }
  .bg-accent-primary { background-color: var(--accent-primary); }
  .bg-accent-hover { background-color: var(--accent-hover); }
  .bg-accent-secondary { background-color: var(--accent-secondary); }
  .bg-accent-secondary-hover { background-color: var(--accent-secondary-hover); }
  .bg-status-success { background-color: var(--status-success); }
  .bg-status-error { background-color: var(--status-error); }
  .bg-status-warning { background-color: var(--status-warning); }
  .bg-status-info { background-color: var(--status-info); }
  .bg-border-base { background-color: var(--border-base); }
  .bg-accent-primary\/5 { background-color: color-mix(in oklab, var(--accent-primary) 5%, transparent); }
  .bg-status-error\/10 { background-color: color-mix(in oklab, var(--status-error) 10%, transparent); }
  .bg-base-100\/95 { background-color: color-mix(in oklab, var(--bg-panel) 95%, transparent); }
  .bg-base-100\/90 { background-color: color-mix(in oklab, var(--bg-panel) 90%, transparent); }
  .bg-base-100\/80 { background-color: color-mix(in oklab, var(--bg-panel) 80%, transparent); }
  .border-status-error { border-color: var(--status-error); }
  .border-status-success { border-color: var(--status-success); }
  .border-status-warning { border-color: var(--status-warning); }
  .border-status-info { border-color: var(--status-info); }
  .border-status-error\/30 { border-color: color-mix(in oklab, var(--status-error) 30%, transparent); }

  /* Overlay surfaces (canvas chrome, sheets, dialogs) */
  .bg-overlay-backdrop { background-color: var(--overlay-backdrop); }
  .bg-overlay-shade { background-color: var(--overlay-shade); }
  .bg-overlay-toolbar-bg { background-color: var(--overlay-toolbar-bg); }
  .border-overlay-toolbar-border { border-color: var(--overlay-toolbar-border); }
  .bg-overlay-toolbar-border { background-color: var(--overlay-toolbar-border); }
  .text-overlay-text-primary { color: var(--overlay-text-primary); }
  .text-overlay-text-muted { color: var(--overlay-text-muted); }
  .text-overlay-text-dim { color: var(--overlay-text-dim); }
  .bg-overlay-label-bg { background-color: var(--overlay-label-bg); }

  /* Border colors */
  .border-base-300 { border-color: var(--border-base); }
  .border-accent-primary { border-color: var(--accent-primary); }

  /* Radius */
  .rounded-radius-control { border-radius: var(--radius-control); }
  .rounded-radius-panel { border-radius: var(--radius-panel); }
  .rounded-md { border-radius: var(--radius-control); }
  .rounded-xl { border-radius: var(--radius-panel); }
  .rounded-full { border-radius: var(--radius-full); }

  /* Shadows */
  .shadow-sm { box-shadow: var(--shadow-sm); }
  .shadow-md { box-shadow: var(--shadow-md); }
  .shadow-lg { box-shadow: var(--shadow-lg); }
  .shadow-shadow-overlay-toolbar { box-shadow: var(--shadow-overlay-toolbar); }


/* Spacing */
  .p-space-page { padding: var(--space-page); }
  .p-space-section { padding: var(--space-section); }
  .p-space-inline { padding: var(--space-inline); }
  .p-space-micro { padding: var(--space-micro); }
  .p-space-empty { padding: var(--space-empty); }
  /* Skip-link focus chrome — tokenized padding, not raw focus:p-3. */
  .focus\:p-space-section:focus { padding: var(--space-section); }
  .pb-space-section { padding-bottom: var(--space-section); }
  .pt-space-section { padding-top: var(--space-section); }
  .mb-space-section { margin-bottom: var(--space-section); }
  .mt-space-section { margin-top: var(--space-section); }
  .pt-0 { padding-top: 0; }
  .gap-space-page { gap: var(--space-page); }
  .gap-space-section { gap: var(--space-section); }
  .gap-space-inline { gap: var(--space-inline); }
  .gap-space-micro { gap: var(--space-micro); }

  /* 2xs hairline scale — mirrors the micro set so *-space-2xs utilities
     always resolve (class-resolution gate; tokens.bao space-2xs). */
  .gap-space-2xs { gap: var(--space-2xs); }
  .gap-x-space-2xs { column-gap: var(--space-2xs); }
  .gap-y-space-2xs { row-gap: var(--space-2xs); }
  .p-space-2xs { padding: var(--space-2xs); }
  .px-space-2xs { padding-inline: var(--space-2xs); }
  .py-space-2xs { padding-block: var(--space-2xs); }
  .pt-space-2xs { padding-top: var(--space-2xs); }
  .pb-space-2xs { padding-bottom: var(--space-2xs); }
  .ps-space-2xs { padding-inline-start: var(--space-2xs); }
  .pe-space-2xs { padding-inline-end: var(--space-2xs); }
  .mt-space-2xs { margin-top: var(--space-2xs); }
  .mb-space-2xs { margin-bottom: var(--space-2xs); }
  .space-y-space-2xs > * + * { margin-top: var(--space-2xs); }
  .gap-x-space-micro { column-gap: var(--space-micro); }
  .gap-x-space-inline { column-gap: var(--space-inline); }
  .gap-x-space-section { column-gap: var(--space-section); }
  .gap-y-space-micro { row-gap: var(--space-micro); }
  .gap-y-space-inline { row-gap: var(--space-inline); }
  .gap-y-space-section { row-gap: var(--space-section); }
  .px-space-page { padding-inline: var(--space-page); }
  .px-space-section { padding-inline: var(--space-section); }
  .px-space-inline { padding-inline: var(--space-inline); }
  .px-space-micro { padding-inline: var(--space-micro); }
  .px-page-gutter-inline { padding-inline: var(--page-gutter-inline); }
  .px-page-gutter-inline-compact { padding-inline: var(--page-gutter-inline-compact); }
  .py-space-page { padding-block: var(--space-page); }
  .py-space-section { padding-block: var(--space-section); }
  .py-space-inline { padding-block: var(--space-inline); }
  .py-space-micro { padding-block: var(--space-micro); }
  .py-space-empty { padding-block: var(--space-empty); }
  .pe-space-inline { padding-inline-end: var(--space-inline); }
  .pe-space-section { padding-inline-end: var(--space-section); }
  .pe-space-micro { padding-inline-end: var(--space-micro); }
  .me-space-inline { margin-inline-end: var(--space-inline); }
  .me-space-micro { margin-inline-end: var(--space-micro); }
  .ms-space-page { margin-inline-start: var(--space-page); }
  .ms-space-section { margin-inline-start: var(--space-section); }
  .ms-space-inline { margin-inline-start: var(--space-inline); }
  .start-space-inline { inset-inline-start: var(--space-inline); }
  .start-space-section { inset-inline-start: var(--space-section); }
  .start-space-micro { inset-inline-start: var(--space-micro); }
  .end-space-inline { inset-inline-end: var(--space-inline); }
  .end-space-section { inset-inline-end: var(--space-section); }
  .end-space-micro { inset-inline-end: var(--space-micro); }
  .ps-icon-gutter { padding-inline-start: var(--icon-gutter); }
  .pe-icon-gutter { padding-inline-end: var(--icon-gutter); }
  .scroll-pt-shell { scroll-padding-top: var(--scroll-pt-shell); }
  .scroll-pt-main { scroll-padding-top: var(--scroll-pt-main); }
  .scroll-mt-section { scroll-margin-top: var(--scroll-mt-section); }
  .m-space-inline { margin: var(--space-inline); }
  .mx-space-micro { margin-inline: var(--space-micro); }
  .mx-space-inline { margin-inline: var(--space-inline); }
  .mx-space-section { margin-inline: var(--space-section); }
  .my-space-micro { margin-block: var(--space-micro); }
  .my-space-inline { margin-block: var(--space-inline); }
  .mt-space-page { margin-top: var(--space-page); }
  .mt-space-section { margin-top: var(--space-section); }
  .mt-space-inline { margin-top: var(--space-inline); }
  .mt-space-micro { margin-top: var(--space-micro); }
  .mb-space-page { margin-bottom: var(--space-page); }
  .mb-space-section { margin-bottom: var(--space-section); }
  .mb-space-inline { margin-bottom: var(--space-inline); }
  .mb-space-micro { margin-bottom: var(--space-micro); }
  .pt-space-page { padding-top: var(--space-page); }
  .pt-space-section { padding-top: var(--space-section); }
  .pt-space-inline { padding-top: var(--space-inline); }
  .pt-space-micro { padding-top: var(--space-micro); }
  .pb-space-page { padding-bottom: var(--space-page); }
  .pb-space-section { padding-bottom: var(--space-section); }
  .pb-space-inline { padding-bottom: var(--space-inline); }
  .pb-space-micro { padding-bottom: var(--space-micro); }
  .inset-x-space-section { inset-inline: var(--space-section); }
  .inset-x-space-inline { inset-inline: var(--space-inline); }
  .top-space-section { top: var(--space-section); }
  .top-space-inline { top: var(--space-inline); }
  .top-space-micro { top: var(--space-micro); }
  .bottom-space-section { bottom: var(--space-section); }
  .bottom-space-inline { bottom: var(--space-inline); }
  .bottom-space-micro { bottom: var(--space-micro); }
  .inset-x-space-micro { inset-inline: var(--space-micro); }
  .max-h-panel-body { max-height: var(--panel-body-min-height); }
  .space-y-space-section > * + * { margin-top: var(--space-section); }
  .space-y-space-page > * + * { margin-top: var(--space-page); }
  .space-y-space-inline > * + * { margin-top: var(--space-inline); }
  .space-y-space-micro > * + * { margin-top: var(--space-micro); }
  /*
   * Negative inline margin — lets a sticky dock bleed to the edges of a
   * gutter-padded parent (login submit dock) so its border and background span
   * the full surface instead of floating inset. Tailwind cannot generate this
   * from an arbitrary custom property, so the token owns it.
   */
  .-mx-space-inline { margin-inline: calc(var(--space-inline) * -1); }


  /* Layout */
  /* Parent marker for descendant state variants (toolbar/tooltips). */

  /* Structural utilities (.flex, .hidden, .grid, .items-…, .justify-…, .w-full)
     are intentionally NOT re-declared here. Tailwind is their single owner:
     a flat copy in this later-loaded sheet lands after Tailwind's
     breakpoint-gated variants in the same utilities layer and silently beats
     them (e.g. a re-declared .flex-wrap killed max-sm:flex-nowrap; .hidden
     killed hidden sm:flex). Only token-valued classes belong in this file. */
  .w-12 { width: 3rem; }
  .w-sidebar-expanded { width: var(--sidebar-expanded); }
  .size-brand-mark {
    width: var(--brand-mark-size);
    height: var(--brand-mark-size);
  }
  .min-w-control { min-width: var(--density-compact-row-height); }
  /* Fluid shell tracks — CSS vars from tokens.bao (no invented rem).
     max-width clamps to the viewport so end-anchored dropdowns never
     paint past the right edge on phone-width shells. */
  .bao-width-menu-lg {
    /* Prefer % over 100vw so padded #app-main does not grow scrollWidth on mobile. */
    width: min(100%, var(--dock-side-wide-basis));
    max-width: min(100%, var(--dock-side-wide-basis));
  }
  .bao-width-menu-md {
    width: min(100%, var(--grid-choice-min));
    max-width: min(100%, var(--grid-choice-min));
  }
   .bao-width-avatar { width: var(--icon-gutter); }
   .bao-width-sheet {
     width: min(100%, var(--sheet-panel-width));
     max-width: min(100%, var(--sheet-panel-max-width));
   }
   /* Phone bottom sheet panel — pathologist inspector / filter overflow. */
   .bao-sheet-panel-bottom {
     max-height: min(72dvh, 32rem);
     padding-block-end: env(safe-area-inset-bottom, 0px);
   }
   .bao-width-rail-collapsed { width: var(--session-rail-collapsed); }
   .bao-width-selection-col { width: var(--selection-column-width); }
   .bao-max-w-search { max-width: min(100%, var(--dock-primary-basis)); }
   .bao-max-w-command { max-width: min(100%, var(--command-palette-max-width)); }
   .bao-max-w-shortcuts { max-width: min(100%, var(--keyboard-shortcuts-max-width)); }
   .bao-max-w-settings { max-width: min(100%, var(--settings-dialog-max-width)); }
   /* Topbar chips size via flex tracks in shell responsive CSS — no hard 10rem cap. */
   .bao-max-w-context-chip { max-width: 100%; min-width: 0; }
   /* Topbar actions: cap width on narrow so brand/title keep room; scroll overflow. */
   .bao-max-w-topbar-actions { max-width: 50%; }
   .bao-max-w-voice-status { max-width: 10rem; }
   /* Toolbar search + flex spacer — tokens.bao layoutSize (no max-w-28 / min-w-2). */
   .bao-max-w-toolbar-search { max-width: min(100%, var(--toolbar-search-max-width)); }
   .bao-min-w-toolbar-spacer { min-width: var(--toolbar-spacer-min-width); }
   @media (min-width: 40rem) {
     .bao-max-w-topbar-actions { max-width: none; }
   }
   /* Sticky topbar clears notch / status bar on phones and tablets.
      Keep padding-top explicit (never padding-block) so safe-area wins. */
   .navbar.z-shell-topbar {
      padding-top: max(var(--safe-area-inset-top, var(--space-micro, 0.25rem)), env(safe-area-inset-top, 0px));
      padding-bottom: var(--space-micro, 0.25rem);
      padding-inline-start: max(var(--safe-area-inset-left, var(--space-inline)), env(safe-area-inset-left, 0px));
      padding-inline-end: max(var(--safe-area-inset-right, var(--space-inline)), env(safe-area-inset-right, 0px));
   }
   @media (min-width: 40rem) {
     .navbar.z-shell-topbar {
       padding-inline-start: max(var(--space-section), env(safe-area-inset-left, 0px));
       padding-inline-end: max(var(--space-section), env(safe-area-inset-right, 0px));
     }
   }
   .bao-max-h-palette { max-height: var(--palette-max-height); }
   .bao-field-basis { flex-basis: min(100%, var(--grid-context-min)); }
   .bao-control-h { height: var(--density-compact-row-height); }
   .bao-min-h-chrome { min-height: var(--chrome-min-height); }
   .min-w-56 { min-width: min(100%, var(--grid-choice-min)); }
   .h-space-page { height: var(--space-page); }
   .h-space-section { height: var(--space-section); }
   .h-space-inline { height: var(--space-inline); }
   .h-space-micro { height: var(--space-micro); }
    .min-h-12 { min-height: var(--chrome-min-height); }
   .min-h-60 { min-height: var(--panel-body-min-height); }
   .max-h-64 { max-height: var(--palette-max-height); }
   .bao-min-h-panel-body { min-height: var(--panel-body-min-height); }
   .bao-min-h-rail-composer { min-height: var(--rail-composer-min-height); }
   .bao-min-h-composer { min-height: var(--composer-min-height); }
   .bao-max-h-modal { max-height: var(--modal-max-height); }
   .bao-max-h-modal-scroll { max-height: var(--modal-scroll-max-height); }
   .bao-max-h-modal-body { max-height: var(--modal-body-max-height); }
   .bao-max-h-composer { max-height: var(--composer-max-height); }
   .bao-min-h-panel-scroll { min-height: var(--panel-scroll-max-height); }
   .max-w-3xl { max-width: var(--command-palette-max-width); }
   .max-w-4xl { max-width: var(--settings-dialog-max-width); }
   .max-w-6xl { max-width: var(--page-wide); }
   .max-w-56 { max-width: var(--grid-choice-min); }
   .max-w-chip { max-width: var(--max-w-chip, 14rem); }
   .max-w-dropdown { max-width: min(100vw - 2rem, var(--max-w-dropdown, 20rem)); }
   .max-w-[var(--page-standard)] { max-width: var(--page-standard); }
  .z-canvas-base { z-index: var(--z-canvas-base); }
  .z-canvas-empty { z-index: var(--z-canvas-empty, 20); }
  .z-canvas-overlay { z-index: var(--z-canvas-overlay); }
  .z-content-overlay { z-index: var(--z-content-overlay); }
  .z-table-header { z-index: var(--z-table-header); }
  .z-table-action { z-index: var(--z-table-action); }
  .z-shell-drawer { z-index: var(--z-shell-drawer); }
  .z-shell-topbar { z-index: var(--z-shell-topbar); }
  .z-dropdown { z-index: var(--z-dropdown); }
  .z-sticky { z-index: var(--z-sticky); }
  .z-overlay { z-index: var(--z-overlay); }
  .z-modal { z-index: var(--z-modal); }
  .z-toast { z-index: var(--z-toast); }

  /* Skip link: above every chrome tier when revealed (focus-variant utilities
     cannot reach token classes, so the focus state lives here). */
  .bao-skip-link:focus { z-index: var(--z-tooltip); }

  /* Grid base utilities (.grid/.grid-cols-*) live in Tailwind only — see the
     structural-utilities note above; re-declaring them here beats Tailwind's
     responsive grid-cols variants. */
  /* Responsive variants (sm:/md:/lg:/xl:) are intentionally NOT defined here —
     hand-written flat copies have no @media gate and apply at every width,
     overriding Tailwind's correctly breakpoint-gated utilities. Tailwind
     generates these from the scanned templates; leave them to it. */


.bao-capture-device-grid {
  display: grid;
  width: 100%;
  min-width: 0;
  align-items: stretch;
  gap: var(--space-inline);
  /* Phone: single column so empty-hint fieldsets never sit beside short peers. */
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 40rem) {
  .bao-capture-device-grid {
    /* auto-fill (not fit) avoids 0px phantom tracks beside 3 short fieldsets. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
  }
}
  /*
   * Reactive panel grid shell (display/gap/container). Track template +
   * span sizes live in tokens-layout-panel-grid.ts (12-track SSOT) — never
   * redeclare grid-template-columns here or spans fight auto-fit cells.
   */
  .bao-grid-reactive-panels {
    display: grid;
    min-width: 0;
    width: 100%;
    align-items: stretch;
    gap: var(--space-section, 0.75rem);
    grid-auto-rows: auto;
    container-type: inline-size;
  }
  /*
   * Fluid KPI stat strip — auto-fit tracks, never hardcoded counts: a phone
   * width yields 2-up, wide hosts 4-up, purely from available inline size
   * (§8.6 canonical transformation; replaces stats-vertical stacking that
   * spent ~140px of viewport per number on compact shells).
   */
  .bao-grid-stats {
    display: grid;
    min-width: 0;
    width: 100%;
    align-items: stretch;
    gap: var(--space-inline, 0.5rem);
    grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  }
  /* Solid metric host — content plane; never glass (pairs with .bao-stat-card). */
  .bao-stat-card {
    background-color: var(--color-base-100, var(--bg-panel));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Page content track — never let max-w-full win over enterprise max-w-page-*. */
  .bao-page-track {
    width: 100%;
    min-width: 0;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--page-section-gap, var(--space-section));
    /*
     * §8.6 canonical transformation — page track is the container query host
     * (enterprise-page @container class). Nested grids reflow off rendered
     * track width, not viewport, so split panels and embedded widgets collapse
     * correctly without per-page media forks.
     */
    container-type: inline-size;
  }
  /*
   * Page-track container column transforms — one owner for collection/form/
   * detail grids inside the content column (never duplicate in page partials).
   */
  @container (max-width: 36rem) {.bao-page-track .bao-form-grid-two,
.bao-page-track .bao-choice-grid,
.bao-page-track .bao-context-grid-reactive,
.bao-page-track .bao-grid-reactive-collection {
      grid-template-columns: minmax(0, 1fr);
    }
    /*
     * KPI stat strips keep auto-fit (Cached|Loaded 2-up on phone). Forcing
     * single-column here burned a full viewport row per number (models pipeline).
     * .bao-grid-stats base owns minmax(min(9rem, 100%), 1fr) fluid tracks.
     */
  }
  @container (min-width: 36rem) and (max-width: 64rem) {.bao-page-track .bao-form-grid-two,
.bao-page-track .bao-choice-grid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }
  /* DaisyUI btn can set display:flex;hidden/stepper must still win. */
  .btn.hidden,
button.hidden,
[hidden].btn,
button[hidden] {
    display: none;
  }
  /*
   * Staggered panel entrance — transform only (no opacity:0). Disabled for
   * HTMX main swaps: desktop uses View Transitions; every settle re-fired
   * stagger and looked like a full refresh. Tokens remain for future use.
   */
  .bao-stagger-children > * {
    animation: none;
  }
  @keyframes bao-stagger-in {
    from { transform: translateY(var(--entrance-offset)); }
    to { transform: none; }
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-stagger-children > * {
    animation: none;
  }
  }
  .bao-page-track.max-w-page-standard {
    max-width: var(--page-standard, 80rem);
  }
  .bao-page-track.max-w-page-wide {
    max-width: var(--page-wide, 96rem);
  }
  .bao-page-track.max-w-page-full {
    max-width: none;
  }

  .bao-state-glyph-sm { width: 1.5rem; height: 1.5rem; }
  .bao-state-glyph-md { width: 3rem; height: 3rem; }
  .bao-state-glyph-lg { width: 4rem; height: 4rem; }


  /*
   * Page-header primary CTAs (New Case, etc.): HIG 44px min height.
   * daisyUI .btn-sm sets a fixed height (~32px); override height+max-height
   * so min-height actually expands the control for touch.
   */
  [data-page-header-actions] > .btn.btn-primary,
  [data-page-header-actions] > a.btn.btn-primary,
  [data-page-header-actions] > .btn.btn-primary.btn-sm,
  [data-page-header-actions] > a.btn.btn-primary.btn-sm,
  [data-primitive="PageHeader"] [data-page-header-actions] > .btn.btn-primary,
  [data-primitive="PageHeader"] [data-page-header-actions] > a.btn.btn-primary {
    /* daisyUI 5 sizes buttons via --size; raise it so btn-sm is still 44px. */
    --size: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    height: var(--touch-target-min, 2.75rem);
    max-height: none;
    min-width: 0;
  }

  /*
   * Dual-title ban — SOLE owner of shell-title visibility.
   * Demote the shell title only when a content H1 is the visible name. The
   * verdict is NOT re-derived here: pages-renderers-header.ts stamps
   * data-page-header-mode on the PageHeader, app-shell JS mirrors it onto
   * <body> for WebView 101 (no :has()), and the :has() branch reads the same
   * stamp so first paint matches post-hydration. The old structural probe
   * (PageHeader > div:not(.sr-only) h1) matched the crumbs wrapper that the
   * demoted layout emits ahead of the sr-only title block, so every page
   * resolved to content mode and BOTH titles ended up clipped.
   *
   * display:none, not the sr-only clip: a demoted shell title is a duplicate
   * of the visible H1, so it must leave the accessibility tree with the
   * pixels. #shell-page-title carries no aria-hidden, so clipping it here
   * would double-announce the page name on content-mode surfaces.
   */
  body[data-page-header-mode="content"] [data-shell-page-title],
  body:has([data-primitive="PageHeader"][data-page-header-mode="content"]) [data-shell-page-title] {
    display: none;
  }


  /*
   * Happydumpling shell law: #app-main is a flex host. Standard pages scroll
   * on main; immersive pages (media/conversation) do not — the thread/canvas does.
   * Content column uses max-width + centered track for non-immersive pages.
   */
  #app-main {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    flex: 1 1 0%;
    width: 100%;
    max-width: 100%;
    /* Inline-size containment keeps nested min-content tables from inflating scrollWidth. */
    contain: inline-size;
    /* Clip horizontal overflow so smoke scrollWidth stays within clientWidth. */
    overflow-x: clip;
    overflow-y: auto;
    overscroll-behavior-x: none;
    /* Sticky topbar must not obscure focused controls (WCAG 2.4.11). */
    scroll-padding-top: var(--scroll-pt-main, var(--chrome-min-height, 3.5rem));
    /* Dock + one HIG row + section rhythm so scrollIntoView(center) clears fixed dock. */
    scroll-padding-bottom: calc(var(--dock-height, 4rem) + env(safe-area-inset-bottom, 0px) + var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
  }
  /* Direct children only — a universal descendant min-width reset lets any
     flex child collapse and paint over siblings (phone toolbar pile-ups). */
  #app-main > * {
    box-sizing: border-box;
    min-width: 0;
  }
  #app-main table {
    max-width: 100%;
  }
  /* Scroll hosts own wide content; do not inflate #app-main.scrollWidth. */
  #app-main .bao-data-panel,
  #app-main .overflow-x-auto,
  #app-main .bao-panel-scroll,
  #app-main .bao-registry-table {
    max-width: 100%;
  }
  #app-main pre,
  #app-main code {
    max-width: 100%;
    overflow-x: auto;
  }
  #app-main > [data-page]:not([data-page-type="media"]):not([data-page-type="conversation"]) {
    width: 100%;
    min-width: 0;
    max-width: 100%;
  }
  #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
    overflow-x: clip;
    overflow-y: auto;
    scroll-behavior: smooth;
    /* Happydumpling fluid gutters: clamp tracks, not flat 12px.
     * padding-block-end is owned by dock clearance SSOT (tokens-layout-dock)
     * so this higher-specificity rule cannot wipe mobile dock room. */
    padding-block-start: var(--page-gutter-block);
    padding-inline: var(--page-gutter-inline);
    /* Notched phones / home indicator — keep content out of unsafe insets. */
    padding-inline-start: max(var(--page-gutter-inline), env(safe-area-inset-left, 0px));
    padding-inline-end: max(var(--page-gutter-inline), env(safe-area-inset-right, 0px));
  }
  /* Mobile: compact inline gutter under tight viewports. */
  @media (max-width: 48rem) {
    #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
      padding-inline: var(--page-gutter-inline-compact);
      padding-inline-start: max(var(--page-gutter-inline-compact), env(safe-area-inset-left, 0px));
      padding-inline-end: max(var(--page-gutter-inline-compact), env(safe-area-inset-right, 0px));
    }
  }
  /* Reduced motion: the ID+:has() selector above (1,x,x) out-specificities the
     universal a11y clamp, so main-scroll snapping gets its own override —
     mirrors the .bao-shell-sidebar pattern in tokens-glass-shell-webview-surfaces. */
  @media (prefers-reduced-motion: reduce) {
    #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
      scroll-behavior: auto;
    }
  }

  /*
   * Page chrome density (Apple HIG / Hick's law): phone keeps ≤1 labeled primary
   * CTA; secondary chrome is icon+tooltip only. Prefer progressive disclosure
   * over packing 6+ labeled controls in one row.
   */
  @media (max-width: 48rem) {
    [data-page-header-actions] {
      flex-wrap: nowrap;
      overflow-x: auto;
      max-width: 100%;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      gap: var(--space-micro, 0.25rem);
    }
    /*
     * Scroll-edge fade: clipped actions stay discoverable (design.md §9.5),
     * but ONLY over genuinely clipped content — the shell runtime
     * (app-shell-glass.ts bindActionStripOverflowState) stamps
     * data-overflow-start/end from live scroll metrics. An unconditional
     * mask fades the primary CTA even when everything fits, which reads
     * as a half-disabled control.
     */
    [data-page-header-actions][data-overflow-end="true"] {
      mask-image: linear-gradient(to right, var(--black) calc(100% - 1.25rem), transparent);
      -webkit-mask-image: linear-gradient(to right, var(--black) calc(100% - 1.25rem), transparent);
    }
    [data-page-header-actions][data-overflow-start="true"] {
      mask-image: linear-gradient(to left, var(--black) calc(100% - 1.25rem), transparent);
      -webkit-mask-image: linear-gradient(to left, var(--black) calc(100% - 1.25rem), transparent);
    }
    [data-page-header-actions][data-overflow-start="true"][data-overflow-end="true"] {
      mask-image: linear-gradient(to right, transparent, var(--black) 1.25rem, var(--black) calc(100% - 1.25rem), transparent);
      -webkit-mask-image: linear-gradient(to right, transparent, var(--black) 1.25rem, var(--black) calc(100% - 1.25rem), transparent);
    }
    [data-page-header-actions]::-webkit-scrollbar {
      display: none;
    }
    /* Contrast modes: replace the fade with the native scrollbar (design.md §9.3).
       :is() with the attribute variants keeps this override above the
       overflow-scoped masks in specificity. */
    @media (prefers-contrast: more), (forced-colors: active) {
      :is([data-page-header-actions], [data-page-header-actions][data-overflow-start="true"], [data-page-header-actions][data-overflow-end="true"]) {
        mask-image: none;
        -webkit-mask-image: none;
        scrollbar-width: thin;
        scrollbar-color: var(--glass-scrollbar-thumb, color-mix(in oklab, var(--color-base-content) 40%, transparent)) var(--glass-scrollbar-track, transparent);
      }
      [data-page-header-actions]::-webkit-scrollbar {
        display: block;
        height: 0.375rem;
      }
    }
    /*
     * Phone density: icon-only densify for ghost/outline square chrome only.
     * Primary + soft clinical secondaries (finish / generate report) keep labels
     * (design.md: essentials must not depend on hover).
     */
    [data-page-header-actions] .bao-toolbar-action:not(.btn-primary):not(.btn-soft) .bao-toolbar-label,
    [data-page-header-actions] .btn:not(.btn-primary):not(.btn-soft) > span.bao-toolbar-label,
    [data-page-header-actions] .btn:not(.btn-primary):not(.btn-soft) > span:not(.sr-only):not([class*="loading"]):not(.badge) {
      display: none;
    }
    [data-page-header-actions] .btn {
      flex-shrink: 0;
      flex-direction: row;
      align-items: center;
    }
    /* Dashboard customize trays: long panel titles → icon density / truncate. */
    [data-dashboard-visibility-tray] .btn:not(.btn-primary),
    [data-dashboard-uniform-size-tray] .btn:not(.btn-primary) {
      max-width: 10rem;
    }
    [data-dashboard-visibility-tray] .btn:not(.btn-primary) {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    [data-primitive="PageHeader"] {
      gap: var(--space-inline, 0.5rem);
    }
    /* Shell topbar: single dense row — no multi-line brand/actions. */
    #app-header.navbar {
      flex-wrap: nowrap;
      min-height: calc(var(--chrome-min-height, 2.25rem) + env(safe-area-inset-top, 0px));
    }
    #app-header .navbar-start {
      flex: 1 1 auto;
      min-width: 0;
      max-width: 70%;
    }
    /* Phone: center search/status stays off; webview SSOT owns tablet+ show. */
    #app-header .navbar-end {
      flex: 0 0 auto;
      max-width: none;
      overflow: visible;
    }
    /*
     * Phone: context strip scrolls in one line — never a multi-row chip wall
     * that pushes brand/title off-screen (Apple nav density).
     */
    #app-header [data-context-switcher],
    #context-switcher {
      max-width: min(55vw, 14rem);
      overflow-x: auto;
      overflow-y: hidden;
      overscroll-behavior-x: contain;
      -webkit-overflow-scrolling: touch;
    }
    /* Page header actions: never overshoot viewport (models Customize etc.). */
    [data-page-header-actions],
    [data-primitive="PageHeader"] [data-page-header-actions] {
      max-width: 100%;
      min-width: 0;
      width: 100%;
      justify-content: flex-start;
      flex-wrap: wrap;
    }
    [data-page-header-actions] > .btn,
    [data-page-header-actions] > a.btn {
      max-width: 100%;
      min-width: 0;
      flex: 0 1 auto;
      /* HIG 44px primary CTAs (New Case, etc.) — btn-sm alone is 32px. */
      min-height: var(--touch-target-min, 2.75rem);
      height: auto;
    }
    /* Icon-only densify ghost/outline only — soft/primary keep verb labels. */
    [data-page-header-actions] > .btn:not(.btn-primary):not(.btn-soft) .bao-toolbar-label,
    [data-page-header-actions] > a.btn:not(.btn-primary):not(.btn-soft) .bao-toolbar-label {
      display: none;
    }
    [data-page-header-actions] > .btn:not(.btn-primary):not(.btn-soft),
    [data-page-header-actions] > a.btn:not(.btn-primary):not(.btn-soft) {
      width: var(--touch-target-min, 2.75rem);
      min-width: var(--touch-target-min, 2.75rem);
      max-width: var(--touch-target-min, 2.75rem);
      padding-inline: 0;
      justify-content: center;
    }
    [data-page-header-actions] > .btn.btn-soft,
    [data-page-header-actions] > a.btn.btn-soft {
      width: auto;
      max-width: none;
      min-width: var(--touch-target-min, 2.75rem);
      padding-inline: 0.5rem;
      justify-content: center;
      gap: var(--space-micro, 0.25rem);
    }
    [data-page-header-actions] > .btn.btn-soft .bao-toolbar-label,
    [data-page-header-actions] > a.btn.btn-soft .bao-toolbar-label {
      display: inline-block;
      max-width: 8rem;
      opacity: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    #app-header [data-context-switcher] ul,
    #context-switcher-menu {
      display: flex;
      flex-wrap: nowrap;
      gap: var(--space-micro);
      width: max-content;
    }
    /* Icon-first square chrome only — never strip verb+noun primary CTAs. */
    #app-header .btn-square .bao-toolbar-label,
    [data-page-header-actions] .btn-square .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border-width: 0;
    }
    /*
     * Keyboard cheatsheet: keep HTML hidden sm:inline-flex — do not force
     * display:none here (that wiped the control on every phone width and
     * could win over sm: for residual viewports). Phone uses user menu.
     */
    /* Tooltips as block captions under icons — kill on touch. */
    #app-header .tooltip::before,
    #app-header .tooltip::after,
    [data-page-header-actions] .tooltip::before,
    [data-page-header-actions] .tooltip::after {
      display: none;
    }
  }


  /* Immersive pages: :has for modern engines + data-main-page-type for WebView 101. */
  #app-main[data-main-page-type="media"],
  #app-main[data-main-page-type="conversation"],
  #app-main:has([data-page-type="media"]),
  #app-main:has([data-page-type="conversation"]) {
    overflow: hidden;
    height: 100%;
    padding-inline: 0;
    padding-block-start: 0;
    /* Dock clearance owned here + framework phone densify — never bury shutter. */
    padding-block-end: calc(
      var(--dock-height, 4rem) + env(safe-area-inset-bottom, 0px)
    );
  }
  @media (min-width: 80rem) {
    #app-main[data-main-page-type="media"],
    #app-main[data-main-page-type="conversation"],
    #app-main:has([data-page-type="media"]),
    #app-main:has([data-page-type="conversation"]) {
      padding-block-end: 0;
    }
  }
  /* Standard pages: consistent content column padding via main; avoid double chrome.
   * block-end is dock SSOT — never use padding-block shorthand here. */
  #app-main:has([data-page-type="home"]),
  #app-main:has([data-page-type="collection"]) {
    padding-block-start: var(--page-gutter-block);
  }
  [data-page-type="media"],
  [data-page-type="conversation"] {
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
  }
  #app-main > [data-page][data-page-type="media"],
  #app-main > [data-page][data-page-type="conversation"] {
    min-height: 0;
    flex: 1 1 auto;
  }
  [data-page-type="media"] > [data-state-content],
  .bao-media-page-root {
    display: flex;
    min-height: 0;
    flex: 1 1 0%;
    flex-direction: column;
    gap: var(--space-inline);
    overflow: hidden;
  }
  /* Capture chrome: command strip shrinks, workbench owns remaining height. */
  [data-page-type="media"] [data-capture-chrome],
  [data-page-type="media"] [data-session-mode-panel],
  .bao-media-page-root [data-capture-chrome] {
    display: flex;
    min-height: 0;
    flex: 1 1 0%;
    flex-direction: column;
    gap: var(--space-inline);
  }
  [data-page-type="media"] [data-capture-context-strip],
  [data-page-type="media"] [data-capture-command-chrome],
  [data-page-type="media"] [data-session-mode-tabs] {
    flex: 0 0 auto;
  }
  .bao-media-workbench,
  [data-page-type="media"] [data-capture-workbench] {
    display: flex;
    flex-wrap: wrap;
    min-width: 0;
    flex: 1 1 auto;
    align-items: stretch;
    gap: var(--space-section);
  }

  /*
   * Model readiness / missing-context gates — dense sticky Alert under session
   * chrome (daisyUI alert SSOT; data-model-readiness-gate owns density).
   */
  [data-page="capture"] [data-model-readiness-gate]:not([hidden]),
  [data-page="capture"] [data-capture-missing-context]:not([hidden]) {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky, 1100);
    margin-block: 0;
  }
  [data-page="capture"] [data-model-readiness-gate][data-state="success"] {
    display: none;
  }
  [data-page="capture"] [data-model-readiness-gate] {
    max-width: 100%;
    overflow: hidden;
  }
  [data-page="capture"] [data-model-readiness-gate] [data-model-readiness-message],
  [data-page="capture"] [data-model-readiness-gate] [data-model-readiness-hint] {
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* Narrow gate: full-width action row (container query, not viewport). */
  @container (max-width: 28rem) {
    [data-model-readiness-gate] [data-model-readiness-actions] {
      flex: 1 1 100%;
      justify-content: stretch;
      margin-inline-start: 0;
    }
    [data-model-readiness-gate] [data-model-readiness-actions] .btn {
      flex: 1 1 auto;
      justify-content: center;
    }
  }
  /*
   * Registry status readout is non-actionable chrome — hide at all widths.
   * Live device state belongs on the preview overlay / shell status, not a
   * second fieldset that steals command-strip height (Apple camera density).
   */
  [data-page="capture"] [data-capture-device-status],
  [data-page="capture"] [data-capture-command-chrome] [data-capture-control][role="status"] {
    display: none;
  }

  /*
   * Capture chrome scroll host: #app-main is overflow:hidden for media pages,
   * so chrome must own vertical scroll and keep workbench usable.
   * Flex basis 0% (not auto) is required so the host shrinks inside the
   * bounded media column — otherwise overflow-y:auto never engages on WebView.
   * Customizer shell (data-dashboard-layout-root) must participate in the flex
   * chain — a bare section breaks min-height:0 and clips without scrolling.
   */
  /*
   * Only the ACTIVE session panel participates in flex geometry.
   * Forcing display:flex on [hidden] panels overrode the HTML hidden attribute
   * and stacked acquire + annotate workbenches in one scroll (DOM / UX failure).
   */
  [data-page="capture"] [data-session-panel][hidden],
  [data-page="capture"] [data-tab-panel][hidden] {
    display: none;
  }
  [data-page="capture"] [data-session-panel]:not([hidden]),
  [data-page="capture"] [data-tab-panel]:not([hidden]) {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    flex: 1 1 0%;
    overflow: hidden;
  }
  [data-page="capture"] [data-session-panel]:not([hidden]) > [data-dashboard-layout-root],
  [data-page="capture"] [data-tab-panel]:not([hidden]) > [data-dashboard-layout-root] {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    flex: 1 1 0%;
    overflow: hidden;
  }
  [data-page="capture"] [data-capture-chrome] {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    flex: 1 1 0%;
    overflow-x: clip;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
  }
  [data-page="capture"] [data-capture-workbench] {
    display: flex;
    min-width: 0;
    flex: 1 1 auto;
    container-type: inline-size;
    container-name: capture-workbench;
  }

  /* Fluid workbench: stack when the workbench container is narrow (not only viewport). */
  @container capture-workbench (max-width: 40rem) {
    [data-capture-workbench] {
      flex-direction: column;
      flex-wrap: nowrap;
    }
    [data-capture-workbench] > * {
      flex: 0 0 auto;
      max-width: 100%;
      min-width: 0;
      width: 100%;
    }
    [data-capture-workbench-primary] {
      order: 0;
      flex: 1 1 auto;
      min-height: min(48dvh, 22rem);
    }
    [data-capture-workbench-secondary] {
      order: 1;
      flex: 0 1 auto;
      max-height: none;
      overflow-y: visible;
      overscroll-behavior: contain;
    }
  }

/*
 * Permanent-rail mode/recent/AI hide SSOT lives in tokens-glass-shell-capture.ts
 * AFTER the unlayered [data-session-mode-tabs] { display: flex } rule so cascade
 * cannot re-show main mode tabs on desktop (dual Acquire/Annotate violation).
 */


  /* Mobile fluidity fallback when CQ unsupported / outer shell is narrow. */
  @media (max-width: 64rem) {
    .bao-media-workbench,
    [data-page-type="media"] [data-capture-workbench] {
      flex-direction: column;
      flex-wrap: nowrap;
    }
    .bao-media-workbench > *,
    [data-page-type="media"] [data-capture-workbench] > * {
      flex: 0 0 auto;
      max-width: 100%;
      min-width: 0;
      width: 100%;
    }
    /*
     * No min-height here. The stage minimums have ONE owner —
     * tokens-glass-shell-capture.ts, which emits unlayered and therefore wins
     * over this module's @layer utilities regardless of specificity. Every
     * min-height this module used to declare for the workbench, its primary
     * column and the preview region was dead on arrival: measured computed
     * values at 1440x900, 768x1024, 390x844 and 320x720 all resolve to the
     * unlayered rules (62vh/36rem, 48vh/22rem, 48vh/28rem) and never to the
     * values written here. Declaring a second, disagreeing set only made the
     * sizing contract unreadable from source.
     */
    .bao-media-workbench-primary,
    [data-capture-workbench-primary] {
      order: 0;
      flex: 1 1 auto;
    }
    .bao-media-workbench-secondary,
    [data-capture-workbench-secondary] {
      order: 1;
      flex: 0 0 auto;
    }

    /*
     * Capture actions: icon-first (Apple camera chrome). Labels live in
     * aria-label + data-tip; never inflate the shutter row with verbs.
     * Gallery pick is a standard .bao-toolbar-action — the same clip covers
     * it; no gallery-specific rules exist anywhere.
     */
    [data-capture-preview-overlay] .bao-toolbar-action .bao-toolbar-label,
    [data-capture-action-bar] .bao-toolbar-action .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    /*
     * Phone capture densify — industry camera pattern:
     * tabs → context capsule → gate → device strip → preview owns stage.
     */
    [data-page="capture"] [data-capture-context-strip],
    [data-page="capture"] [data-capture-top-chrome] {
      order: 1;
      flex: 0 0 auto;
    }
    [data-page="capture"] [data-model-readiness-gate],
    [data-page="capture"] [data-capture-missing-context] {
      order: 2;
      flex: 0 0 auto;
    }
    [data-page="capture"] [data-capture-command-chrome] {
      order: 3;
      flex: 0 0 auto;
      gap: var(--space-micro);
      padding-block: var(--space-micro);
      padding-inline: var(--space-inline);
      max-height: none;
      border-bottom: 1px solid var(--color-base-300, var(--border-base));
    }
    [data-page="capture"] [data-capture-workbench] {
      order: 4;
      flex: 1 1 auto;
    }
    /* Non-actionable registry status steals a full fieldset on phone — hide. */
    [data-page="capture"] [data-capture-device-status],
    [data-page="capture"] [data-capture-command-chrome] [data-capture-control][role="status"] {
      display: none;
    }
    [data-page="capture"] [data-capture-workbench-primary] {
      order: 0;
      flex: 1 1 auto;
    }
    [data-page="capture"] [data-capture-workbench-secondary] {
      order: 1;
      flex: 0 0 auto;
      max-height: none;
      overflow-y: visible;
      pointer-events: none;
    }
    [data-page="capture"] .bao-preview-region {
      flex: 1 1 auto;
    }
    /*
     * Live-only float: idle shutter chrome stays in-flow (static) so tablet
     * columns never paint overlay over session controls (structure audit).
     */
    [data-page="capture"]
      .bao-preview-region[data-preview-state="live"]
      > [data-capture-preview-overlay],
    [data-page="capture"]
      .bao-preview-region[data-preview-state="live"]
      > [data-capture-preview-overlay] {
      z-index: var(--z-overlay, 1200);
      inset-inline: var(--space-inline, 0.5rem);
      bottom: var(--space-section, 0.75rem);
      justify-content: space-between;
      align-items: flex-end;
    }
    [data-page="capture"]
      .bao-preview-region:not([data-preview-state="live"])
      > [data-capture-preview-overlay] {
      position: static;
      inset: auto;
      bottom: auto;
      z-index: auto;
    }
    /*
     * Tablet idle overflow containment: when overlay is position:static,
     * preview region must clip overflow so shutter chrome never paints over
     * session controls below (structure: capture-idle-overlay-session-collision).
     */
    [data-page="capture"]
      .bao-preview-region:not([data-preview-state="live"]) {
      flex: 0 0 auto;
      height: auto;
      overflow: clip;
    }
    [data-page="capture"] [data-capture-preview-overlay] .bao-capture-shutter {
      width: 3.5rem;
      height: 3.5rem;
      min-width: 3.5rem;
      min-height: 3.5rem;
    }
    /*
     * Tablet action-bar anti-occlusion: when idle overlay is position:static
     * (not live preview), the action bar inside it must stay above session
     * content below. Ensure action bar has proper stacking context.
     */
    [data-page="capture"]
      .bao-preview-region:not([data-preview-state="live"])
      [data-capture-action-bar] {
      position: relative;
      z-index: 1;
    }
    /*
     * Tablet portrait (≤64rem) acquire: secondary rail stacks BELOW the stage
     * (order after primary) so idle shutter chrome never intersects session
     * controls (structure: capture-idle-overlay-session-collision). Densify
     * heading + recent captures; keep discovery. Annotate keeps full rail.
     */
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-secondary] [data-capture-session-heading],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-secondary] [data-capture-session-heading],
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-secondary] [data-capture-recent-captures],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-secondary] [data-capture-recent-captures] {
      display: none;
      max-height: 0;
      min-height: 0;
      overflow: hidden;
      margin: 0;
      padding: 0;
      border-width: 0;
      pointer-events: none;
      visibility: hidden;
    }
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-primary],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-primary] {
      order: 0;
      /* Size to in-flow shutter chrome — flex:1 shrink clipped overlay into secondary (structure audit). */
      flex: 0 0 auto;
      height: auto;
      min-width: 0;
      width: 100%;
      overflow: visible;
    }
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-secondary],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-secondary] {
      display: flex;
      flex-direction: column;
      /* Room for case picker + specimen without clipping centres under stage. */
      /* No max-height: nested clip left last case cards under host (reachability). */
      max-height: none;
      min-height: 0;
      width: 100%;
      overflow-x: visible;
      overflow-y: visible;
      overscroll-behavior: contain;
      scroll-padding-block: var(--space-section, 0.75rem);
      /* Host PE none; case rows/inputs re-arm auto (reachability centre-hit). */
      pointer-events: none;
      visibility: visible;
      /* After primary stage so shutter (in primary) cannot paint over session. */
      order: 2;
    }
  }
  /* 44px touch floor on session-rail controls (case/specimen pickers,
     add-specimen form) — the command-chrome rule never covered the rail. */


  @media (pointer: coarse) {
    [data-page="capture"] [data-capture-workbench-secondary] .select,
    [data-page="capture"] [data-capture-workbench-secondary] input,
    [data-page="capture"] [data-capture-workbench-secondary] .btn {
      min-height: var(--touch-target-min, 2.75rem);
    }
  }
  @media (max-width: 47.99rem) {
    /*
     * iOS-camera fold order: the viewfinder owns the screen on phones. The
     * device/source fieldsets move BELOW the stage (still reachable by
     * scroll), and the stage bleeds edge-to-edge without panel chrome
     * (§8.6 immersive stage; unlayered token rules beat the card utilities).
     */
    [data-page="capture"] [data-capture-command-chrome] {
      order: 5;
      border-bottom: 0;
      border-top: 1px solid var(--color-base-300, var(--border-base));
    }
    [data-page="capture"] .bao-preview-region {
      border-radius: 0;
      border-inline-width: 0;
    }
    /* Camera idiom: shutter column centered over the stage; every overlay
       row (tuning, shutter form wrapper, action rack) centers its content
       instead of hugging an edge. */
    [data-page="capture"] [data-capture-preview-overlay] {
      align-items: center;
    }
    [data-page="capture"] [data-capture-preview-overlay] > div,
    [data-page="capture"] [data-capture-preview-overlay] [data-capture-action-bar] {
      justify-content: center;
    }
    [data-page="capture"]
      .bao-preview-region[data-preview-state="live"]
      > [data-capture-preview-overlay] {
      bottom: calc(
        var(--dock-height) + var(--dock-safe-area-offset) + var(--space-section)
      );
    }
    /*
     * These three are IN FLOW inside #app-main, which already reserves the dock
     * band on its own bottom padding. Each used to add --dock-height again, so
     * each carried a second copy of that band as dead space. Measured at 390x844
     * with #app-main reserving 64px: the idle overlay's margin computed 76px, and
     * the placeholder's padding computed 120px with 120px empty below its centred
     * content inside a 284px box.
     *
     * The idle overlay really is position:static (tokens-layout-capture-preview),
     * so the absolute bottom offset above never applies to it — but static means
     * the container's padding DOES lift it, which is exactly why it must not lift
     * itself as well.
     *
     * What remains is the clearance these elements genuinely own: --touch-target-min
     * for the shutter bar that floats over the preview region, and --space-section
     * for breathing room. Neither is the dock.
     */
    [data-page="capture"]
      .bao-preview-region:not([data-preview-state="live"])
      > [data-capture-preview-overlay] {
      bottom: auto;
      margin-block-end: var(--space-section);
    }
    [data-page="capture"] .bao-preview-region > [data-camera-preview-placeholder] {
      padding-block-end: calc(
        var(--touch-target-min) + var(--space-section)
      );
    }
    /* Secondary panel sits below stage — spacing only; #app-main clears the dock. */
    [data-page="capture"] [data-capture-workbench-secondary] {
      padding-block-end: var(--space-section);
    }
    /*
     * iOS Camera idiom (phone acquire): hide the entire session secondary rail
     * so the viewfinder owns the stage (structure:
     * capture-phone-acquire-secondary-visible). Case identity is shell context
     * + journey capsules. Annotate keeps its inspector rail.
     */
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-secondary],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-secondary] {
      display: none;
      max-height: 0;
      min-height: 0;
      overflow: hidden;
      margin: 0;
      padding: 0;
      border-width: 0;
      pointer-events: none;
      visibility: hidden;
    }
    /* Primary stage owns the phone acquire viewport band. */
    [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-primary],
    [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-primary] {
      min-height: min(78dvh, 36rem);
      flex: 1 1 auto;
      order: 0;
      width: 100%;
    }
    /*
     * Phone context completion (CA-01): the capsule above the shutter opens
     * the hidden session rail as a glass-strong bottom sheet so case +
     * specimen can be set without leaving the capture page.
     */
    [data-page="capture"] [data-capture-chrome][data-context-sheet-open="true"] [data-capture-workbench-secondary] {
      display: flex;
      flex-direction: column;
      position: fixed;
      inset-inline: var(--space-inline, 0.5rem);
      inset-block-end: calc(
        var(--dock-height, 4rem) + var(--dock-safe-area-offset, 0px) + var(--space-inline, 0.5rem)
      );
      z-index: var(--z-overlay, 1200);
      max-height: min(70dvh, 28rem);
      min-height: 0;
      width: auto;
      overflow-y: auto;
      overscroll-behavior: contain;
      -webkit-overflow-scrolling: touch;
      margin: 0;
      padding: var(--space-inline, 0.5rem);
      visibility: visible;
      /* Host passes hits; case rows / inputs re-arm PE so list centres work. */
      pointer-events: none;
      border: 1px solid var(--liquid-border-regular);
      border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
      background: color-mix(
        in oklab,
        var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
        transparent
      );
      backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      box-shadow:
        var(--shadow-glass-floating),
        var(--shadow-glass-inner);
    }
    [data-page="capture"] [data-capture-chrome][data-context-sheet-open="true"] [data-capture-session-heading] {
      display: flex;
      max-height: none;
      min-height: 0;
      overflow: visible;
      visibility: visible;
      pointer-events: auto;
    }
    @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
      [data-page="capture"] [data-capture-chrome][data-context-sheet-open="true"] [data-capture-workbench-secondary] {
        background: var(--bg-panel);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
    @media (prefers-reduced-transparency: reduce) {
      [data-page="capture"] [data-capture-chrome][data-context-sheet-open="true"] [data-capture-workbench-secondary] {
        background: var(--color-base-100);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
    @media (forced-colors: active) {
      [data-page="capture"] [data-capture-chrome][data-context-sheet-open="true"] [data-capture-workbench-secondary] {
        background: Canvas;
        color: CanvasText;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        border: 1px solid ButtonText;
        box-shadow: none;
      }
    }
    /*
     * Phone action bar: gallery · shutter · overflow only — annotate/AI/scan
     * actions left the bar at EVERY breakpoint and live in the grouped
     * overflow sheet, so no per-action hide chains exist. Bar discipline is
     * one tier rule in tokens-glass-shell-capture-overlay.ts.
     */
  }
  /*
   * Context capsule (persistent top chrome, acquire mode at every
   * breakpoint): opens the pickers bottom sheet on phone; reveals the docked
   * session rail on tablet/desktop. Annotate mode owns the inspector rail —
   * the capsule never paints there. Repeated attribute selectors outrank the
   * shell-wide .btn:not([hidden]):not(.hidden) display rule (0,3,0).
   */
  [data-page="capture"][data-session-mode="annotate"] [data-capture-context-sheet-toggle][data-capture-context-sheet-toggle] {
    display: none;
  }

/*
   * Context capsule is the COMPACT context affordance only.
   *
   * app-shell-capture-actions.ts routes it by width: at <=47.99rem
   * captureContextSheetViewport() opens the bottom sheet,
above that
   * revealCaptureContextRail() just scrolls the docked rail into view — where
   * SESSION CONTROLS already renders the active case,
the specimen picker and
   * a Change case action. So on tablet/desktop the capsule restated context
   * the operator could already see,
while its band cost a full row: measured
   * 263px of chrome above the preview at 1280x900,
of which this row was 48px
   * spanning 720px to hold one 200px button.
   *
   * display:none (not removal) — openCaptureContextSheet/closeCaptureContextSheet
   * resolve the toggle through chrome.querySelector and must still find it to
   * reflect aria-expanded.
   */
  @media (min-width: 48rem) {
    [data-capture-chrome] [data-capture-top-chrome] {
      display: none;
    }
  }
  .bao-preview-region {
    position: relative;
    display: flex;
    min-width: 0;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
    container-type: inline-size;
    /* Region box must not steal hits from siblings (device strip) when empty
       planes pass through; interactive descendants re-arm PE:auto below. */
    pointer-events: none;
    background-image: radial-gradient(
      ellipse 120% 80% at 50% 38%,
      color-mix(in oklab, var(--color-primary) 7%, transparent),
      transparent 60%
    );
  }
  .bao-preview-region > [data-camera-preview-video],
  .bao-preview-region button,
  .bao-preview-region a,
  .bao-preview-region input,
  .bao-preview-region select,
  .bao-preview-region textarea,
  .bao-preview-region .btn,
  .bao-preview-region [data-primitive="Button"],
  .bao-preview-region [data-capture-empty-cta],
  .bao-preview-region fieldset {
    pointer-events: auto;
  }.bao-preview-region > [data-camera-preview-video],
.bao-preview-region > [data-camera-preview-placeholder] {
    min-height: 0;
    flex: 1 1 auto;
  }
  /* Idle: keep empty video from painting over the placeholder (mobile void gray). */
  .bao-preview-region > [data-camera-preview-video] {
    z-index: 0;
    background: transparent;
  }
  /* Mirror preview — document flag from hardware prefs;no element style=. */
  html[data-capture-mirror-preview="true"]
    [data-page="capture"]
    [data-camera-preview-video],
html[data-capture-mirror-preview="true"] [data-page="capture"] video {
    transform: scaleX(-1);
  }
  .bao-preview-region:not([data-preview-state="live"])
    > [data-camera-preview-video] {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
  [data-capture-chrome][data-capture-case-ready="false"] [data-capture-command-chrome] {
    display: none;
  }[data-capture-chrome][data-capture-case-ready="false"] [data-camera-preview-video],
[data-capture-chrome][data-capture-case-ready="false"] [data-capture-preview-overlay] {
    display: none;
  }
  .bao-preview-region > [data-camera-preview-placeholder] {
    z-index: 2;
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-inline, 0.5rem);
    padding: var(--space-section, 0.75rem);
    text-align: center;
    /* Empty plane passes hits; CTAs re-arm pointer-events on the action slot. */
    pointer-events: none;
  }/* Case-required shares layout with camera empty. Geometry only — display is
     owned solely by tokens-glass-shell-capture-context-plane.ts,
which must
     stay unlayered to beat the unlayered EmptyState primitive. A display
     declaration here would be dead on arrival. */
  .bao-preview-region > [data-capture-case-required] {
    z-index: 2;
    position: absolute;
    inset: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-inline, 0.5rem);
    padding: var(--space-section, 0.75rem);
    text-align: center;
    pointer-events: none;
  }.bao-preview-region > [data-camera-preview-placeholder] [data-capture-empty-cta],
.bao-preview-region > [data-capture-case-required] [data-capture-empty-cta],
.bao-preview-region > [data-camera-preview-placeholder] fieldset,
.bao-preview-region > [data-capture-case-required] fieldset {
    pointer-events: auto;
  }.bao-preview-region[data-preview-state="live"] > [data-camera-preview-video] {
    opacity: 1;
    visibility: visible;
    z-index: 1;
  }/* Live hide for this plane has ONE owner,
in tokens-glass-shell-capture:
     that copy is unlayered and this module is layered,
so the rule that used
     to sit here could never apply. */
  /* Media capture: primary actions float on the viewport (glass overlay). */
  .bao-preview-region > [data-capture-preview-overlay] {
    pointer-events: none;
  }.bao-preview-region > [data-capture-preview-overlay] .pointer-events-auto,
.bao-preview-region > [data-capture-preview-overlay] form,
.bao-preview-region > [data-capture-preview-overlay] button,
.bao-preview-region > [data-capture-preview-overlay] a {
    pointer-events: auto;
  }
  /* Idle: placeholder and overlay share the column flow instead of stacking
     absolutely — centered placeholder copy must never be occluded by the
     floating action glass (design.md §1.1/§5.3: controls carry their own
     layer; content stays legible). Live keeps the floating overlay. */
  .bao-preview-region:not([data-preview-state="live"]) {
    row-gap: var(--space-inline, 0.5rem);
  }
  .bao-preview-region:not([data-preview-state="live"])
    > [data-camera-preview-placeholder] {
    position: relative;
    inset: auto;
    flex: 1 1 auto;
  }/*
   * Idle: control chrome is IN FLOW under the placeholder,
docked to the
   * SAME bottom band the live overlay floats over — no geometry jump of the
   * primary control cluster when a stream attaches (CLS guard). Absolute
   * bottom-docked utilities are live-only so the shutter never paints over
   * session controls (mobile composition — design.md §5.3 / §8.6).
   *
   * The dock owns its own placement rather than inheriting it as a side
   * effect: an auto block-start margin consumes whatever free space the
   * column has,
so the resting offset is the block-end margin alone. That
   * margin is --space-section,
the same value the live overlay uses for its
   * bottom inset above,
so the cluster does not shift when a stream attaches.
   * The previous --space-inline block margin left idle 4px higher than live,
* and left the dock's position dependent on the placeholder still winning
   * the flex-grow — a coupling that silently breaks if that sibling changes.
   *
   * Phone (<=47.99rem) deliberately overrides the block-end margin with the
   * fixed mobile dock's clearance (tokens-layout-capture-preview phone band):
   * there the stage is full-bleed and the live overlay floats at the same
   * dock-cleared offset,
so the larger idle gap is the CLS guard,
not slack.
   */
  .bao-preview-region:not([data-preview-state="live"])
    > [data-capture-preview-overlay] {
    position: static;
    inset: auto;
    bottom: auto;
    top: auto;
    z-index: auto;
    margin-block-start: auto;
    margin-block-end: var(--space-section, 0.75rem);
    margin-inline: var(--space-section, 0.75rem);
    align-self: stretch;
    width: auto;
  }/* Live: float controls over the stream (camera idiom). */
  .bao-preview-region[data-preview-state="live"] > [data-capture-preview-overlay],
.bao-preview-region[data-preview-state="live"] > [data-capture-preview-overlay] {
    position: absolute;
    inset-inline: var(--space-section, 0.75rem);
    bottom: var(--space-section, 0.75rem);
    z-index: var(--z-overlay, 1200);
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-inline);
    padding: var(--space-section);
  }
  /* The overlay action row never wraps — one inline media-control bar at
     every breakpoint; secondary actions live in the overflow sheet. */
  [data-capture-preview-overlay] [data-capture-action-bar] {
    flex-wrap: nowrap;
  }
  /* Camera composition (Apple grammar): gallery · shutter · overflow. The
     shutter is the centered hero;gallery docks leading,
the single overflow
     trigger docks trailing — nothing else paints in the bar. */
  [data-capture-preview-overlay] [data-capture-preview-actions] {
    flex-wrap: nowrap;
    justify-content: center;
  }
  [data-capture-preview-overlay] [data-capture-preview-actions] [data-capture-gallery-root] {
    margin-inline-end: auto;
  }
  [data-capture-preview-overlay] [data-capture-preview-actions] [data-capture-action-bar] {
    margin-inline-start: auto;
  }/*
   * Persistent top chrome: the context capsule sits under the segmented mode
   * selector — the only top elements besides the L0 viewfinder. Solid pill
     chrome (persistent control,
not a transient glass layer).
   */
  [data-page="capture"] [data-capture-top-chrome] {
    flex: 0 0 auto;
    padding-block: 0 var(--space-micro, 0.25rem);
  }
  [data-page="capture"] [data-capture-top-chrome] [data-capture-context-sheet-toggle] {
    border-radius: var(--radius-full, 9999px);
    max-width: min(100%, 16rem);
  }

  /*
   * Session mode tabs — always-labeled two-item segmented control on phone.
   * Hover-reveal of inactive labels is a pointer-only cheat;touch has no
   * hover,
so both Acquire/Annotate keep permanent verbs (Apple HIG).
   */
  @media (max-width: 40rem) {
    [data-session-mode-tabs] .bao-session-mode-tab .bao-toolbar-label {
      display: inline-block;
      max-width: 8rem;
      margin-inline-start: var(--space-micro, 0.25rem);
      opacity: 1;
      overflow: hidden;
      white-space: nowrap;
    }
  }

  /* Device strip: one row when possible; never stretch fieldsets into voids.
   * display:contents unwraps the details on desktop, so z-index on the host is
   * dead — re-arm PE on real boxes (summary + content children) and pass hits
   * through the stage primary when geometry still overlaps the strip. */
  [data-page="capture"] [data-capture-command-chrome] {
    flex: 0 0 auto;
    flex-shrink: 0;
    overflow: visible;
  }
  [data-page="capture"] [data-capture-command-chrome] > *,
  [data-page="capture"] [data-capture-device-select],
  [data-page="capture"] [data-camera-rescan],
  [data-page="capture"] [data-capture-control] {
    position: relative;
    z-index: 3;
    pointer-events: auto;
  }
  /* Stage hosts pass hits when their boxes still overlap the device strip;
   * interactive descendants re-arm PE:auto so shutter/CTAs keep working. */
  [data-page="capture"] [data-capture-workbench],
  [data-page="capture"] .bao-media-workbench,
  [data-page="capture"] [data-capture-workbench-primary],
  [data-page="capture"] .bao-media-workbench-primary,
  [data-page="capture"] [data-capture-workbench-secondary],
  [data-page="capture"] .bao-media-workbench-secondary {
    pointer-events: none;
  }
  [data-page="capture"] [data-capture-workbench-secondary] *,
  [data-page="capture"] .bao-media-workbench-secondary * {
    pointer-events: none;
  }
  /* Re-arm only real controls — never fieldset/summary hosts (they steal centres). */
  [data-page="capture"] [data-capture-workbench] button,
  [data-page="capture"] [data-capture-workbench] a,
  [data-page="capture"] [data-capture-workbench] input:not([type="hidden"]),
  [data-page="capture"] [data-capture-workbench] select,
  [data-page="capture"] [data-capture-workbench] textarea,
  [data-page="capture"] [data-capture-workbench] .btn,
  [data-page="capture"] [data-capture-workbench] [data-primitive="Button"],
  [data-page="capture"] [data-capture-workbench] [data-case-card],
  [data-page="capture"] [data-capture-workbench] [data-case-option],
  [data-page="capture"] [data-capture-workbench-secondary] button,
  [data-page="capture"] [data-capture-workbench-secondary] a,
  [data-page="capture"] [data-capture-workbench-secondary] input:not([type="hidden"]),
  [data-page="capture"] [data-capture-workbench-secondary] select,
  [data-page="capture"] [data-capture-workbench-secondary] .btn,
  [data-page="capture"] [data-capture-workbench-secondary] [data-case-card],
  [data-page="capture"] [data-capture-workbench-secondary] [data-case-option],
  [data-page="capture"] .bao-media-workbench button,
  [data-page="capture"] .bao-media-workbench a,
  [data-page="capture"] .bao-media-workbench input:not([type="hidden"]),
  [data-page="capture"] .bao-media-workbench select,
  [data-page="capture"] .bao-media-workbench textarea,
  [data-page="capture"] .bao-media-workbench .btn,
  [data-page="capture"] .bao-media-workbench [data-primitive="Button"],
  [data-page="capture"] .bao-media-workbench [data-case-card],
  [data-page="capture"] .bao-media-workbench [data-case-option] {
    pointer-events: auto;
  }
  [data-page="capture"] [data-case-picker-disclosure] > summary {
    position: relative;
    z-index: 1;
    pointer-events: auto;
  }
  [data-page="capture"] [data-capture-command-chrome] .bao-capture-device-grid {
    align-items: end;
  }
  /*
   * Nested more-sources is details+daisyUI collapse. Collapse keeps a grid
   * track when closed, so Rescan network retained a real getBoundingClientRect
   * while unreachable — the visual gate stamped it then failed after prior
   * actions. Force closed children out of layout at every breakpoint (phone
   * already had this inside the max-width sheet band).
   */
  [data-page="capture"] [data-capture-more-sources]:not([open]) > :not(summary) {
    display: none;
  }
  /*
   * Phone/tablet: device picker lives in a collapsed disclosure so the
   * preview owns the first viewport (Apple camera idiom / design.pdf §8).
   */
  @media (max-width: 63.99rem) {
    [data-page="capture"] [data-capture-device-sheet]:not([open]) {
      flex: 0 0 auto;
    }
    /*
     * Author flex/grid on device-sheet children outranks UA closed-details
     * hide: selects kept real boxes outside the summary-only host and
     * scrollIntoView placed them under the absolute preview video
     * (phone reachability). Hide non-summary children when closed.
     */
    [data-page="capture"] [data-capture-device-sheet]:not([open]) > :not(summary),
    [data-page="capture"] [data-capture-more-sources]:not([open]) > :not(summary) {
      display: none;
    }
    [data-page="capture"] [data-capture-device-sheet] > summary {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: var(--space-micro, 0.25rem);
      min-height: var(--touch-target-min, 2.75rem);
      padding-inline: var(--space-inline, 0.5rem);
      padding-block: var(--space-micro, 0.25rem);
      border: 1px solid color-mix(in oklab, var(--color-base-300) 60%, transparent);
      border-radius: var(--radius-field, 0.5rem);
      background: var(--color-base-100);
      cursor: pointer;
      list-style: none;
    }
    [data-page="capture"] [data-capture-device-sheet] > summary::-webkit-details-marker {
      display: none;
    }
    [data-page="capture"] [data-capture-device-sheet][open] {
      position: fixed;
      inset-inline: 0;
      inset-block-end: calc(var(--dock-height, 4rem) + var(--dock-safe-area-offset, 0px));
      z-index: var(--z-overlay, 1200);
      max-height: min(70dvh, 28rem);
      overflow-y: auto;
      overscroll-behavior: contain;
      -webkit-overflow-scrolling: touch;
      border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
      border: 1px solid var(--liquid-border-regular);
      /* L2 transient sheet — glass-strong (design.pdf §5.7); solid material path below. */
      background: color-mix(
        in oklab,
        var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
        transparent
      );
      backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      padding: var(--space-inline, 0.5rem);
      box-shadow:
        var(--shadow-glass-floating),
        var(--shadow-glass-inner);
    }
    @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
      [data-page="capture"] [data-capture-device-sheet][open] {
        background: var(--bg-panel);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
    @media (prefers-reduced-transparency: reduce) {
      [data-page="capture"] [data-capture-device-sheet][open] {
        background: var(--color-base-100);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
  }

  /* Highest-specificity re-arm for case picker cards (beat secondary * none). */
  [data-page="capture"] [data-capture-workbench-secondary] button[data-case-option],
  [data-page="capture"] [data-capture-workbench-secondary] button[data-case-card],
  [data-page="capture"] .bao-media-workbench-secondary button[data-case-option],
  [data-page="capture"] .bao-media-workbench-secondary button[data-case-card] {
    pointer-events: auto;
    position: relative;
    z-index: 2;
  }


  @media (min-width: 64rem) {/*
     * Keep the device disclosure as a real layout box on workstations. A
     * closed <details> must remove its body from layout and hit testing;
     * flattening the host leaves controls with rectangles that are painted
     * underneath the preview and cannot receive a real pointer click.
     */
    [data-page="capture"] [data-capture-device-sheet] {
      display: block;
    }
    [data-page="capture"] [data-capture-device-sheet]:not([open]) > :not(summary) {
      display: none;
    }/*
     * The summary remains a full-width disclosure chip for the controls it governs.
     * It keeps the same touch-target floor and field tokens as the phone/tablet
     * summary, so the affordance reads identically at every width.
     */
    [data-page="capture"] [data-capture-device-sheet] > summary {
      display: inline-flex;
      flex: 0 0 auto;
      align-items: center;
      gap: var(--space-micro, 0.25rem);
      min-height: var(--touch-target-min, 2.75rem);
      padding-inline: var(--space-inline, 0.5rem);
      padding-block: var(--space-micro, 0.25rem);
      border: 1px solid color-mix(in oklab, var(--color-base-300) 60%, transparent);
      border-radius: var(--radius-field, 0.5rem);
      background: var(--color-base-100);
      cursor: pointer;
      list-style: none;
    }
    [data-page="capture"] [data-capture-device-sheet] > summary::-webkit-details-marker {
      display: none;
    }/*
     * Desktop tuning popover: the trigger lives in the overflow sheet,
so
     * the panel must not paint far from the bar inside the normal details box
     * device strip. While open (data-tuning-popover on the chrome root,
set
     * by app-shell-camera-tuning-controls) it re-anchors as an L2 transient
     * glass popover centered above the media bar. Solid + reduced-
     * transparency + forced-colors fallbacks per the a11y matrix.
     */
    [data-capture-chrome][data-tuning-popover="open"] [data-uvc-tuning-panel]:not([hidden]) {
      position: fixed;
      inset-inline: 0;
      inset-block-end: calc(var(--space-section, 0.75rem) + 5rem);
      z-index: var(--z-overlay, 1200);
      inline-size: min(24rem, calc(100vw - 2 * var(--space-section, 0.75rem)));
      margin-inline: auto;
      max-block-size: min(60dvh, 26rem);
      overflow-y: auto;
      overscroll-behavior: contain;
      padding: var(--space-inline, 0.5rem);
      border: 1px solid var(--liquid-border-regular);
      border-radius: var(--radius-box, 1rem);
      background: color-mix(
        in oklab,
        var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
        transparent
      );
      backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
      box-shadow:
        var(--shadow-glass-floating),
        var(--shadow-glass-inner);
    }
    @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
      [data-capture-chrome][data-tuning-popover="open"] [data-uvc-tuning-panel]:not([hidden]) {
        background: var(--bg-panel);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
    @media (prefers-reduced-transparency: reduce) {
      [data-capture-chrome][data-tuning-popover="open"] [data-uvc-tuning-panel]:not([hidden]) {
        background: var(--color-base-100);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
      }
    }
    @media (forced-colors: active) {
      [data-capture-chrome][data-tuning-popover="open"] [data-uvc-tuning-panel]:not([hidden]) {
        background: Canvas;
        color: CanvasText;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        border: 1px solid ButtonText;
        box-shadow: none;
      }
    }
  }
  /* Narrow container: collapse device grid to a single column so fieldsets
     stack instead of shrinking below usable width. */
  @container (max-width: 48rem) {
    [data-page="capture"] [data-capture-command-chrome] .bao-capture-device-grid {
      grid-template-columns: 1fr;
      gap: var(--space-micro);
    }
  }/*
   * Media immersive: hide layout customizer chrome — stage owns the fold.
   * Customize remains on collection/home surfaces and via command palette.
   * Specificity beats page-header action flex rules without importance.
   */
  [data-page-type="media"] [data-page-header-actions] [data-dashboard-edit-toggle].btn,
[data-page-type="media"] [data-dashboard-customizer-dropdown],
[data-page-type="media"] [data-dashboard-customizer-popover],
[data-page-type="media"] [data-dashboard-panel-controls],
[data-page="capture"] [data-page-header-actions] [data-dashboard-edit-toggle].btn,
[data-page="editor"] [data-page-header-actions] [data-dashboard-edit-toggle].btn {
    display: none;
    width: 0;
    min-width: 0;
    max-width: 0;
    height: 0;
    min-height: 0;
    overflow: hidden;
    padding: 0;
    margin: 0;
    border: 0;
    pointer-events: none;
    visibility: hidden;
  }
  [data-page="capture"] [data-capture-workbench-secondary] {
    /* overflow-y:auto re-clipped case-card centres (reachability); page chrome scrolls. */
    overflow-y: visible;
    overscroll-behavior: contain;
    min-height: 0;
  }

  /*
   * Media page header is sr-only (shell title owns naming). Keep a compact
   * escape hatch if a page still emits a visible header.
   */
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"]:not(.sr-only) {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-micro);
    padding-block-end: var(--space-micro);
    /* Width alone inherited preflight currentColor — a full-brightness
       hairline. Divide with the same subtle token mix panels use. */
    border-block-end: 1px solid
      color-mix(in oklab, var(--color-base-300, var(--border-base)) 30%, transparent);
  }
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"]:not(.sr-only) h1 {
    font-size: var(--text-sm, 0.875rem);
    line-height: 1.25;
  }
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"]:not(.sr-only) p {
    display: none;
  }
  /* Session mode tabs sit under shell chrome as primary media navigation.
     Segmented sizing SSOT: tokens-glass-shell-capture.ts. */
  [data-page-type="media"] [data-session-mode-tabs] {
    margin-block-end: 0;
  }/*
   * The phone dock for these header actions is NOT declared here. This module
   * emits inside @layer utilities,
and the canonical
   * .bao-page-header width:100% is unlayered — an unlayered declaration
   * beats a layered one regardless of specificity,
so a width set here is
   * silently discarded (measured: inline-size stayed 359px). It lives in
   * tokens-glass-shell-capture-status.ts,
which emits unlayered.
   */
  /*
   * Media full-bleed belongs to the camera/canvas STAGE only. Structured
   * chrome (page header,
journey band,
control strips) keeps the page
   * gutter — content must never shear against the viewport edge (§8.2/§9.5).
   */
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"]:not(.sr-only),
[data-page-type="media"] [data-capture-workbench-secondary] {
    padding-inline: var(--page-gutter-inline, var(--space-section, 0.75rem));
  }/*
   * Fine pointer: icon-first with tip/hover reveal. Coarse pointer: visible
   * compact labels — hover must not own essential media actions (design.pdf §6).
   *
   * Both branches now honour data-toolbar-density. They did not: the coarse
   * branch below gated on [data-toolbar-density="labeled"] while this one
   * clipped the editor dock unconditionally,
so the same file answered the
   * same question two ways and a dock asking for labels got them on touch and
   * lost them on a mouse.
   */
  @media (hover: hover) and (pointer: fine) {[data-page="capture"] [data-capture-preview-overlay] .bao-toolbar-label,
[data-page="capture"] [data-capture-action-bar] .bao-toolbar-label,
[data-editor-toolbar]:not([data-toolbar-density="labeled"]) .bao-toolbar-label,
[data-editor-toolbar]:not([data-toolbar-density="labeled"]) .bao-toolbar-action .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
  }@media (hover: none),
(pointer: coarse) {[data-page="capture"] [data-capture-preview-overlay] .bao-toolbar-label,
[data-page="capture"] [data-capture-action-bar] .bao-toolbar-label:not(.sr-only),
[data-editor-toolbar][data-toolbar-density="labeled"] .bao-toolbar-label,
[data-editor-toolbar][data-toolbar-density="labeled"] .bao-toolbar-action .bao-toolbar-label {
      position: static;
      width: auto;
      height: auto;
      max-width: 6rem;
      margin: 0;
      margin-inline-start: var(--space-micro, 0.25rem);
      padding: 0;
      overflow: hidden;
      clip: auto;
      white-space: nowrap;
      text-overflow: ellipsis;
      font-size: var(--text-xs, 0.75rem);
      opacity: 1;
    }
  }
  /* Editor: hide search field on very narrow canvas; filters + favorites remain. */
  @media (max-width: 40rem) {
    [data-editor-toolbar-controls] [data-primitive="SearchField"] {
      display: none;
    }
  }
  /* Annotate workbench: canvas column owns height;inspector scrolls alone. */
  [data-page-type="media"] [data-editor-chrome],
[data-page="capture"] [data-editor-chrome] {
    display: flex;
    min-height: 0;
    flex: 1 1 0%;
    flex-direction: column;
    overflow: hidden;
  }/* The session wrapper receives a generated section payload. It must join
     the bounded flex chain or its canvas collapses when secondary chrome is
     intentionally absent. */
  [data-page-type="media"] [data-session-panel] > section,
[data-page="capture"] [data-session-panel] > section {
    display: flex;
    min-height: 0;
    max-height: 100%;
    flex: 1 1 0%;
    flex-direction: column;
    overflow: hidden;
  }[data-page-type="media"] .bao-editor-workbench,
[data-page="capture"] .bao-editor-workbench {
    min-height: 0;
    flex: 1 1 0%;
    overflow-x: hidden;
    overflow-y: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
  }[data-page-type="media"] [data-editor-canvas-overlays],
[data-page="capture"] [data-editor-canvas-overlays] {
    max-height: 100%;
  }


  /* Context chips are interactive buttons — reset native button chrome. */
  button.bao-context-card {
    appearance: none;
    cursor: pointer;
    text-align: start;
    font: inherit;
    color: inherit;
  }
  /* On full Chat page, hide the rail mini-composer (same surface — no dual chat). */
  body:has([data-page="chat"]) #session-rail-live [data-session-section="ai"],
  body:has([data-page="chat"]) #chat-assistant-rail-live,
  body:has([data-page="chat"]) [data-chat-assistant-rail] {
    display: none;
  }
  /* Media tabs never steal vertical space with trailing margin. */
  [data-page-type="media"] [data-session-mode-tabs] {
    margin-bottom: 0;
  }

  .bao-conversation-empty-panel-body {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-inline, 0.5rem);
  }
  .bao-conversation-empty-header {
    display: flex;
    min-width: 0;
    flex-direction: row;
    align-items: flex-start;
    gap: var(--space-inline, 0.5rem);
  }
  .bao-conversation-empty-header > [aria-hidden="true"] {
    flex: 0 0 auto;
  }
  .bao-conversation-empty-header > div:last-child {
    flex: 1 1 auto;
    min-width: 0;
  }
  .bao-composer-shell {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: 0;
  }
  [data-page-type="conversation"] > [data-state-content],
  .bao-conversation-shell {
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0;
    height: 100%;
  }
  #app-main:has([data-page-type="conversation"]) > [data-page] > [data-state-content]:not([hidden]),
  #app-main:has([data-page-type="media"]) > [data-page] > [data-state-content]:not([hidden]) {
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
  }
  .bao-conversation-messages {
    position: relative;
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
    overflow: hidden;
  }
  .bao-conversation-thread {
    min-height: 0;
    flex: 1 1 auto;
  }
  /*
   * Empty conversation: surface starters in the vertical middle of the message
   * region so the workbench is not a hollow top strip over dead white space.
   */
  .bao-conversation-empty {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
    gap: var(--space-inline, 0.5rem);
    padding-block: var(--space-section, 0.75rem);
  }
  .bao-conversation-empty-panel-body {
    max-width: min(100%, 40rem);
  }
  /*
   * Phone empty-state densify: glyph + title + starters own the scan path.
   * Long body copy is secondary (2-line clamp); full text stays for AT via
   * title/aria on the heading cluster (Apple empty-state density).
   */
  @media (max-width: 47.99rem) {
    .bao-conversation-empty [class*="emptyStateBody"],
    .bao-conversation-empty p.text-sm.leading-snug {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
    }
    .bao-conversation-empty-panel-body {
      padding: var(--space-inline, 0.5rem);
      gap: var(--space-micro, 0.25rem);
    }
  }
  .bao-conversation-empty [class*="conversationEmptyActions"],
  .bao-conversation-empty .flex.w-full.flex-wrap {
    width: 100%;
  }
  /*
   * Dock clearance has ONE owner: #app-main. This footer is in flow inside it
   * (flex: 0 0 auto, no positioning), so the container's bottom padding already
   * lifts it above the fixed dock. Reserving --dock-height here counted the same
   * band twice — measured at 390x844, #app-main reserved 64px and the footer
   * reserved another 64px, leaving 64px empty below the composer inside a 188px
   * box. The safe-area floor is a different concern (home indicator) and stays,
   * now at every width rather than only above 80rem.
   */
  .bao-conversation-footer {
    min-width: 0;
    flex: 0 0 auto;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .bao-conversation-gutter {
    padding-inline-start: max(var(--page-gutter-inline-compact), env(safe-area-inset-left, 0px));
    padding-inline-end: max(var(--page-gutter-inline-compact), env(safe-area-inset-right, 0px));
  }
  @media (min-width: 48rem) {
    .bao-conversation-gutter {
      padding-inline-start: max(var(--page-gutter-inline), env(safe-area-inset-left, 0px));
      padding-inline-end: max(var(--page-gutter-inline), env(safe-area-inset-right, 0px));
    }
  }
  /*
   * Conversation dock + composer: solid form surface (design.md §5.5 / §1.5).
   * Glass stays on shell chrome only — not on dense input shells.
   */
  footer.bao-conversation-dock,
  .bao-conversation-footer.bao-conversation-dock,
  .bao-composer-dock-safe {
    position: sticky;
    bottom: 0;
    z-index: var(--z-sticky, 1100);
    background: var(--bg-panel, var(--color-base-100));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: color-mix(in oklab, var(--border-base, var(--color-base-300)) 70%, transparent);
    box-shadow: 0 -1px 0 color-mix(in oklab, var(--border-base) 55%, transparent);
  }
  /* Tokenized dock fade (design.md §3) — no raw opacity-40 / gradient utilities. */
  .bao-composer-dock-fade {
    background: linear-gradient(
      to top,
      color-mix(in oklab, var(--color-base-100) 40%, transparent),
      transparent
    );
  }
  /* Context strip: solid content plane (design.md §1.1 —
     no glass spray on dense operational chips; dock is also solid form chrome). */
  .bao-conversation-context-strip {
    box-sizing: border-box;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    flex: 0 0 auto;
    position: relative;
    top: auto;
    z-index: auto;
    overflow-x: clip;
    background: var(--bg-panel, var(--color-base-100));
    /* S63: liquid-blur token present; 0px keeps solid content plane (no spray). */
    backdrop-filter: blur(var(--liquid-blur-none, 0px));
    -webkit-backdrop-filter: blur(var(--liquid-blur-none, 0px));
    scrollbar-color: var(--glass-scrollbar-thumb, color-mix(in oklab, var(--color-base-content) 40%, transparent)) var(--glass-scrollbar-track, transparent);
  }
  /* Always a single horizontal rail — never a multi-row card wall. */
  .bao-chat-context-grid {
    display: flex;
    box-sizing: border-box;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    gap: var(--space-micro);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }
  .bao-chat-context-grid .bao-context-card,
  .bao-chat-context-grid [data-chat-context-card] {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    flex: 0 0 auto;
    min-height: var(--touch-target-min, 2.75rem);
    max-width: min(11rem, 58vw);
    transform: none;
    animation: none;
  }
  /*
   * Context-more dropdown open-state unclip/lift lives in
   * tokens-layout-dock-densify-b (same cascade layer as the rail overflow
   * rule it overrides) — keep this file free of a losing duplicate.
   */
  /*
   * Context strip remounts on capture-dx:context-changed. Never re-run
   * entrance or transform motion — WebView 101 paints continuous vibration.
   */
  [data-chat-context],
  [data-chat-context].htmx-added,
  [data-chat-context] .htmx-added,
  [data-chat-context] .bao-context-card,
  [data-capture-context-strip],
  [data-capture-context-strip].htmx-added,
  [data-capture-context-strip] .htmx-added {
    animation: none;
    transform: none;
  }
  .bao-chat-context-grid [data-chat-context-more] {
    flex: 0 0 auto;
  }
  [data-chat-composer] .bao-toolbar-action .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  [data-chat-composer] [data-chat-model-fieldset] {
    min-width: 0;
    max-width: min(14rem, 48vw);
  }
  /*
   * Phone densify: value-first chips (Apple status pill). Mono type labels
   * hide; aria-label on the button still names the field.
   */
  @media (max-width: 47.99rem) {
    /*
     * Chips only. The overflow menu is a DOM descendant of this grid even
     * though its popover paints in the top layer, so every rule here reached
     * the menu rows too: the kind label was clipped to 1px and each row was
     * capped at 9.5rem inside a panel with room for full-width rows. A chip can
     * drop its label to aria-label because the strip around it supplies the
     * context; a menu row is read alone and must name itself.
     */
    .bao-chat-context-grid .bao-context-card:not([role="menuitem"]) > span:first-child,
    .bao-chat-context-grid [data-chat-context-card]:not([role="menuitem"]) > span:first-child {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    .bao-chat-context-grid .bao-context-card:not([role="menuitem"]),
    .bao-chat-context-grid [data-chat-context-card]:not([role="menuitem"]) {
      position: relative;
      max-width: min(9.5rem, 48vw);
      padding-inline: var(--space-inline, 0.5rem);
    }
    .bao-chat-context-grid .bao-context-card > span:last-child,
    .bao-chat-context-grid [data-chat-context-card] > span:last-child {
      font-size: var(--text-xs, 0.75rem);
    }
    /* Starter prompts: one compact wrap row, not a tall button column. */
    [data-chat-empty] fieldset {
      flex-direction: row;
      flex-wrap: wrap;
    }
    [data-chat-empty] fieldset > .btn {
      flex: 1 1 auto;
      basis: min(100%, 9.5rem);
      max-width: 100%;
    }
    /*
     * Composer owns model pick (data-chat-model-select). Hide the context-strip
     * model chip on phone so operators are not shown two truncated model
     * affordances (context card + select) in one viewport.
     */
    [data-page="chat"] [data-chat-context-card="model"] {
      display: none;
    }
  }
  /* Empty-state invite: solid content panel (never glass-on-content).
     Static, in-flow body copy = content, not chrome. Was a liquid tint +
     backdrop-filter painting at 0.84 alpha, contradicting this very line;
     now mirrors .bao-composer-shell below (legibility over transparency). */
  .bao-conversation-empty .bao-conversation-empty-panel-body {
    border: 1px solid color-mix(in oklab, var(--border-base, var(--color-base-300)) 70%, transparent);
    border-radius: var(--radius-box, 1rem);
    background: var(--bg-panel, var(--color-base-100));
    box-shadow:
      0 1px 0 color-mix(in oklab, var(--color-base-content) 5%, transparent) inset,
      0 2px 8px color-mix(in oklab, var(--color-base-content) 6%, transparent);
  }
  /* Composer shell: solid interactive panel (legibility over transparency). */
  .bao-composer-shell {
    border: 1px solid color-mix(in oklab, var(--border-base, var(--color-base-300)) 70%, transparent);
    border-radius: var(--radius-box, 1rem);
    background: var(--bg-panel, var(--color-base-100));
    box-shadow:
      0 1px 0 color-mix(in oklab, var(--color-base-content) 5%, transparent) inset,
      0 2px 8px color-mix(in oklab, var(--color-base-content) 6%, transparent);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-chat-context-grid .bao-context-card {
    min-width: 0;
  }

  /* Phone: composer toolbar scrolls; model select flexes with container. */
  @media (max-width: 47.99rem) {
    [data-chat-composer] [data-chat-composer-form] > .flex.items-center.gap-space-inline:first-child,
    [data-chat-composer] .bao-composer-shell > .border-b {
      display: flex;
      overflow-x: auto;
      overscroll-behavior-x: contain;
      -webkit-overflow-scrolling: touch;
      flex-wrap: nowrap;
      touch-action: pan-x;
      scroll-snap-type: x proximity;
      scrollbar-width: none;
      /* Fade trailing edge so clipped tools read as scrollable (HIG). */
      mask-image: linear-gradient(
        to right,
        var(--black) 0%,
        var(--black) calc(100% - 1.25rem),
        transparent 100%
      );
      -webkit-mask-image: linear-gradient(
        to right,
        var(--black) 0%,
        var(--black) calc(100% - 1.25rem),
        transparent 100%
      );
    }
    [data-chat-composer] [data-chat-composer-form] > .flex.items-center.gap-space-inline:first-child::-webkit-scrollbar,
    [data-chat-composer] .bao-composer-shell > .border-b::-webkit-scrollbar {
      display: none;
    }
    /* Each tool keeps intrinsic size — never squash into a clipped flex row. */
    [data-chat-composer] [data-chat-composer-form] > .flex.items-center.gap-space-inline:first-child > *,
    [data-chat-composer] .bao-composer-shell > .border-b > * {
      flex: 0 0 auto;
      scroll-snap-align: start;
    }
    [data-chat-composer] [data-chat-model-fieldset] {
      max-width: min(11rem, 40vw);
      min-width: 6.5rem;
    }
    [data-chat-composer] [data-chat-model-select] {
      max-width: 100%;
    }
  }

  /*
   * Collection pages may embed a shared context-strip (specimens).
   * Never inherit conversation track/gutter chrome on data collections.
   */
  [data-page-type="collection"] [data-chat-context].bao-conversation-context-strip,
  [data-page-type="collection"] [data-chat-context].bao-chat-context-grid {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
    border-block-end: 1px solid color-mix(in oklab, var(--border-base, var(--color-base-300)) 70%, transparent);
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  [data-page-type="collection"] [data-chat-context] .bao-context-card {
    background: var(--bg-panel, var(--color-base-100));
    border-color: color-mix(in oklab, var(--border-base, var(--color-base-300)) 88%, transparent);
  }
  @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .bao-conversation-context-strip,
    footer.bao-conversation-dock,
    .bao-conversation-footer.bao-conversation-dock,
    .bao-composer-shell,
    .bao-conversation-empty .bao-conversation-empty-panel-body {
      background-color: var(--color-base-100, var(--bg-panel));
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
  }

  @media (prefers-reduced-transparency: reduce) {
    .bao-conversation-context-strip,
    footer.bao-conversation-dock,
    .bao-conversation-footer.bao-conversation-dock,
    .bao-composer-shell,
    .bao-conversation-empty .bao-conversation-empty-panel-body {
      background-color: var(--color-base-100, var(--bg-panel));
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
  }

  .bao-conversation-composer,
  .bao-conversation-empty,
  .bao-context-card {
    min-width: 0;
  }
  /*
   * Happydumpling agent chat empty: top-docked invite (not mid-void center).
   * Messages region still fills remaining height so the composer stays docked.
   */
  .bao-conversation-messages:has(.bao-conversation-empty:not([hidden])) {
    flex: 1 1 auto;
    min-height: 0;
    justify-content: flex-start;
    overflow-y: auto;
  }
  .bao-conversation-messages:has(.bao-conversation-empty:not([hidden])) .bao-conversation-thread {
    /* Do not use visibility:hidden + min-height:100% — WebView 101 reflows and shakes. */
    display: none;
  }
  .bao-conversation-empty:not([hidden]) {
    /* In-flow flex host — absolute overlay made starters feel static/dead. */
    position: relative;
    inset: auto;
    display: flex;
    width: 100%;
    max-width: min(48rem, 100%);
    margin-inline: auto;
    min-height: 0;
    flex: 1 1 auto;
    align-items: stretch;
    justify-content: flex-start;
    box-sizing: border-box;
    z-index: auto;
  }
  /*
   * Beat UA/daisy flex utilities via specificity: class+attr beats
   * single-class .flex / .hidden when empty host is marked [hidden].
   * Flex host only applies via :not([hidden]) above — dual-gated hide.
   */
  [data-page="dashboard"] [data-dashboard-widget]:not([data-widget="recent-cases"]) {
    min-height: 0;
  }
  [data-page="dashboard"] [data-dashboard-widget][data-widget="recent-cases"],
  [data-page="dashboard"] [data-dashboard-widget][data-widget-id="recent-cases"] {
    min-width: 0;
  }
  [data-page="dashboard"] [data-dashboard-widget][data-widget="recent-cases"] .bao-panel-scroll,
  [data-page="dashboard"] [data-dashboard-widget][data-widget-id="recent-cases"] .bao-data-panel,
  [data-page="dashboard"] [data-dashboard-widget][data-widget-id="recent-cases"] .bao-panel-scroll {
    max-height: min(22rem, 42vh);
  }
  /* Form/detail pages: same fluid gutters as collection (HD content track).
   * block-end is dock SSOT — never use padding-block shorthand here. */
  #app-main:has([data-page-type="form"]),
  #app-main:has([data-page-type="detail"]),
  #app-main:has([data-page-type="ops"]) {
    padding-block-start: var(--page-gutter-block);
    padding-inline: var(--page-gutter-inline);
  }
  @media (max-width: 48rem) {
    #app-main:has([data-page-type="form"]),
    #app-main:has([data-page-type="detail"]),
    #app-main:has([data-page-type="ops"]) {
      padding-inline: var(--page-gutter-inline-compact);
      padding-inline-start: max(var(--page-gutter-inline-compact), env(safe-area-inset-left, 0px));
      padding-inline-end: max(var(--page-gutter-inline-compact), env(safe-area-inset-right, 0px));
    }
  }
  /* Page headers: full-bleed actions row on narrow screens (SSOT + fluid). */
  @media (max-width: 40rem) {
    [data-primitive="PageHeader"] {
      align-items: stretch;
    }
    [data-primitive="PageHeader"] [data-page-header-actions],
    [data-primitive="PageHeader"] .card-actions {
      width: 100%;
      justify-content: flex-start;
    }
  }
  [data-page-type="form"] > [data-state-content],
  [data-page-type="detail"] > [data-state-content],
  [data-page-type="ops"] > [data-state-content] {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: var(--page-section-gap);
  }
  /* Collection primary table host — solid operational surface (tokens only). */
  [data-page-type="collection"] [data-case-list],
  [data-page-type="collection"] [data-table-host],
  [data-page="cases"] [data-cases-workbench],
  .bao-table-host {
    min-width: 0;
  }


  .bao-conversation-messages > .bao-conversation-empty[hidden],
  .bao-conversation-messages > [data-chat-empty][hidden],
  .bao-conversation-empty[hidden],
  [data-chat-empty][hidden] {
    display: none;
  }
  .bao-conversation-empty-panel-body {
    width: 100%;
    min-width: 0;
  }
  [data-chat-empty] .bao-toolbar-label {
    display: inline;
    white-space: normal;
  }
  /*
   * Full /chat page owns the conversation surface. Hide the rail AI section
   * so operators never face dual composers (page + session rail) — single AI
   * SSOT (enterprise conversation contract).
   */
  body:has([data-page="chat"])
    #session-rail
    [data-session-section="ai"],
  body:has([data-page="chat"])
    #session-rail-sheet
    [data-session-section="ai"] {
    display: none;
  }



  [data-page-type="collection"],
  [data-page-type="detail"] {
    display: flex;
    min-width: 0;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-section);
  }
  [data-page-type="collection"] > [data-state-content],
  [data-page-type="detail"] > [data-state-content] {
    flex: 1 1 auto;
  }
  /* Collection pages: last interactive row clears fixed mobile dock. */
  @media (max-width: 47.99rem) {
    [data-page-type="collection"] > [data-state-content] {
      padding-block-end: calc(var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
    }
  }

  @media (max-width: 47.99rem) {
    [data-page-type="collection"] .overflow-y-auto,
    [data-page-type="collection"] [data-scroll-region],
    [data-page-type="collection"] .bao-panel-scroll {
      scroll-padding-bottom: calc(var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
      padding-block-end: calc(var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
    }
  }
  
  
  .bao-table-empty-panel {
    max-height: 12rem;
  }
  [data-page-type="media"] [data-capture-device-toolbar],
  [data-page-type="media"] [data-capture-command-chrome] .bao-capture-device-grid {
    gap: var(--space-micro);
    align-items: stretch;
  }
  @media (min-width: 40rem) {
    [data-page-type="media"] [data-capture-command-chrome] .bao-capture-device-grid {
      grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    }
  }
  [data-page-type="media"] [data-capture-command-chrome] {
    gap: var(--space-micro);
    padding-inline: var(--space-inline);
  }
  [data-page-type="media"] [data-capture-command-chrome] fieldset,
  [data-page-type="media"] [data-capture-command-chrome] [data-capture-control] {
    min-width: 0;
    padding: var(--space-micro) var(--space-inline);
  }
  /*
   * Background only. Layout (display/flex-direction/align/justify/gap) and —
   * critically — VISIBILITY belong to tokens-layout-capture-preview.ts, which
   * hides this placeholder while [data-capture-case-ready="false"] so the
   * case-required overlay can own the plane. Re-declaring display:flex here
   * gave that one question a second owner and defeated the conditional hide:
   * on /capture at 375px the camera-empty and case-required overlays painted
   * on top of each other ("Live Preview" over "Active case", colliding CTAs).
   * That file's own comment already said not to force display here.
   */
  .bao-preview-region > [data-camera-preview-placeholder] {
    background:
      radial-gradient(circle at 50% 40%, color-mix(in oklab, var(--color-base-content) 4%, transparent), transparent 55%);
  }
  [data-page-type="form"] [data-settings-tabs] {
    max-width: var(--max-w-page-wide, 72rem);
    width: 100%;
  }
  [data-page-type="form"] [data-settings-panel] {
    max-width: none;
    width: 100%;
    min-width: 0;
  }


  @media (max-width: 47.99rem) {
    [data-page-type="collection"] {
      padding-block-end: calc(var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
    }
  }

  @media (max-width: 47.99rem) {
    [data-page-type="collection"] input,
    [data-page-type="collection"] .btn,
    [data-page-type="collection"] button {
      scroll-margin-block-end: calc(var(--touch-target-min, 2.75rem));
    }
  }


  /* Physical end-spacer so last controls can scroll fully above fixed dock. */
  @media (max-width: 47.99rem) {
    [data-page-type="collection"] > [data-state-content]::after {
      content: "";
      display: block;
      flex-shrink: 0;
      width: 100%;
      height: calc(var(--dock-height, 4rem) + var(--dock-safe-area-offset, 0px) + var(--touch-target-min, 2.75rem));
      min-height: calc(var(--dock-height, 4rem) + var(--touch-target-min, 2.75rem));
      pointer-events: none;
    }
  }
/*
   * Collection form grids (licensing,
models,
…): stack half/third cards on
   * phone — page-type SSoT,
never per-page licensing/models forks.
   */
  /*
   * Phone stack for collection + form page types (licensing,
settings
   * companion forms). One rule set — never fork per page id.
   */
  @media (max-width: 42rem) {[data-page-type="collection"] .bao-grid-reactive-panels,
[data-page-type="form"] .bao-grid-reactive-panels,
[data-page-type="ops"] .bao-grid-reactive-panels,
[data-page-type="detail"] .bao-grid-reactive-panels,
[data-page-type="detail"] [data-dashboard-grid] {
      grid-template-columns: minmax(0, 1fr);
    }[data-page-type="collection"] .bao-grid-panel-half,
[data-page-type="collection"] .bao-grid-panel-third,
[data-page-type="collection"] .bao-grid-panel-quarter,
[data-page-type="collection"] .bao-grid-panel-narrow,
[data-page-type="collection"] .bao-grid-panel-wide,
[data-page-type="form"] .bao-grid-panel-half,
[data-page-type="form"] .bao-grid-panel-third,
[data-page-type="form"] .bao-grid-panel-quarter,
[data-page-type="form"] .bao-grid-panel-narrow,
[data-page-type="form"] .bao-grid-panel-wide,
[data-page-type="ops"] .bao-grid-panel-half,
[data-page-type="ops"] .bao-grid-panel-third,
[data-page-type="ops"] .bao-grid-panel-quarter,
[data-page-type="detail"] .bao-grid-panel-half,
[data-page-type="detail"] .bao-grid-panel-third,
[data-page-type="detail"] .bao-grid-panel-quarter,
[data-page-type="detail"] .bao-grid-panel-narrow,
[data-page-type="detail"] .bao-grid-panel-wide,
[data-page-type="detail"] .bao-grid-panel-full,
[data-page-type="detail"] [data-dashboard-grid] > [data-dashboard-widget] {
      grid-column: 1 / -1;
      max-width: none;
      min-width: 0;
    }[data-page-type="collection"] .card-body,
[data-page-type="collection"] form,
[data-page-type="form"] .card-body,
[data-page-type="form"] form,
[data-page-type="ops"] .card-body,
[data-page-type="ops"] form,
[data-page-type="detail"] .card-body,
[data-page-type="detail"] form {
      min-width: 0;
      width: 100%;
    }[data-page-type="collection"] .input,
[data-page-type="collection"] .select,
[data-page-type="collection"] .btn:not(.btn-square),
[data-page-type="form"] .input,
[data-page-type="form"] .select,
[data-page-type="form"] .btn:not(.btn-square),
[data-page-type="ops"] .input,
[data-page-type="ops"] .select,
[data-page-type="ops"] .btn:not(.btn-square),
[data-page-type="detail"] .input,
[data-page-type="detail"] .select,
[data-page-type="detail"] .btn:not(.btn-square) {
      min-height: var(--touch-target-min, 2.75rem);
    }
    /* Primary form submit full-width;secondary icon chrome stays square. */
    [data-page-type="collection"] form .btn.btn-primary,
[data-page-type="form"] form .btn.btn-primary,
[data-page-type="ops"] form .btn.btn-primary,
[data-page-type="detail"] form .btn.btn-primary {
      width: 100%;
      justify-content: center;
    }[data-page-type="collection"] form .btn.btn-square,
[data-page-type="form"] form .btn.btn-square,
[data-page-type="detail"] form .btn.btn-square {
      width: var(--touch-target-min, 2.75rem);
      max-width: var(--touch-target-min, 2.75rem);
    }/* Card bodies: no horizontal scroll from long mono paths / table shells.
     * Use :is() so we never emit bare bao-data-panel multi-selectors that
     * the solid-content-plane gate mistakes for glass co-tagging. */
    [data-page-type="collection"] :is(.card, .bao-data-panel),
[data-page-type="detail"] :is(.card, .bao-data-panel) {
      overflow-x: clip;
      max-width: 100%;
    }/* Form fieldsets: full-bleed rows on phone (settings + licensing). */
    [data-page-type="form"] .bao-settings-group.fieldset,
[data-page-type="collection"] .bao-settings-group.fieldset,
[data-page-type="form"] fieldset.bao-settings-group,
[data-page-type="collection"] fieldset.bao-settings-group {
      width: 100%;
      max-width: none;
    }
  }/* Dashboard widgets: equal card rhythm,
less empty whitespace. */
  [data-page-type="home"] [data-dashboard-grid] {
    align-items: stretch;
    gap: var(--space-inline, 0.5rem);
  }
  [data-page-type="home"] [data-dashboard-widget] {
    min-height: 6rem;
    height: 100%;
  }/* Phone 2-up: tighter gap,
still fluid tracks (no fixed column counts). */
  @media (max-width: 39.99rem) {
    [data-page-type="home"] [data-dashboard-grid] {
      gap: var(--space-micro, 0.25rem);
    }
    [data-page-type="home"] [data-dashboard-widget] {
      min-height: 5.25rem;
    }
  }/* Dashboard embeds one dense case list inside one widget surface. Rows are
     separators,
never a second card layer. */
  [data-page-type="home"] [data-case-list] tbody {
    gap: 0;
  }
  [data-page-type="home"] [data-case-list] tbody > tr {
    border: 0;
    border-block-end: 1px solid var(--border-base);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    padding: var(--space-inline) 0;
  }
  [data-page-type="home"] [data-case-list] tbody > tr:last-child {
    border-block-end: 0;
  }

  /* Page header densify — phone-first title stack (happydumpling topbar rhythm). */
  [data-primitive="PageHeader"] {
    gap: var(--space-inline);
  }
  [data-primitive="PageHeader"] h1 {
    font-size: var(--text-xl, 1.25rem);
    line-height: 1.2;
  }
  [data-primitive="PageHeader"] p {
    font-size: var(--text-xs, 0.75rem);
    line-height: 1.35;
    color: var(--text-secondary);
  }
  /*
   * Page type answered the viewport and then stopped: the title held
   * --text-xl from 48rem to 4K and the lead paragraph held --text-xs at every
   * width above phone, so a 27" display rendered the page title at the same
   * 20px as a tablet and its supporting copy at 12px. These steps are keyed to
   * the CONTAINER, not the viewport, so a header inside a narrow inspector
   * pane keeps the compact size while the same primitive on a wide page grows
   * — the rest of this layout is already container-driven, and type was the
   * one axis still asking the window how big it was.
   *
   * Every step lands on the canonical tokens.bao ramp (xs -> sm, xl -> 2xl),
   * so computed-token-audit still grades each rendered size against a declared
   * stop instead of a free-floating fluid value.
   */
  @container (min-width: 48rem) {
    [data-primitive="PageHeader"] p {
      font-size: var(--text-sm, 0.875rem);
    }
  }
  @container (min-width: 64rem) {
    [data-primitive="PageHeader"] h1 {
      font-size: var(--text-2xl, 1.5rem);
    }
  }
  @media (max-width: 40rem) {
    [data-primitive="PageHeader"] [data-page-header-actions] {
      gap: var(--space-micro);
    }
    [data-primitive="PageHeader"] [data-page-header-actions] .btn {
      /* WCAG/HIG: never shrink header CTAs below 44px on phone. */
      min-height: var(--touch-target-min, 2.75rem);
      height: auto;
      min-width: var(--touch-target-min, 2.75rem);
      padding-inline: var(--space-inline);
      font-size: var(--text-xs, 0.75rem);
    }
  }

  /*
   * Operational solid tiles — dense data surfaces (content plane).
   * Page-type SSoT: collection + home DashboardWidget / bao-data-panel.
   * Glass is chrome-only;:not(.bao-surface-glass-*) is a hard ban so a
   * mis-tagged glass class never inherits content-tile paint.
   */
  [data-page-type="collection"] .bao-data-panel:not(.bao-surface-glass-regular):not(.bao-surface-glass-clear),
[data-page-type="collection"] [data-primitive="FilterBar"],
[data-page-type="home"] [data-dashboard-widget]:not(.bao-surface-glass-regular):not(.bao-surface-glass-clear),
[data-page-type="home"] .card.bao-data-panel:not(.bao-surface-glass-regular):not(.bao-surface-glass-clear) {
    border: 1px solid color-mix(in oklab, var(--border-base) 88%, var(--text-primary));
    border-radius: var(--radius-field, 0.75rem);
    background: color-mix(in oklab, var(--bg-panel) 96%, transparent);
    box-shadow:
      0 1px 0 color-mix(in oklab, var(--bg-panel) 65%, transparent) inset,
      0 1px 2px color-mix(in oklab, var(--text-primary) 6%, transparent);
  }

  /* Collection filter bar densify */
  [data-page-type="collection"] [data-primitive="FilterBar"] {
    padding: var(--space-inline) var(--space-section);
    gap: var(--space-inline);
  }


  /* Case list host — happydumpling solid list density */
  .bao-case-list-host {
    border: 1px solid color-mix(in oklab, var(--border-base) 90%, var(--text-primary));
    border-radius: var(--radius-field, 0.75rem);
    background: color-mix(in oklab, var(--bg-panel) 94%, transparent);
    box-shadow: 0 1px 2px color-mix(in oklab, var(--text-primary) 5%, transparent);
    padding: var(--space-inline);
  }
  .bao-case-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-inline);
    margin: 0;
    padding: 0;
    list-style: none;
  }
  /*
   * Closed <details> must not paint the list. Author `display:flex` on
   * .bao-case-list outranks the UA closed-content hide in Chromium, so the
   * list kept real boxes (and PE:auto cards) while the host stayed summary-
   * height only — case cards stacked over specimen select/add (capture@tablet
   * reachability). Explicit :not([open]) hide + [open] re-show is the fix.
   */
  details[data-case-picker-disclosure]:not([open]) > .bao-case-list,
  details[data-case-picker-disclosure]:not([open]) > :not(summary) {
    display: none;
  }
  details[data-case-picker-disclosure][open] > .bao-case-list {
    display: flex;
    flex-direction: column;
    position: relative;
    z-index: 2;
  }
  /*
   * Specimen create disclosure: author flex on the form host left the submit
   * ("Add specimen") with a real box while closed — visual gate stamped it
   * then timed out on click (/specimens dual-walk).
   */
  details[data-specimen-create-disclosure]:not([open]) > :not(summary) {
    display: none;
  }
  .bao-case-card {
    /* visible: auto+intrinsic collapsed layout while cards painted full size (reachability). */
    content-visibility: auto;
    contain-intrinsic-size: auto 5rem;
    position: relative;
    z-index: 1;
    isolation: isolate;
    scroll-margin-block-end: calc(var(--dock-height, 4rem) + var(--touch-target-min, 2.75rem));
    display: flex;
    flex-direction: column;
    gap: var(--space-micro);
    margin: 0;
    padding: var(--space-section);
    border: 1px solid color-mix(in oklab, var(--border-base) 88%, var(--text-primary));
    border-radius: var(--radius-field, 0.75rem);
    background: var(--bg-panel);
    box-shadow: 0 1px 2px color-mix(in oklab, var(--text-primary) 5%, transparent);
    overflow: hidden;
    min-width: 0;
  }
  @media (max-width: 47.99rem) {
    .bao-case-list-host {
      padding: var(--space-micro);
    }
    .bao-case-list {
      gap: var(--space-micro);
    }
    .bao-case-card {
      padding-block: var(--space-micro);
      padding-inline: var(--space-inline);
      gap: var(--space-micro);
    }
  }
  .bao-case-card {
    transition:
      border-color var(--duration-fast, 120ms) var(--easing-standard),
      box-shadow var(--duration-fast, 120ms) var(--easing-standard),
      transform var(--duration-fast, 120ms) var(--easing-standard);
  }
  @media (hover: hover) and (pointer: fine) {
    .bao-case-card:hover {
      border-color: color-mix(in oklab, var(--color-primary) 40%, var(--border-base));
      box-shadow: 0 2px 8px color-mix(in oklab, var(--text-primary) 8%, transparent);
      transform: translateY(-1px);
    }
  }
  .bao-case-card:active {
    transform: scale(0.99);
  }
  .bao-case-card:focus-within {
    border-color: color-mix(in oklab, var(--color-primary) 40%, var(--border-base));
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-case-card {
      transition: none;
    }
    .bao-case-card:hover,
    .bao-case-card:active {
      transform: none;
    }
  }
  [data-case-option][data-selected="true"] .bao-case-card,
  [data-case-option][aria-selected="true"] .bao-case-card {
    border-color: color-mix(in oklab, var(--color-success) 50%, var(--border-base));
    border-inline-start-color: var(--color-success);
    background: color-mix(in oklab, var(--color-success) 10%, var(--bg-panel));
  }
  .bao-case-card-selected-icon {
    display: none;
  }
  [data-case-option][data-selected="true"] .bao-case-card-selected-icon,
  [data-case-option][aria-selected="true"] .bao-case-card-selected-icon {
    display: inline-block;
  }
  .bao-case-status.badge {
    border-radius: var(--radius-full);
    font-size: var(--text-2xs, 0.6875rem);
    font-weight: 600;
  }
  .bao-case-meta {
    color: var(--text-secondary);
    opacity: 1;
  }
  /* Case detail preview fact rows — one owner for the label/value pair chain. */
  .bao-case-fact-row {
    display: flex;
    min-width: 0;
    align-items: baseline;
    gap: var(--space-micro);
    font-size: var(--text-xs, 0.75rem);
  }
  /* Sentence case labels (Apple HIG) — uppercase burned vertical rhythm. */
  .bao-case-fact-label {
    flex-shrink: 0;
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
    color: color-mix(in oklab, var(--color-base-content) 72%, var(--color-base-100));
  }
  .bao-case-status {
    display: inline-flex;
    align-items: center;
    padding: var(--space-micro) var(--space-inline);
    border-radius: var(--radius-full);
    border: 1px solid color-mix(in oklab, var(--color-info) 40%, var(--border-base));
    background: color-mix(in oklab, var(--color-info) 12%, transparent);
    color: var(--color-info-content);
    font-size: var(--text-xs, 0.75rem);
    font-weight: 500;
    white-space: nowrap;
  }
  /*
   * Capture strip cards only — never bare .bao-context-card.
   * Chat/shell chips share that class for borders but stay row flex;
   * forcing column here fought horizontal rail CSS and reflow-thrashed
   * (WebView 101 chip vibration) on every HTMX remount.
   */
  [data-capture-context],
  [data-capture-context-strip] > .bao-context-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-micro);
    min-height: 3.25rem;
    padding: var(--space-inline) var(--space-section);
    border: 1px solid color-mix(in oklab, var(--border-base) 88%, var(--text-primary));
    border-inline-start: 3px solid var(--color-primary);
    border-radius: var(--radius-field, 0.75rem);
    background: var(--bg-panel);
    text-align: start;
    box-shadow: 0 1px 2px color-mix(in oklab, var(--text-primary) 4%, transparent);
  }
  #case-list-section,
  [data-case-list] {
    display: block;
    min-height: 8rem;
    width: 100%;
    visibility: visible;
    opacity: 1;
  }

  /*
   * Active clinical case in the worklist — shell context match (Apple list
   * selection). Primary edge + soft fill; never a second chrome plate.
   */
  [data-case-row-primary][data-case-active="true"],
  tr[data-case-row-primary][data-case-active="true"] {
    border-inline-start: 3px solid var(--color-primary);
    background: color-mix(in oklab, var(--color-primary) 8%, var(--bg-panel));
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-primary) 22%, transparent);
  }
  @media (hover: hover) and (pointer: fine) {
    [data-case-row-primary][data-case-active="true"]:hover {
      background: color-mix(in oklab, var(--color-primary) 12%, var(--bg-panel));
    }
  }

  /* Fluid collection grid — auto-fit density */
  .bao-grid-reactive-collection {
    display: grid;
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(100%, var(--grid-track-min, 18rem)), 1fr)
    );
    gap: var(--space-inline);
    align-items: stretch;
    width: 100%;
    min-width: 0;
  }

  /* Empty panels — compact dashed host, never sparse white voids */
  .bao-empty-panel {
    min-height: 4.5rem;
    padding: var(--space-section);
  }

  /* Ambient page field so solid tiles read against chrome (not pure void) */
  #app-main {
    background:
      radial-gradient(
        ellipse 90% 40% at 50% -8%,
        color-mix(in oklab, var(--color-primary) 6%, transparent),
        transparent 55%
      ),
      var(--color-base-100);
  }

  /*
   * Collection densify: workbench owns chrome; avoid stacked section-group voids.
   */
  [data-page-type="collection"] [data-section-group] {
    gap: var(--space-inline);
  }

  /* Filter bar fields: controls align on one baseline (labels are sr-only). */


  /*
   * Conversation mobile: value-only chips (labels via aria-label + tooltip).
   * Research: ≤3 primary chips on phone; secondary in overflow (already SSOT).
   */
  @media (max-width: 40rem) {
    /*
     * Chips only — NOT the overflow menu rows, which carry role="menuitem".
     * Both rules below used to match every context card regardless of where it
     * rendered, so an overflow row showed its value with the kind clipped to
     * 1px: a menu of "Not selected", "0", "default-routing" with nothing saying
     * which was patient, annotations, or policy. A chip can drop its label to
     * a tooltip because it sits in a labelled strip; a menu row is read on its
     * own and cannot. The width cap was equally wrong there, pinning full-width
     * rows to 9rem inside a panel that had room for all of them.
     */
    [data-page="chat"] [data-chat-context-card]:not([role="menuitem"]) > span:first-child {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    [data-page="chat"] [data-chat-context-card]:not([role="menuitem"]) {
      position: relative;
      max-width: min(42vw, 9rem);
    }
    [data-page="chat"] [data-chat-composer-footer] [data-chat-model-fieldset] legend {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
    }
  }

  /* New Case dialog steps: dense on phone; box height tracks visual viewport. */
  @media (max-width: 40rem) {
    [data-case-create-dialog] .bao-dialog-step-label {
      font-size: var(--text-2xs, 0.6875rem);
      max-width: 4.5rem;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    [data-case-create-dialog] .modal-box,
    [data-case-create-dialog] .bao-modal-box-shell {
      max-height: min(92dvh, var(--vv-height, 100%));
    }
    [data-case-create-dialog] .bao-modal-body-scroll {
      max-height: min(
        var(--modal-body-max-height),
        calc(var(--vv-height, 100dvh) * 0.5)
      );
    }
  }

  /*
   * Dashboard recent-cases embed: widget owns card chrome. Strip nested
   * DataSection padding and flatten phone row "mini-cards" so we never get
   * card-in-card nesting (Hick/Apple density).
   */
  [data-dashboard-widget] [data-case-list] {
    padding: 0;
    gap: var(--space-micro);
  }
  @media (max-width: 47.99rem) {
    [data-dashboard-widget] [data-case-list] tbody tr {
      border-radius: 0;
      border-inline: 0;
      border-block-start: 0;
      border-block-end: 1px solid color-mix(in oklab, var(--color-base-300) 70%, transparent);
      background: transparent;
      box-shadow: none;
      padding-inline: 0;
      padding-block: var(--space-micro);
    }
  }

  /*
   * Case detail split pane (imp-t03): master-detail on the tablet band
   * (48–64rem) — list left, detail pane right, no full-page navigation.
   * The pane shell is .bao-generated (partials/case-detail-pane.bao) and
   * injected beside the list inside the workbench; outside the band it is
   * display:none so band/orientation changes preserve its content.
   */
  [data-case-detail-pane] {
    display: none;
  }
  @media (min-width: 48rem) and (max-width: 63.99rem) {
    [data-cases-workbench]:has(> [data-case-detail-pane]) {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(20rem, 24rem);
      gap: var(--space-inline);
      align-items: start;
    }
    [data-cases-workbench]:has(> [data-case-detail-pane]) > [data-case-filters] {
      grid-column: 1 / -1;
    }
    [data-case-detail-pane] {
      display: flex;
      flex-direction: column;
      gap: var(--space-inline);
      min-width: 0;
      padding: var(--space-inline);
      border-inline-start: 1px solid color-mix(in oklab, var(--border-base) 80%, transparent);
      outline: none;
    }
    [data-case-detail-pane]:focus-visible {
      outline: 2px solid var(--color-primary);
      outline-offset: -2px;
    }
  }
  .bao-case-detail-pane-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-inline);
  }
  .bao-case-detail-pane-title {
    margin: 0;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    color: var(--text-primary);
  }
  .bao-case-detail-pane-description {
    margin: 0;
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-secondary);
  }
  .bao-case-detail-pane-body {
    min-height: 8rem;
    min-width: 0;
  }
  .bao-case-detail-pane-error {
    padding: var(--space-inline);
    border-radius: var(--radius-field, 0.75rem);
    border: 1px solid color-mix(in oklab, var(--color-error) 40%, var(--border-base));
    color: var(--color-error);
    font-size: var(--text-xs, 0.75rem);
  }
  .bao-case-detail-pane-error-copy {
    margin: 0;
  }
  .bao-case-detail-facts {
    display: flex;
    flex-direction: column;
    gap: var(--space-micro);
    margin: 0;
  }
  .bao-case-detail-facts dd {
    margin: 0;
    min-width: 0;
  }
  .bao-case-detail-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-micro);
    margin-block-start: var(--space-inline);
    /* Semantic <fieldset> (biome useSemanticElements): strip UA border,
       padding, and min-content sizing — the class owns all geometry. */
    margin-inline: 0;
    padding: 0;
    border: 0;
    min-inline-size: 0;
  }
/*
   * Global page density SSOT — every pageType inherits Apple-tight chrome:
   * form cards,
tables,
and section stacks share the same spacing scale.
   * No per-page bespoke overrides.
   */
  [data-page] .bao-page-track {
    gap: var(--space-section);
  }[data-page-type="collection"] .bao-page-track,
[data-page-type="form"] .bao-page-track,
[data-page-type="detail"] .bao-page-track {
    gap: var(--space-inline);
  }
  [data-page] [data-primitive="DataSection"] {
    min-width: 0;
  }
  [data-page] .bao-data-panel {
    min-width: 0;
    max-width: 100%;
  }/* Overflow containment: no panel may paint outside the page track */
  [data-page] .bao-page-track,
[data-page] [data-state-content] {
    min-width: 0;
    max-width: 100%;
  }
  /* Secondary soft chrome: ban invisible zero-height ghosts from layout thrash */
  [data-page] .btn.btn-soft.btn-square {
    flex-shrink: 0;
  }/* Phone: page headers densify — subtitle max 2 lines,
actions full-width stack */
  @media (max-width: 40rem) {
    [data-page] [data-primitive="PageHeader"] p {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
    }
    [data-page] [data-page-header-actions] {
      width: 100%;
    }
    [data-page] [data-page-header-actions] > .btn {
      flex: 1 1 auto;
      min-height: var(--touch-target-min, 2.75rem);
    }
  }


  .bao-workbench {
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    min-height: 0;
    overflow-x: clip;
  }
  /* Conversation dock SSOT: tokens-layout-page-shells-conversation.ts owns
     all .bao-conversation-dock rules (solid form surface per design.md §5.5).
     Skeleton module must not duplicate — DRY single owner. */
  @media (prefers-reduced-motion: reduce) {.bao-stagger-children > *,
.bao-session-mode-tab,
.bao-motion-transition-colors {
      transition-property: none; transition-duration: var(--duration-instant, 0ms);
      animation: none;
    }
  }
  /* Conversation message stream — scroll region host. */
  .bao-conversation-stream {
    scrollbar-gutter: stable;
    scroll-padding-block: var(--space-section);
  }
  /* Conversation page header band. */
  .bao-conversation-header {
    min-height: var(--chrome-min-height, 2.25rem);
  }
  .bao-skeleton-header {
    min-height: var(--density-comfortable-row-height);
    border-radius: var(--radius-field);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-toolbar {
    min-height: var(--density-default-row-height);
    border-radius: var(--radius-field);
    background-color: var(--bg-surface-hover);
  }
  /* Floating glass toolbar — geometry-faithful surface for capture/editor top chrome. */
  .bao-skeleton-toolbar-floating {
    min-height: var(--density-comfortable-row-height);
    width: 100%;
    border-radius: var(--radius-field);
    border: 1px solid var(--liquid-border-clear) 50%, transparent));
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-inspector {
    min-height: var(--skeleton-media-min-height);
    border: 1px solid var(--border-base);
    border-radius: var(--radius-box);
    background-color: var(--bg-surface);
  }
  /* Workbench side panel — captures card card-border geometry + card-body padding. */
  .bao-skeleton-side-panel {
    min-height: var(--skeleton-panel-min-height, 7rem);
    width: 100%;
    border-radius: var(--radius-box);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  .bao-skeleton-side-panel-card {
    min-height: var(--skeleton-panel-min-height, 7rem);
    width: 100%;
    border-radius: var(--radius-box);
    border: 1px solid var(--border-base);
    background-color: var(--bg-panel);
    box-shadow: 0 1px 2px color-mix(in oklab, var(--color-base-content) 6%, transparent);
  }
  /* Workbench preview region — matches camera preview box geometry. */
  .bao-skeleton-preview-region {
    aspect-ratio: 16 / 9;
    min-height: var(--skeleton-media-min-height, 12rem);
    width: 100%;
    max-width: 100%;
    border-radius: var(--radius-box);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  /* Workbench preview empty placeholder — matches centered icon+copy stack. */
  .bao-skeleton-preview-empty {
    min-height: var(--skeleton-media-min-height, 12rem);
    width: 100%;
    border-radius: var(--radius-box);
    border: 1px dashed color-mix(in oklab, var(--color-base-300) 70%, transparent);
    background-color: var(--bg-surface);
  }
  /* Workbench canvas region — matches annotation editor canvas column. */
  .bao-skeleton-canvas-region {
    min-height: var(--canvas-region-min-height, clamp(18rem, 42vh, 30rem));
    width: 100%;
    border-radius: var(--radius-box);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  /* Panel card header — matches card-title band above card-body. */
  .bao-skeleton-panel-header {
    min-height: var(--density-comfortable-row-height);
    width: 100%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }.bao-skeleton-message,
.bao-skeleton-message-reply {
    min-height: var(--density-comfortable-row-height);
    width: min(75%, var(--page-standard));
    border-radius: var(--radius-box);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-message-reply {
    margin-inline-start: auto;
    width: min(60%, var(--page-standard));
  }
  /* Assistant chat bubble — mirrors chat-bubble border + radius at chat-start row. */
  .bao-skeleton-message-assistant {
    min-height: var(--density-comfortable-row-height);
    width: min(75%, var(--page-conversation, 48rem));
    border-radius: var(--radius-box);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  /* User chat bubble — mirrors chat-bubble-primary at chat-end row. */
  .bao-skeleton-message-user {
    min-height: var(--density-comfortable-row-height);
    width: min(60%, var(--page-conversation, 48rem));
    border-radius: var(--radius-box);
    background-color: var(--bg-surface-hover);
  }
  /* Conversation context strip — sticky chip band above thread. */
  .bao-skeleton-conversation-context-strip {
    min-height: 3rem;
    width: 100%;
    max-width: var(--page-wide, 96rem);
    margin-inline: auto;
    border-radius: var(--radius-field);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface-hover);
  }
  /* Glass-docked composer — same footprint as final composer footer. */
  .bao-skeleton-conversation-composer-docked {
    min-height: 8rem;
    width: 100%;
    max-width: var(--page-wide, 96rem);
    margin-inline: auto;
    border-radius: var(--radius-box);
    border: 1px solid color-mix(in oklab, var(--color-base-300) 50%, transparent);
    background-color: var(--bg-surface-hover);
  }

  .bao-media-workbench-primary {
    display: flex;
    min-width: 0;
    min-height: 0;
    flex: 999 1 24rem;
    flex-direction: column;
    gap: var(--space-inline);
  }
  .bao-media-workbench-secondary {
    display: flex;
    min-width: 0;
    min-height: 0;
    max-height: 100%;
    /* Cap inspector rail so it never starves the media stage on ultrawide. */
    flex: 0 1 20rem;
    max-width: min(24rem, 100%);
    flex-direction: column;
    gap: var(--space-inline);
    overflow-y: visible;
  }
  .bao-preview-context-panel {
    min-width: 0;
    width: 100%;
  }

  .bao-form-adaptive {
    display: grid;
    min-width: 0;
    gap: var(--space-section);
  }
  .bao-form-grid-two {
    display: grid;
    min-width: 0;
    gap: var(--space-section);
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(100%, var(--grid-form-min, 16rem)), 1fr)
    );
  }
  .bao-choice-grid {
    display: grid;
    min-width: 0;
    gap: var(--space-inline);
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(100%, var(--grid-choice-min, 14rem)), 1fr)
    );
  }
  /*
   * Collection choice densify: compact padding + single column on phone.
   * Security/permission radiogroups (API keys scopes) KEEP helper copy —
   * title-only chips hide least-privilege meaning (enterprise anti-pattern).
   * Non-security choice grids may densify titles only via data-choice-density.
   */
  @media (max-width: 47.99rem) {
    .bao-choice-grid {
      gap: var(--space-micro);
      grid-template-columns: minmax(0, 1fr);
    }.bao-choice-grid > .label,
.bao-choice-grid > .bao-choice-option {
      padding: var(--space-inline, 0.5rem);
      min-height: var(--touch-target-min, 2.75rem);
    }/* Optional dense chips: opt-in only (never default for permission scopes). */
    .bao-choice-grid[data-choice-density="title-only"] > .label > span.grid > span:last-child,
.bao-choice-grid[data-choice-density="title-only"] > .label > span > span.text-xs,
.bao-choice-grid[data-choice-density="title-only"] > .bao-choice-option > span.grid > span:last-child,
.bao-choice-grid[data-choice-density="title-only"] > .bao-choice-option > span > span.text-xs {
      display: none;
    }/* Permission scopes: clamp helper to 2 lines (always visible,
never buried). */
    [data-api-keys-create-form] .bao-choice-grid > .bao-choice-option > span.grid > span.text-xs,
[data-api-keys-create-form] .bao-choice-grid > .bao-choice-option > span > span.text-xs {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
      white-space: normal;
    }
  }/* Collection dual panels (licensing,
ops): full-width stack under tablet. */
  .bao-context-grid-reactive {
    display: grid;
    min-width: 0;
    align-items: stretch;
    gap: var(--space-micro);
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(100%, var(--grid-context-min, 10rem)), 1fr)
    );
  }
  .bao-page-chrome[data-chat-context] {
    display: flex;
    flex-wrap: wrap;
    min-width: 0;
    gap: var(--space-micro);
  }
  .bao-tabs-scroll {
    width: 100%;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--glass-scrollbar-thumb, color-mix(in oklab, var(--color-base-content) 40%, transparent)) var(--glass-scrollbar-track, transparent);
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--space-inline);
    /*
     * No mask here. The clipped-content affordance has ONE owner —
     * .bao-scroll-edge-x (tokens-utilities-scroll-edge), which every compiled
     * .bao-tabs-scroll host also carries. That owner is self-hiding: its cover
     * layers are background-attachment:local, so each cue disappears once its
     * edge is fully revealed and nothing is drawn when there is no overflow.
     *
     * The mask that used to live here was unconditional, so a strip with
     * nothing to scroll still faded its trailing 1.25rem — measured on
     * /capture at 375px: scrollWidth === clientWidth === 357 and the tab strip
     * was still dimmed at the right edge. Two affordances on one element, one
     * of them lying about scrollability.
     */
  }/* Form-type settings (page-type SSoT — never fork only [data-page=settings]). */
  [data-page-type="form"] .bao-tabs-scroll,
[data-page="settings"] .bao-tabs-scroll {
    gap: var(--space-micro);
    padding-block: var(--space-micro);
  }[data-page-type="form"] .bao-settings-tab,
[data-page="settings"] .bao-settings-tab {
    flex: 0 0 auto;
    scroll-snap-align: start;
    padding-inline: var(--toolbar-action-pad-inline);
    min-height: var(--touch-target-min, 2.75rem);
  }
  @media (max-width: 40rem) {/*
     * Phone: one compact,
horizontally scrollable destination rail — for form
     * pages whose tablist is NOT markup-hidden on compact.
     *
     * Deliberately does not declare display. The settings tablist ships the
     * max-sm:hidden utility and its sibling select[data-settings-tab-select]
     * ships sm:hidden,
so the markup contract is "select owns phone,
rail owns
     * >=sm". A display:flex here is specificity (0,
3,
0) and beat the
     * max-sm:hidden utility (0,
1,
0),
so BOTH controls painted at 375px — a
     * duplicate 12-destination navigation costing 96px of first viewport.
     * Without the override,
the daisyUI .tabs display:flex still lays out
     * every rail that is not markup-hidden,
at equal specificity that the
     * utilities layer correctly wins.
     */
    [data-page-type="form"] .tabs.bao-tabs-scroll,
[data-page="settings"] .tabs.bao-tabs-scroll,
[data-page-type="form"] .tabs.tabs-box.bao-tabs-scroll,
[data-page="settings"] .tabs.tabs-box.bao-tabs-scroll {
      flex-direction: row;
      flex-wrap: nowrap;
      align-items: center;
      justify-content: flex-start;
      gap: var(--space-2xs);
      width: 100%;
      max-width: 100%;
      overflow-x: auto;
      overflow-y: hidden;
      scroll-snap-type: x proximity;
      padding: var(--space-micro);
      border-radius: var(--radius-field, 0.75rem);
    }[data-page-type="form"] .tabs.bao-tabs-scroll > .bao-settings-tab,
[data-page="settings"] .tabs.bao-tabs-scroll > .bao-settings-tab,
[data-page-type="form"] .tabs.bao-tabs-scroll > .tab.bao-settings-tab,
[data-page="settings"] .tabs.bao-tabs-scroll > .tab.bao-settings-tab {
      display: inline-flex;
      width: auto;
      max-width: none;
      min-width: max-content;
      flex: 0 0 auto;
      min-height: var(--touch-target-min, 2.75rem);
      height: auto;
      padding-inline: var(--space-section);
      justify-content: center;
      align-items: center;
      gap: var(--toolbar-action-pad-inline);
      border-radius: var(--radius-field, 0.75rem);
      scroll-snap-align: start;
    }[data-page-type="form"] .tabs.bao-tabs-scroll > .bao-settings-tab .bao-toolbar-label,
[data-page="settings"] .tabs.bao-tabs-scroll > .bao-settings-tab .bao-toolbar-label {
      position: static;
      width: auto;
      height: auto;
      max-width: none;
      margin: 0;
      padding: 0;
      overflow: hidden;
      clip: auto;
      white-space: nowrap;
      text-overflow: ellipsis;
      flex: 0 1 auto;
      min-width: max-content;
      text-align: start;
      font-size: var(--text-sm, 0.875rem);
      font-weight: 500;
      letter-spacing: var(--tracking-control, -0.01em);
      color: inherit;
      opacity: 1;
    }
  }/*
   * Settings density: single full-width column. Multi-col auto-fit stranded
   * short groups beside tall ones and split Device Registry into a third rail.
   */
  [data-page-type="form"] .bao-settings-tab-panel > [data-state="success"],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] {
    display: grid;
    min-width: 0;
    width: 100%;
    gap: var(--space-section);
    /* Single dense column — auto-fit multi-col stranded short groups (Capture Defaults)
       beside tall Camera Service and split Device Registry into a third rail. */
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }[data-page-type="form"] .bao-settings-group,
[data-page="settings"] .bao-settings-group {
    max-width: none;
    min-width: 0;
    height: 100%;
  }/* Deep links / registry / contract summaries / robot embeds / appearance span full panel width. */
  [data-page-type="form"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-deep-link],
[data-page-type="form"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-device-registry],
[data-page-type="form"] .bao-settings-tab-panel > [data-state="success"] > [data-ai-policy-summary],
[data-page-type="form"] .bao-settings-tab-panel > [data-state="success"] > [data-robot-control],
[data-page-type="form"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-appearance],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-deep-link],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-device-registry],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] > [data-ai-policy-summary],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] > [data-robot-control],
[data-page="settings"] .bao-settings-tab-panel > [data-state="success"] > [data-settings-appearance] {
    grid-column: 1 / -1;
  }
  .bao-dock-reactive { display: flex; flex-wrap: wrap; }
  .bao-dock-reactive-primary { flex: 999 1 var(--dock-primary-basis, 32rem); min-width: 0; }
  .bao-dock-reactive-side { flex: 1 1 var(--dock-side-basis, 15rem); min-width: 0; }
  .bao-dock-reactive-side-wide { flex: 1 1 var(--dock-side-wide-basis, 18rem); min-width: 0; }
/* Mobile dock CSS emits UNLAYERED from tokens.ts (tokens-layout-dock) —
   never nest it inside @layer utilities or WebView 101 drops the hide. */

  /*
   * Shared reactive panel cells: equal-height rows (align-items: stretch on
   * .bao-grid-reactive-panels) + full-width track fill. design.md / AGENTS:
   * grid macro, consistent density — never ragged card heights in a row.
   * Never allow a panel to paint outside its track (clip overflow).
   */
  .bao-grid-reactive-panels {
    overflow-x: clip;
    max-width: 100%;
    /* 12-track macro (design.md / AGENTS): fluid spans via grid-column. */
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }
  .bao-grid-panel-quarter,
  .bao-grid-panel-third,
  .bao-grid-panel-half,
  .bao-grid-panel-narrow,
  .bao-grid-panel-wide,
  .bao-grid-panel-full {
    min-width: 0;
    max-width: 100%;
    height: 100%;
    min-height: var(--bao-dashboard-panel-min-height, 11rem);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
  }
  .bao-grid-panel-full {
    min-height: var(--bao-dashboard-panel-full-min-height, 8rem);
    grid-column: 1 / -1;
  }
  /* Phone: every panel full-bleed (touch-first; no orphan half beside void). */
  .bao-grid-panel-quarter,
  .bao-grid-panel-third,
  .bao-grid-panel-half,
  .bao-grid-panel-narrow,
  .bao-grid-panel-wide {
    grid-column: 1 / -1;
  }
  .bao-grid-panel-quarter { --bao-panel-basis: var(--grid-panel-half-min); }
  .bao-grid-panel-third,
  .bao-grid-panel-narrow { --bao-panel-basis: var(--grid-panel-half-min); }
  .bao-grid-panel-half,
  .bao-grid-panel-wide { --bao-panel-basis: var(--grid-panel-half-min); }

  /*
   * design.md content-first: max TWO columns on multi-panel workbenches.
   * Every non-full span maps to half (6/12). Third/narrow/quarter never pack
   * into 3-up/4-up rows (span 3/4 left void tracks and nested-card thrash).
   * Wide = primary half; narrow = supporting half; full = 1 / -1 stack.
   */
  @container (min-width: 36rem) {
    .bao-grid-panel-half,
    .bao-grid-panel-wide,
    .bao-grid-panel-third,
    .bao-grid-panel-narrow,
    .bao-grid-panel-quarter {
      grid-column: span 6;
    }
  }
  /*
   * design.md §8.4/§8.6 adaptive feed: compact stat widgets are leaf cards
   * (value + label + one action — no nested tables), so they pack THREE-up
   * on expanded containers instead of stretching full rows of whitespace.
   * Rich widgets (data-widget-density="rich": embedded tables/lists) keep
   * the max-2-col rule above — nested content never packs 3-up.
   */
  @container (min-width: 48rem) {
    .bao-grid-reactive-panels > [data-dashboard-widget][data-widget-density="compact"],
    .bao-grid-reactive-panels > [data-primitive="DashboardWidget"][data-widget-density="compact"],
    .bao-grid-reactive-panels > * > [data-dashboard-widget][data-widget-density="compact"],
    .bao-grid-reactive-panels > * > [data-primitive="DashboardWidget"][data-widget-density="compact"] {
      grid-column: span 4;
    }
  }
  @container (min-width: 80rem) {
    .bao-grid-reactive-panels > [data-dashboard-widget][data-widget-density="compact"],
    .bao-grid-reactive-panels > [data-primitive="DashboardWidget"][data-widget-density="compact"],
    .bao-grid-reactive-panels > * > [data-dashboard-widget][data-widget-density="compact"],
    .bao-grid-reactive-panels > * > [data-primitive="DashboardWidget"][data-widget-density="compact"] {
      grid-column: span 3;
    }
  }
  /*
   * Nested widget shells (section-group wraps cells): fill equal-height tracks.
   * Direct children and one-level nested cards share the same stretch SSOT.
   */
  .bao-grid-reactive-panels > [data-dashboard-widget],
  .bao-grid-reactive-panels > [data-primitive="DashboardWidget"],
  .bao-grid-reactive-panels > .card,
  .bao-grid-reactive-panels > * > [data-dashboard-widget],
  .bao-grid-reactive-panels > * > [data-primitive="DashboardWidget"],
  .bao-grid-reactive-panels > * > .card {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    min-height: var(--bao-dashboard-panel-min-height, 11rem);
    min-width: 0;
  }
  /* design.md content-first: compact strips (journey) must not stretch to the
   * tallest row or claim the 11rem panel floor — primary tables own height. */
  .bao-grid-reactive-panels > [data-dashboard-widget][data-widget-density="compact"],
  .bao-grid-reactive-panels > [data-primitive="DashboardWidget"][data-widget-density="compact"],
  .bao-grid-reactive-panels > * > [data-dashboard-widget][data-widget-density="compact"],
  .bao-grid-reactive-panels > * > [data-primitive="DashboardWidget"][data-widget-density="compact"] {
    height: auto;
    min-height: 0;
    max-height: none;
    align-self: start;
    flex: 0 0 auto;
  }
  .bao-grid-reactive-panels > [data-dashboard-widget] > .card-body,
  .bao-grid-reactive-panels > [data-primitive="DashboardWidget"] > .card-body,
  .bao-grid-reactive-panels > .card > .card-body,
  .bao-grid-reactive-panels > * > [data-dashboard-widget] > .card-body,
  .bao-grid-reactive-panels > * > [data-primitive="DashboardWidget"] > .card-body,
  .bao-grid-reactive-panels > * > .card > .card-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    /* Surface content: clip width only; allow vertical scroll (never bury tables). */
    overflow-x: clip;
    overflow-y: auto;
  }
  .bao-grid-reactive-panels [data-widget-value],
  .bao-grid-reactive-panels .bao-dashboard-panel-value {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    overflow-x: clip;
    overflow-y: auto;
  }
  /* Path / mono / secondary copy never blows the card width (design.md type). */
  .bao-grid-reactive-panels .bao-dashboard-widget-secondary,
  .bao-grid-reactive-panels .bao-dashboard-widget-meta,
  .bao-grid-reactive-panels .bao-dashboard-widget-detail,
  .bao-grid-reactive-panels [data-widget-secondary],
  .bao-grid-reactive-panels [data-widget-meta] {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .bao-grid-reactive-panels .card-title,
  .bao-grid-reactive-panels .bao-dashboard-widget-label,
  .bao-grid-reactive-panels [data-widget-title] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Dense list rows inside dashboard panels (template library, saved reports). */
  .bao-list-row-dense {
    min-height: var(--bao-list-row-min-height, 3.25rem);
  }
  [data-report-templates] .list-row,
  [data-saved-reports] .list-row {
    min-height: var(--bao-list-row-min-height, 3.25rem);
    align-items: center;
  }

  /* Subgrid: children that need cross-sibling column alignment (status rows,
     export action rows, label/value meta grids) opt in via .bao-subgrid.
     The parent grid's column track is inherited; @supports guards older engines. */
  @supports (grid-template-columns: subgrid) {
    .bao-subgrid {
      grid-column: 1 / -1;
      display: grid;
      grid-template-columns: subgrid;
      align-items: center;
      gap: var(--space-inline);
    }
    .bao-subgrid-rows {
      grid-template-rows: subgrid;
    }
  }

  /* Dense toolbar kbd: hide below 2xl; unlock btn-square width when shown. */
  .bao-toolbar-action .kbd {
    display: none;
  }
  @media (min-width: 1536px) {
    .bao-toolbar-action .kbd {
      display: inline-flex;
    }
    .bao-toolbar-action.btn-square:has(> .kbd) {
      width: auto;
      min-width: 2.5rem;
      padding-inline: 0.625rem;
      gap: 0.375rem;
    }
  }

  /* Download cards contain file paths and action controls; direct-touch flow is one card per row. */
  @media (max-width: 42rem), (hover: none) and (pointer: coarse) {
    [data-predefined-models-section] [data-model-download-card] {
      grid-column: 1 / -1;
    }
  }

  /* Choice grid base SSOT: tokens-layout-framework.ts. Checked state only here. */
  .bao-choice-grid > .bao-choice-option:has(> input:checked) {
    border-color: color-mix(in oklab, var(--color-primary) 50%, var(--color-base-300));
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--color-primary) 20%, transparent);
  }



  /* Nested tables inside dashboard cards: no second framed host (SSOT single card). */
  [data-dashboard-widget] [data-case-list] > .bao-data-panel,
  [data-dashboard-widget] [data-primitive="DataTable"].bao-data-panel,
  [data-dashboard-widget] .overflow-x-auto.bao-data-panel {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  /* Nested framed cards under any widget host — unframe (never dual chrome). */
  [data-dashboard-widget] .card.card-border.bao-data-panel,
  [data-dashboard-widget] [data-primitive="Card"].bao-data-panel {
    border-width: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  /* Content KPI shells are solid (design.md content plane). Neutralize any
   * accidental glass filter so glass-on-content cannot reappear. */
  [data-page="dashboard"] [data-dashboard-widget],
  [data-page-type="home"] [data-dashboard-widget],
  [data-page-type="collection"] [data-dashboard-widget] {
    background-color: var(--bg-panel, var(--color-base-100));
    transition-property: border-color, box-shadow;
    transition-timing-function: var(--easing-standard);
    transition-duration: var(--duration-fast, 120ms);
  }
  /*
   * Pointer hover: elevate border only (never glass/frost on content KPIs).
   * Coarse/touch relies on focus-visible + press from shared motion tokens.
   */
  @media (hover: hover) and (pointer: fine) {
    [data-page="dashboard"] [data-dashboard-widget]:hover,
    [data-page-type="home"] [data-dashboard-widget]:hover,
    [data-page-type="collection"] [data-dashboard-widget]:hover {
      border-color: color-mix(
        in oklab,
        var(--color-primary, var(--p)) 28%,
        var(--color-base-300, var(--border-base))
      );
      box-shadow: 0 1px 0 color-mix(in oklab, var(--color-base-content) 6%, transparent) inset,
        0 4px 14px color-mix(in oklab, var(--color-base-content) 7%, transparent);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    [data-page="dashboard"] [data-dashboard-widget],
    [data-page-type="home"] [data-dashboard-widget],
    [data-page-type="collection"] [data-dashboard-widget] {
      transition-duration: var(--duration-instant);
    }
  }
  /* Dashboard panel body slot — dense KPI geometry. */
  .bao-dashboard-panel-value {
    margin-block-start: var(--space-micro);
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
  }

  /* Dashboard widget value fragment — two-track grid (copy | meta). */
  .bao-dashboard-widget-fragment {
    display: grid;
    min-width: 0;
    align-items: start;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-micro) var(--space-inline);
    text-align: start;
  }
  /*
   * Phone 2-up KPI tiles are ~11rem wide — side meta crushes status pills
   * ("Connect a capture device…"). Stack copy above meta so status wraps
   * full width (Apple Health summary density). Parent overflow must not clip
   * the wrap — card-body/panel-value clip was still ellipsing at 157px.
   */
  @media (max-width: 39.99rem) {
    [data-page="dashboard"] .bao-dashboard-widget-fragment,
    [data-page-type="home"] .bao-dashboard-widget-fragment {
      grid-template-columns: minmax(0, 1fr);
      gap: var(--space-micro, 0.25rem);
    }
    [data-page="dashboard"] .bao-dashboard-widget-meta,
    [data-page-type="home"] .bao-dashboard-widget-meta {
      justify-self: start;
      max-width: 100%;
      width: 100%;
      flex-direction: row;
      flex-wrap: wrap;
      align-items: center;
      justify-content: flex-start;
    }
    [data-page="dashboard"] .bao-dashboard-widget-status,
    [data-page-type="home"] .bao-dashboard-widget-status,
    [data-page="dashboard"] .bao-dashboard-widget-secondary,
    [data-page-type="home"] .bao-dashboard-widget-secondary {
      white-space: normal;
      overflow: visible;
      text-overflow: unset;
      max-width: 100%;
      overflow-wrap: anywhere;
    }
    [data-page="dashboard"] .bao-dashboard-panel-value,
    [data-page-type="home"] .bao-dashboard-panel-value,
    [data-page="dashboard"] [data-dashboard-widget] .card-body,
    [data-page-type="home"] [data-dashboard-widget] .card-body {
      overflow: visible;
    }
  }
  .bao-dashboard-widget-copy {
    min-width: 0;
  }
  .bao-dashboard-widget-value {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-xl, 1.25rem);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: var(--tracking-tight, -0.025em);
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
  }
  .bao-dashboard-widget-value:empty {
    display: none;
  }
  .bao-dashboard-widget-label {
    display: block;
    margin-block-start: var(--space-micro);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-xs, 0.75rem);
    font-weight: 500;
    color: var(--text-tertiary);
  }
  .bao-dashboard-widget-label:empty {
    display: none;
  }
  .bao-dashboard-widget-secondary {
    display: block;
    margin-block-start: var(--space-micro);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-tertiary);
  }
  /* Paths / mono detail lines never blow out the KPI tile width. */
  .bao-dashboard-widget-secondary[data-path],
  .bao-dashboard-widget-secondary.font-mono,
  [data-dashboard-widget] .bao-dashboard-widget-secondary {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Compact density densifies body; height ownership is panel-grid (content-first). */
  [data-dashboard-widget][data-widget-density="compact"] .card-body,
  [data-dashboard-widget][data-widget-density="compact"] [data-widget-value],
  [data-dashboard-widget][data-widget-density="compact"] .bao-dashboard-panel-value {
    min-height: 0;
    flex: 0 1 auto;
  }
  .bao-dashboard-widget-secondary:empty {
    display: none;
  }
  @media (max-width: 40rem) {
    .bao-dashboard-widget-value {
      font-size: var(--text-lg, 1.125rem);
    }
    [data-dashboard-widget] {
      min-width: 0;
    }
  }
  /*
   * KPI reflow (design.pdf §9.5 "reflow rather than clip text", §9.6 "fixed
   * capsules may need to become rows").
   *
   * The two-track grid above pairs minmax(0,1fr) copy with an `auto` meta
   * track. The meta track holds a nowrap status chip, so it cannot shrink
   * while the copy track floors at 0 — a 168px tile left the value 94px and
   * rendered "Setup required" as "Set...". Below the stacking threshold the
   * chip drops to its own row and the value keeps the full inline size.
   *
   * Container-scoped, not viewport-scoped: the SAME tile is narrow at 2-up on
   * a phone, 3-up on a tablet and 4-up on a wide desktop, so a viewport
   * breakpoint cannot express the condition (design.pdf §8 — adapt to the
   * available window, not a guessed device class).
   *
   * :has() keeps inline-size containment off the recent-cases value slot,
   * whose .dropdown-content must escape the tile box.
   */
  .bao-dashboard-panel-value:has(> .bao-dashboard-widget-fragment) {
    container-type: inline-size;
    container-name: bao-widget-value;
  }
  /*
   * 20rem (not 15rem): desktop 3–4-up KPI tiles often land ~16–19rem wide —
   * enough for a status chip beside the value but not for "Setup required"
   * at text-xl, which previously ellipsed to "Setup r…". Stack earlier so
   * operators always read full KPI words (design.pdf §9.5).
   */
  @container bao-widget-value (max-width: 20rem) {
    .bao-dashboard-widget-fragment {
      grid-template-columns: minmax(0, 1fr);
    }
    .bao-dashboard-widget-meta {
      flex-direction: row;
      flex-wrap: wrap;
      align-items: center;
      justify-content: flex-start;
    }
    /* Value and label own the tile width here: wrap instead of ellipsing. */
    .bao-dashboard-widget-value,
    .bao-dashboard-widget-label {
      white-space: normal;
      overflow-wrap: anywhere;
    }
    .bao-dashboard-widget-value {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
    }
  }
  .bao-dashboard-widget-meta {
    display: flex;
    flex-shrink: 0;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-micro);
  }
  /* Trend chip — direction + colour (WCAG 1.4.1). */
  .bao-dashboard-widget-trend {
    display: inline-flex;
    align-items: center;
    gap: var(--space-micro);
    white-space: nowrap;
    border-radius: var(--radius-full);
    padding-inline: var(--space-micro);
    padding-block: var(--space-micro);
    font-size: var(--text-xs, 0.75rem);
    font-weight: 600;
    line-height: 1.2;
    background: color-mix(in oklab, var(--text-primary) 8%, transparent);
    color: var(--text-secondary);
  }
  .bao-dashboard-widget-trend::before {
    content: "\2192";
    font-variant-numeric: tabular-nums;
  }
  .bao-dashboard-widget-trend[data-trend-value="up"] {
    background: color-mix(in oklab, var(--status-success) 14%, transparent);
    color: var(--status-success);
  }
  .bao-dashboard-widget-trend[data-trend-value="up"]::before {
    content: "\2191";
  }
  .bao-dashboard-widget-trend[data-trend-value="down"] {
    background: color-mix(in oklab, var(--status-error) 14%, transparent);
    color: var(--status-error);
  }
  .bao-dashboard-widget-trend[data-trend-value="down"]::before {
    content: "\2193";
  }
  .bao-dashboard-widget-trend:empty {
    display: none;
  }
  .bao-dashboard-widget-status {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    gap: var(--space-micro);
    white-space: nowrap;
    font-size: var(--text-xs, 0.75rem);
    font-weight: 500;
    color: var(--text-tertiary);
  }
  .bao-dashboard-widget-detail {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    margin-block-start: var(--space-micro);
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: var(--text-xs, 0.75rem);
    line-height: 1.3;
    color: var(--text-tertiary);
  }
  .bao-dashboard-widget-detail:empty {
    display: none;
  }
  /* Phone density (Apple HIG): drop long secondary detail; status + value remain. */
  @media (max-width: 40rem) {
    .bao-dashboard-widget-detail {
      display: none;
    }
    .bao-dashboard-widget-secondary:not([data-path]):not(.font-mono) {
      display: none;
    }
    .bao-dashboard-widget-trend:empty,
    .bao-dashboard-widget-trend:not([data-trend-value]),
    .bao-dashboard-widget-trend[data-trend-value=""],
    .bao-dashboard-widget-trend[data-trend-value="flat"],
    .bao-dashboard-widget-trend[data-trend-value="none"] {
      display: none;
    }
  }
  /* Whole-card affordance: the widget action is a stretched cover link over
     the card (design.md §5.4 — the card IS the control; no duplicate pill).
     Inner interactive chrome (customize controls) layers above it (z-20). */
  .bao-dashboard-widget-action {
    position: absolute;
    inset: 0;
    margin: 0;
    padding: 0;
    min-height: 0;
    min-width: 0;
    height: auto;
    align-self: auto;
  }
  .bao-dashboard-widget-action:empty {
    display: none;
  }
  /* Empty KPI: drop long detail so tiles do not balloon equal-row white voids */
  [data-dashboard-widget-fragment][data-state="empty"] + .bao-dashboard-widget-detail {
    display: none;
  }
 

  /*
   * KPI / collection solid grid densify (happydumpling / uiux-standardizer):
   * solid bao-data-panel widgets, compact headers, no idle indicator bloat.
   * Phone single-column lives in tokens-glass-shell-webview (all data-page).
   * Collection pages (evidence, reports) use the same DashboardWidget shell.
   */
  [data-page-type="home"] [data-dashboard-grid],
  [data-page="dashboard"] [data-dashboard-grid],
  [data-page-type="collection"] [data-dashboard-grid] {
    grid-auto-rows: minmax(0, auto);
  }
  [data-page="dashboard"] [data-dashboard-widget]:not([data-widget="recent-cases"]),
  [data-page-type="collection"] [data-dashboard-widget] {
    height: auto;
    align-self: start;
  }
  /* Idle HTMX indicators never reserve layout space */
  .htmx-indicator:not(.htmx-request) {
    display: none;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
    overflow: hidden;
    position: absolute;
  }
  .htmx-indicator.htmx-request {
    display: inline-block;
  }

  [data-page="dashboard"] [data-dashboard-widget]:not([data-widget="recent-cases"]),
  [data-page-type="collection"] [data-dashboard-widget] {
    padding: 0;
    overflow: hidden;
  }

  /*
   * Compact density (design.md content-first): progressive strips such as the
   * case journey must not stretch to the tallest grid row or pad like KPI cards.
   */
  [data-dashboard-widget][data-widget-density="compact"] {
    height: auto;
    min-height: 0;
    max-height: none;
    align-self: start;
  }
  [data-dashboard-widget][data-widget-density="compact"] > .card-body,
  [data-dashboard-widget][data-widget-density="compact"] > [class*="card-body"] {
    padding-block: var(--space-micro, 0.25rem);
    padding-inline: var(--space-inline, 0.5rem);
    gap: var(--space-micro, 0.25rem);
  }
  [data-dashboard-widget][data-widget-density="compact"] [data-case-journey-host],
  [data-dashboard-widget][data-widget-density="compact"] [data-case-journey] {
    min-height: 0;
  }

  /*
   * Single padding owner: card-body (panelShell). Never re-pad the first child —
   * double padding was the evidence-health / KPI tile spacing sin.
   * Collection pathologist panels share the same geometry contract.
   */
  [data-page="dashboard"] [data-dashboard-widget] > .card-body,
  [data-page="dashboard"] [data-dashboard-widget] > [class*="card-body"],
  [data-page-type="collection"] [data-dashboard-widget] > .card-body,
  [data-page-type="collection"] [data-dashboard-widget] > [class*="card-body"] {
    padding: var(--space-inline);
    gap: var(--space-micro);
    min-height: 0;
  }

  [data-page="dashboard"] [data-dashboard-widget] .card-title,
  [data-page="dashboard"] [data-dashboard-widget] .bao-dashboard-widget-title,
  [data-page-type="collection"] [data-dashboard-widget] .card-title,
  [data-page-type="collection"] [data-dashboard-widget] .bao-dashboard-widget-title {
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    line-height: 1.25;
    margin: 0;
    gap: var(--space-micro);
  }

  [data-page="dashboard"] [data-dashboard-widget] .bao-dashboard-panel-value,
  [data-page-type="collection"] [data-dashboard-widget] .bao-dashboard-panel-value {
    min-height: 0;
    margin-block-start: 0;
    flex: 1 1 auto;
  }

  /* Nested fragment hosts stay unframed — DashboardWidget owns the only chrome. */
  [data-page="dashboard"] [data-dashboard-widget] [data-dashboard-widget-fragment],
  [data-page-type="collection"] [data-dashboard-widget] [data-dashboard-widget-fragment] {
    min-width: 0;
  }
  [data-page="dashboard"] [data-dashboard-widget] .bao-dashboard-widget-action:empty,
  [data-page="dashboard"] [data-dashboard-widget] a.bao-dashboard-widget-action[href=""],
  [data-page="dashboard"] [data-dashboard-widget] a.bao-dashboard-widget-action:not([href]),
  [data-page-type="collection"] [data-dashboard-widget] .bao-dashboard-widget-action:empty,
  [data-page-type="collection"] [data-dashboard-widget] a.bao-dashboard-widget-action[href=""],
  [data-page-type="collection"] [data-dashboard-widget] a.bao-dashboard-widget-action:not([href]) {
    display: none;
  }

  /* Idle HTMX indicators must not reserve layout on home/collection. */
  [data-page="dashboard"] .htmx-indicator:not(.htmx-request),
  [data-page-type="home"] .htmx-indicator:not(.htmx-request),
  [data-page-type="collection"] .htmx-indicator:not(.htmx-request) {
    display: none;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
    overflow: hidden;
    position: absolute;
  }

  /*
   * Layout customizer exclusivity (Apple edit mode):
   * daisyUI .card-actions is @layer — unlayered SSOT with compound
   * class+attr selectors wins via cascade. Only show size/move/
   * visibility chrome while data-dashboard-editing=true.
   */
  [data-dashboard-panel-controls][hidden],
  [data-dashboard-panel-controls].hidden,
  [data-dashboard-visibility-tray][hidden],
  [data-dashboard-visibility-tray].hidden,
  [data-dashboard-uniform-size-tray][hidden],
  [data-dashboard-uniform-size-tray].hidden,
  [data-dashboard-layout-root][data-dashboard-editing="false"] [data-dashboard-panel-controls],
  [data-dashboard-layout-root][data-dashboard-editing="false"] [data-dashboard-visibility-tray],
  [data-dashboard-layout-root][data-dashboard-editing="false"] [data-dashboard-uniform-size-tray],
  .card-actions[data-dashboard-panel-controls][hidden],
  .card-actions[data-dashboard-panel-controls].hidden,
  .card-actions[data-dashboard-panel-controls][hidden].flex,
  .card-actions[data-dashboard-panel-controls].hidden.flex {
    display: none;
    visibility: hidden;
    pointer-events: none;
    width: 0;
    height: 0;
    min-height: 0;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border: 0;
    gap: 0;
  }

  [data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-panel-controls]:not([hidden]):not(.hidden),
  [data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-visibility-tray]:not([hidden]):not(.hidden),
  [data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-uniform-size-tray]:not([hidden]):not(.hidden) {
    display: flex;
    visibility: visible;
    pointer-events: auto;
    width: auto;
    height: auto;
    min-height: 0;
  }

  /* Uniform size tray labels → icon-first density on phone when editing. */
  @media (max-width: 47.99rem) {
    [data-dashboard-uniform-size-tray] > .btn {
      min-width: var(--touch-target-min, 2.75rem);
      max-width: var(--touch-target-min, 2.75rem);
      padding-inline: 0;
      justify-content: center;
    }
  }

  /*
   * Popover customizer (renderAs: "popover"):
   * Source shell under the layout root is display:contents / none until edit.
   * JS reparents trays into [data-dashboard-customizer-dropdown] next to the
   * header customize button — that host must size and show content itself.
   */
  [data-dashboard-customizer-popover] {
    display: contents;
  }
  [data-dashboard-layout-root][data-dashboard-customizer-render="popover"][data-dashboard-editing="false"] [data-dashboard-customizer-popover] {
    display: none;
  }
  [data-dashboard-layout-root][data-dashboard-customizer-render="popover"][data-dashboard-editing="true"] [data-dashboard-customizer-content] {
    display: flex;
    flex-direction: column;
    visibility: visible;
  }
  /*
   * Runtime host + open-tray geometry live UNLAYERED in
   * tokens-layout-customizer-host.ts — inside @layer utilities they lose to
   * unlayered daisyUI dropdown structure and the tray ships 0x0.
   */
  [data-dashboard-customizer-dropdown] [data-dashboard-uniform-size-tray]:not([hidden]):not(.hidden),
  [data-dashboard-customizer-dropdown] [data-dashboard-visibility-tray]:not([hidden]):not(.hidden),
  [data-dashboard-layout-root][data-dashboard-customizer-render="popover"] [data-dashboard-uniform-size-tray]:not([hidden]):not(.hidden),
  [data-dashboard-layout-root][data-dashboard-customizer-render="popover"] [data-dashboard-visibility-tray]:not([hidden]):not(.hidden) {
    display: flex;
    flex-wrap: wrap;
    visibility: visible;
    width: 100%;
    max-width: none;
    min-height: var(--touch-target-min, 2.75rem);
    margin-block: 0;
    gap: var(--space-micro, 0.25rem);
  }
  [data-dashboard-customizer-dropdown] [data-dashboard-customizer-section],
  [data-dashboard-customizer-content] [data-dashboard-customizer-section] {
    display: flex;
    flex-direction: column;
    gap: var(--space-micro, 0.25rem);
    width: 100%;
    min-width: 0;
  }

  /*
   * Phone home densify — widget helper/body copy is secondary to the KPI.
   * Two-line clamp keeps the card grid scannable (Apple widget density);
   * full copy remains in aria when truncated by overflow.
   */
  @media (max-width: 47.99rem) {
    [data-page="dashboard"] [data-dashboard-widget] .text-label,
    [data-page="dashboard"] [data-dashboard-widget] .text-xs.text-label,
    [data-page="dashboard"] [data-dashboard-widget] p.text-xs,
    [data-page="dashboard"] [data-dashboard-widget] [data-widget-description] {
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    [data-page="dashboard"] [data-dashboard-widget] .bao-dashboard-panel-value {
      font-size: var(--text-lg, 1.125rem);
      line-height: 1.2;
    }
    /*
     * Drag-handle chrome clutter: fine-pointer grab affordance is desktop-only.
     * Phone / coarse pointers reorder via customize move buttons (HIG 2.5.7).
     */
    [data-dashboard-drag-handle] {
      cursor: default;
      min-height: 0;
      padding-block: 0;
    }
  }
  @media (pointer: coarse) {
    [data-dashboard-drag-handle] {
      cursor: default;
      min-height: 0;
      padding-block: 0;
    }
  }

  /*
   * Solid KPI metric card — never glass material (Apple layer model / glass law).
   * Hard-ban accidental bao-surface-glass-* co-tags on metric hosts.
   */
  
  .bao-stat-card.bao-surface-glass-regular,
  .bao-stat-card.bao-surface-glass-clear,
  .bao-stat-card.bao-surface-glass-strong,
  [data-dashboard-widget].bao-surface-glass-regular,
  [data-dashboard-widget].bao-surface-glass-clear,
  [data-dashboard-widget].bao-surface-glass-strong {
    background-color: var(--color-base-100, var(--bg-panel));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }



  /* Page-level skeleton shells — geometry matches final success layout
     (CLS < 0.1). Pair daisyUI skeleton class on children for motion. */
  .bao-page-skeleton {
    box-sizing: border-box;
    width: 100%; min-width: 0; max-width: 100%;
    overflow-x: clip;
  }
  .bao-page-skeleton-default {
    display: flex;
    flex-direction: column;
    gap: var(--space-inline);
    padding-block: var(--space-page);
  }
  .bao-page-skeleton-grid {
    display: grid;
    align-items: stretch;
    grid-auto-rows: auto;
    gap: var(--space-section);
    padding-block: var(--space-page);
    container-type: inline-size;
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(100%, var(--grid-track-min, 18rem)), 1fr)
    );
  }
  /* Detail / auth skeleton: centered single column matching AuthShell card
     geometry so the skeleton-to-content swap stays CLS-safe (CLS < 0.1). */
  .bao-page-skeleton-detail {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 0;
    flex: 1 1 auto;
    gap: var(--space-section);
    padding-block: var(--space-empty);
    padding-inline: var(--space-page);
  }
  .bao-page-skeleton-detail > .bao-skeleton-panel {
    width: 100%;
    max-width: var(--auth, 28rem);
  }
  .bao-page-skeleton-table {
    display: flex;
    flex-direction: column;
    gap: var(--space-inline);
    padding-block: var(--space-page);
  }
  .bao-page-skeleton-media {
    display: grid;
    min-width: 0;
    gap: var(--space-section);
    padding-block: var(--space-page);
    container-type: inline-size;
    grid-template-columns: minmax(0, 1fr);
  }
  @container (min-width: 40rem) {
    .bao-page-skeleton-media {
      grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    }
  }
  .bao-page-skeleton-media-side {
    display: flex;
    flex-direction: column;
    gap: var(--space-inline);
    min-width: 0; max-width: 100%;
  }
  /* Conversation skeleton matches context strip + thread + composer geometry. */
  .bao-page-skeleton-conversation {
    display: flex;
    flex-direction: column;
    gap: var(--space-section);
    min-height: 0;
    flex: 1 1 auto;
    padding-block: var(--space-page);
  }
  .bao-skeleton-conversation-chip {
    height: 3rem;
    width: 100%;
    min-width: 0;
    border-radius: var(--radius-field, 0.75rem);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-conversation-thread {
    flex: 1 1 auto;
    min-height: min(28rem, 50vh);
    width: 100%;
    border-radius: var(--radius-box, 1rem);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  .bao-skeleton-conversation-composer {
    height: 8rem;
    width: 100%;
    max-width: var(--page-wide, 96rem);
    margin-inline: auto;
    border-radius: var(--radius-box, 1rem);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface-hover);
  }

  /* Skeleton blocks — solid surfaces for data (no glass on dense chrome). */
  .bao-skeleton-line {
    height: 1rem;
    width: 100%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-line-short {
    height: 1rem;
    width: 75%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-panel {
    box-sizing: border-box; display: flex;
    flex-direction: column;
    gap: var(--space-inline);
    min-height: var(--skeleton-panel-min-height, 7rem);
    width: 100%; min-width: 0; max-width: 100%;
    overflow-x: clip;
    padding: var(--space-page);
    border-radius: var(--radius-box, 1rem);
    border: 1px solid var(--border-base);
    background-color: var(--bg-panel);
    box-shadow: 0 1px 2px color-mix(in oklab, var(--color-base-content) 6%, transparent);
  }
  .bao-skeleton-panel-body {
    height: 3.5rem;
    width: 100%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-home-full {
    min-height: 12rem;
    width: 100%;
    border-radius: var(--radius-box, 1rem);
  }
  .bao-skeleton-media {
    box-sizing: border-box; aspect-ratio: 16 / 9;
    width: 100%; max-width: 100%;
    min-height: var(--skeleton-media-min-height, 12rem);
    overflow-x: clip;
    border-radius: var(--radius-box, 1rem);
    border: 1px solid var(--border-base);
    background-color: var(--bg-surface);
  }
  .bao-skeleton-table-header {
    height: var(--density-default-row-height, 2.25rem);
    width: 100%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }
  .bao-skeleton-table-row {
    height: var(--density-default-row-height, 2.25rem);
    width: 100%;
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface);
    border: 1px solid var(--border-base);
  }
  /* Pathologist journey step skeleton — matches .bao-pathologist-journey step
     label + circle geometry (tokens-glass-journey-workflow). */
  .bao-pathologist-journey .bao-skeleton-journey-step,
  .bao-skeleton-journey-step {
    display: inline-block;
    height: var(--text-2xs, 0.6875rem);
    width: 4.5rem;
    max-width: 100%;
    min-height: var(--touch-target-min, 2.75rem);
    border-radius: var(--radius-sm);
    background-color: var(--bg-surface-hover);
  }



  /* Named layout surfaces — replace arbitrary Tailwind brackets in .bao tokens. */
  .bao-editor-root {
    min-width: 0;
    max-width: 100%;
    min-height: var(--editor-root-min-height);
    overflow-x: clip;
  }
  /**
   * Canvas column owns the floating toolbar + canvas stage.
   * It is the flex item under workbench (not the nested canvas alone).
   */
  .bao-editor-canvas-column {
    position: relative;
    display: flex;
    min-width: 0;
    max-width: 100%;
    /* Bound flex cross-axis so Fabric host cannot grow the page unboundedly. */
    min-height: 0;
    max-height: 100%;
    flex: 1 1 var(--editor-canvas-basis, 32rem);
    flex-direction: column;
    /*
     * No horizontal scrollport on the column: a panning column could carry
     * the floating tool dock off either screen edge (AE-03) and fought touch
     * draw gestures. Horizontal scrolling lives strictly on the tool strip
     * below (overflow-x: auto + pan-x). overflow-x: hidden keeps the column
     * a scroll container so sticky chrome keeps its current containing port.
     */
    overflow-x: hidden;
    overflow-y: hidden;
  }
  /**
   * Sticky (not absolute) tool strip: sits in normal flow of the canvas column
   * so its scrollport is always viewport-bounded. Absolute overlays were
   * measuring wider than the column and failing hard overflow audits.
   */
  .bao-editor-floating-toolbar,
  .bao-editor-canvas-column > [data-editor-toolbar] {
    box-sizing: border-box;
    position: sticky;
    top: 0;
    display: flex;
    /*
     * Wrap, never scroll horizontally.
     *
     * This was flex-wrap: nowrap + overflow-x: auto, which hides content at
     * EVERY width once the tools carry their verbs. Measured with labels on:
     * 18 tools at ~91px each need ~1638px, so a 768px tablet dock reported 568px
     * of hidden horizontal scroll and even a 1280px desktop cannot fit the
     * strip. A palette whose controls sit off-screen behind a scrollbar with no
     * affordance is the original "cut off / bunched up" defect in its general
     * form — the phone was simply where it was noticed first.
     *
     * Wrapping keeps every tool reachable at every width and costs vertical
     * space that the collapse + sheet pattern already manages on phone.
     */
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-micro, 0.25rem);
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding-block-start: calc(env(safe-area-inset-top, 0px) + var(--space-micro, 0.25rem));
    overflow-x: hidden;
    /*
     * The dock grows to fit its wrapped rows. overflow-y: hidden here simply
     * traded one hidden dimension for another: measured at 768px the wrapped
     * palette needed 260px against a 218px box, clipping 42px — a whole tool
     * row — with no scrollbar and no affordance. A palette must not hide its
     * tools in either axis.
     */
    overflow-y: visible;
    flex: 0 0 auto;
    height: auto;
    row-gap: var(--space-micro, 0.25rem);
  }

  /*
   * The controls row and its filter .join bound themselves at EVERY width.
   *
   * daisyUI's .join is inline-flex and cannot wrap, and the markup pins it
   * with .shrink-0, so once its chips carry verbs it measures 421px and pushes
   * the dock back into hidden horizontal scroll — 92px of it at 768px even
   * after the dock itself started wrapping. These were first fixed inside the
   * phone-expanded block, which left tablet and desktop broken; the constraint
   * belongs to the component, not to one breakpoint.
   */
  /*
   * The scroll track and every joined row wrap at EVERY width.
   *
   * These were only taught to wrap inside the phone media query, so expanding
   * the sheet on tablet reproduced the original defect exactly: measured at
   * 768px the track, the controls row and each [data-tool-group] all reported
   * 916px inside a 694px dock — 238px of hidden horizontal scroll with 38
   * elements past the right edge. The groups are daisyUI .join elements
   * carrying .shrink-0: inline-flex, so they neither wrap nor narrow on their
   * own, and bounding only the controls row left every tool group untouched.
   */
  [data-editor-toolbar] [data-editor-tool-scroll] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-micro, 0.25rem);
    min-width: 0;
    max-width: 100%;
  }
  [data-editor-toolbar] [data-tool-group] {
    flex-wrap: wrap;
    flex-shrink: 1;
    min-width: 0;
    max-width: 100%;
    row-gap: var(--space-micro, 0.25rem);
  }
  [data-editor-toolbar] [data-editor-toolbar-controls] {
    flex-wrap: wrap;
    min-width: 0;
    max-width: 100%;
  }
  [data-editor-toolbar] [data-editor-toolbar-controls] fieldset.join {
    flex-wrap: wrap;
    flex-shrink: 1;
    max-width: 100%;
    row-gap: var(--space-micro, 0.25rem);
  }
  [data-editor-toolbar] [data-editor-toolbar-controls] > label {
    box-sizing: border-box;
    min-width: 0;
    max-width: 100%;
  }
  /*
   * The spacer pushes trailing actions right on ONE line; on a wrapped line it
   * pushes them off the edge instead. Its min-width was carrying Redo 91px
   * past the dock at 768px even with everything else bounded. In a wrapping
   * palette the spacer's only job is to eat slack, so it must be free to
   * collapse to nothing.
   */
  [data-editor-toolbar] > .bao-min-w-toolbar-spacer {
    min-width: 0;
    flex-basis: 0;
  }

  .bao-editor-floating-toolbar > .btn,
  .bao-editor-canvas-column > [data-editor-toolbar] > .btn,
  .bao-editor-floating-toolbar [role="toolbar"] > .btn,
  .bao-editor-canvas-column > [data-editor-toolbar] [role="toolbar"] > .btn {
    flex: 0 0 auto;
  }
  /*
   * Context status strip — always a bounded horizontal track. Badges may
   * scroll; they must never expand #app-main past the viewport (paint
   * audits on 320–390px phones).
   */
  [data-editor-context-bar] {
    min-width: 0;
    max-width: 100%;
  }
  [data-editor-chrome] > .flex:has(> [data-editor-context-bar]) {
    min-width: 0;
    max-width: 100%;
  }
  /*
   * Context badge value pill — single-line ellipsis at every viewport so
   * long image UUIDs ('30c35c36-bd5e-...-52ee0') never balloon the strip
   * past the editor column. The dock-densify-b phone rule covers ≤47.99rem;
   * this global rule extends the cap to tablet/desktop so Custody never
   * gets pushed off the right edge of the rail.
   */
  [data-editor-context] strong,
  [data-editor-context-bar] .bao-context-badge strong,
  [data-capture-context] strong {
    display: block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }
  [data-editor-context],
  [data-editor-context-bar] .bao-context-badge,
  [data-capture-context] {
    min-width: 0;
    max-width: 100%;
  }

  /*
   * Phone / compact tablet (≤47.99rem): icon-first tool chrome.
   * Search demotes — filter joins + tool icons own the strip (HIG dense
   * toolbar; labels stay in aria-label/tooltip). Breakpoint aligned to
   * shell phone band (not 640px drift).
   */

  
@media (max-width: 47.99rem) {
  .bao-editor-floating-toolbar,
  .bao-editor-canvas-column > [data-editor-toolbar] {
    margin-block-end: 0;
  }
  [data-editor-toolbar-controls] {
    flex: 0 0 auto;
    overflow-x: visible;
  }
  [data-editor-context-bar] {
    display: flex;
    max-width: 100%;
    flex-wrap: nowrap;
    gap: var(--space-micro, 0.25rem);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    touch-action: pan-x;
  }
  [data-editor-context-bar]::-webkit-scrollbar {
    display: none;
  }
  [data-editor-context-bar] .bao-context-badge {
    flex: 0 0 auto;
    max-width: 10.5rem;
    scroll-snap-align: start;
  }
  [data-editor-context-bar] [data-editor-context="image"].bao-context-badge,
  [data-editor-context-bar] [data-editor-context="image"] {
    max-width: 7.25rem;
  }
  [data-editor-context-bar] .bao-context-badge > span:not(.status):not(.sr-only):first-of-type {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  [data-editor-status-bar] {
    max-height: 28vh;
    overflow: hidden;
    gap: var(--space-2xs, 0.125rem);
  }
  [data-editor-status-bar] [data-editor-status="cursor"],
  [data-editor-status-bar] [data-editor-status="input"],
  [data-editor-status-bar] [data-editor-status="layer"],
  [data-editor-status-bar] [data-editor-status="calibration"] {
    display: none;
  }
  [data-editor-chrome] [data-editor-save],
  [data-editor-chrome] form[data-editor-save-form] button[type="submit"],
  [data-editor-chrome] [data-bao-action="save-annotations"] {
    position: sticky;
    bottom: var(--space-micro, 0.25rem);
    z-index: var(--z-canvas-overlay, 30);
  }
  [data-ai-overlay-item] [data-ai-overlay-coords],
  [data-ai-overlay-item] [data-ai-overlay-space],
  [data-ai-overlay-item] [data-ai-overlay-geometry-projection-row],
  [data-ai-overlay-item] [data-ai-overlay-source-row],
  [data-ai-overlay-item] [data-ai-overlay-created-row] {
    display: none;
  }
  .bao-ai-provenance-sticky {
    grid-template-columns: minmax(0, 1fr);
  }
}

  .bao-editor-canvas {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    max-width: 100%;
    min-height: max(min(24rem, var(--editor-canvas-height, clamp(26rem, 58vh, 44rem))), min(18rem, 50vh));
    /*
     * No viewport-fraction cap — let the canvas fill its bounded flex chain.
     * The host column owns the height bound; capping at 70vh starved tall
     * viewports and clipped content. WebView Chromium 101 (BlueStacks) does not
     * support dvh (Chrome 108+); use 100% of the bounded host instead.
     */
    max-height: 100%;
    flex: 1 1 auto;
    height: 100%;
    overflow: hidden;
  }
  /* Annotate session panel shares the same bounded flex chain as capture stage.
   * Panel + chrome clip; workbench owns overflow-y scroll (never nest hidden). */
  [data-session-panel="annotate"] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: 100%;
    flex: 1 1 0%;
    overflow: hidden;
  }
  [data-session-panel="annotate"] [data-editor-chrome] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: 100%;
    flex: 1 1 0%;
    overflow: hidden;
  }
  [data-session-panel="annotate"] .bao-editor-workbench {
    min-height: 0;
    max-height: 100%;
    flex: 1 1 0%;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
  }
  .bao-editor-canvas[data-state="success"] [data-editor-canvas-empty] {
    display: none;
  }
  /*
   * Empty / error: Fabric stage paints an opaque white box over the empty
   * CTA (z-canvas-base under fabric). Park fabric below and inert until an
   * image loads; empty panel owns the full stage with solid fill + CTAs.
   */
  .bao-editor-canvas[data-state="empty"] .bao-fabric-stage,
  .bao-editor-canvas[data-state="error"] .bao-fabric-stage {
    opacity: 0;
    pointer-events: none;
    z-index: var(--z-canvas-base, 1);
  }
  .bao-editor-canvas[data-state="empty"] [data-editor-canvas-empty],
  .bao-editor-canvas[data-state="error"] [data-editor-canvas-empty] {
    display: flex;
    z-index: var(--z-canvas-empty, 20);
    pointer-events: auto;
  }
  /* No clinical image: present one acquisition decision, not an inert tool wall. */
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-toolbar] [data-tool],
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-ai-review-form] button[type="submit"],
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-ai-review-form] select,
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-ai-review-form] input:not([type="hidden"]) {
    pointer-events: none;
  }
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-context-bar],
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-toolbar],
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-canvas-overlays],
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-save-form],
  [data-editor-chrome]:not([data-annotation-ready="true"]) .bao-editor-3d-column {
    display: none;
  }
  /* Dual guard: the canvas island is authoritative when its state is empty. */
  .bao-editor-canvas[data-state="empty"] ~ [data-editor-toolbar],
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) [data-editor-toolbar],
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) [data-editor-canvas-overlays],
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) .bao-editor-3d-column,
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) [data-editor-save-form] {
    display: none;
  }
  /*
   * Empty / not-ready stage: keep the status strip mounted so AI model
   * provenance chips remain visible once the registry selects a live model.
   * Diagnostic chips (cursor/zoom/input) stay hidden until annotation-ready.
   */
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-status-bar],
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) [data-editor-status-bar] {
    display: flex;
  }
  [data-editor-chrome]:not([data-annotation-ready="true"]) [data-editor-status-bar] [data-editor-status]:not([data-editor-status="ai-model"]):not([data-editor-status="ai-provider"]),
  [data-editor-chrome]:has(.bao-editor-canvas[data-state="empty"]) [data-editor-status-bar] [data-editor-status]:not([data-editor-status="ai-model"]):not([data-editor-status="ai-provider"]) {
    display: none;
  }
  /*
   * Wide viewports: the phone block (tokens-layout-editor-surfaces-phone)
   * caps this strip at 28vh, moves it clear of the image, and demotes the
   * engineering telemetry it classifies as non-clinical — but all of that
   * sits inside its own max-width media query. Desktop and tablet inherit
   * the wrapping and none of the containment, so a seventh chip starts a
   * second row that paints across the specimen.
   *
   * The input readout is the widest and restates its own label when no
   * device is detected; the phone block already groups it with cursor and
   * layer as engineering telemetry rather than a clinical readout. Dropping
   * just that one returns the remaining readouts to a single row.
   *
   * Hiding it in CSS is deliberate over the render-time data-status-empty
   * contract: that attribute pair is only unhidden by the AI provenance
   * client, so applying it to any other chip would delete the readout
   * permanently. Here the node and its live updates stay in the DOM.
   */
  @media (min-width: 48rem) {
    [data-editor-status-bar] [data-editor-status="input"] {
      display: none;
    }
  }
  /* Selected AI finding — ring so operators know which assessment is active. */
  [data-ai-overlay-item][data-selected="true"],
  [data-ai-overlay-item][aria-selected="true"] {
    outline: 2px solid color-mix(in oklab, var(--color-primary) 55%, transparent);
    outline-offset: 2px;
    border-radius: var(--radius-field, 0.5rem);
    background-color: color-mix(in oklab, var(--color-primary) 8%, var(--color-base-100));
  }
  [data-editor-chrome]:not([data-annotation-ready="true"]) .bao-editor-canvas-column {
    width: 100%;
    min-width: 100%;
    flex-basis: 100%;
  }
  /*
   * Immersion idle-hide: fade tool dock + stage header after inactivity
   * (JS sets data-editor-chrome-idle). Status strip STAYS painted — zoom /
   * AI provenance chips are diagnostic HUD, not chrome thrash (Apple media
   * player keeps the scrubber readable; design.md §5.4 diagnostic affordance).
   */
  [data-editor-chrome][data-editor-chrome-idle="true"] [data-editor-toolbar],
  [data-editor-chrome][data-editor-chrome-idle="true"] [data-editor-stage-header] {
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-normal, 200ms) var(--easing-standard, ease);
  }
  /*
   * Immersive maximize: mirror island state on editor chrome (WebViews without
   * :has). Hide inspector sheet; floating toolbar + stage/status keep glass SSOT.
   */
  [data-editor-chrome][data-canvas-maximized="true"] [data-editor-canvas-overlays] {
    display: none;
  }
  /* WebView-safe mirror of the maximized canvas state. */
  body[data-canvas-maximized="true"] [data-editor-canvas-overlays] {
    display: none;
  }
  body[data-canvas-maximized="true"] [data-editor-toolbar] {
    position: fixed;
    inset-inline: var(--page-gutter-inline);
    inset-block-end: calc(var(--dock-height) + env(safe-area-inset-bottom));
    z-index: var(--z-canvas-overlay);
  }
  /*
   * Selected tool affordance (design.pdf §2.11): active tool gets filled-weight
   * icon stroke beyond the glass ring — stroke-only SVGs only; no path fill.
   */
  [data-editor-toolbar] [data-tool][aria-pressed="true"] svg {
    stroke-width: 2.2;
  }
  [data-editor-toolbar] [data-tool][aria-pressed="true"] svg [fill="none"] {
    fill: none;
  }
  @media (prefers-reduced-motion: reduce) {
    [data-editor-chrome][data-editor-chrome-idle="true"] [data-editor-toolbar],
    [data-editor-chrome][data-editor-chrome-idle="true"] [data-editor-stage-header] {
      transition: none;
    }
  }
  [data-editor-requires-image][aria-disabled="true"] {
    pointer-events: none;
  }
  /*
   * Clear-glass density modifier (AE-14): stage header + status strip carry
   * bao-glass-small, which previously existed only for glass-regular, so the
   * class was dead on clear chrome. Small clear chrome drops to the mobile
   * blur step — the lightest live blur in the ladder above none.
   */
  .bao-surface-glass-clear.bao-glass-small {
    backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-clear));
    -webkit-backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-clear));
  }
  /*
   * 2D/3D dimension radios are sr-only (focusable) instead of display:none
   * (AE-06): the visible focus ring lands on the hosting label so keyboard
   * operation of the radiogroup stays perceivable.
   */
  [data-viz-dimension-switcher] label:has(input[type="radio"]:focus-visible) {
    outline: 2px solid var(--color-primary, var(--accent-primary, currentColor));
    outline-offset: 2px;
  }
  /* 'More tools' belongs to the compact thumb-zone dock; the desktop strip
     is a single scrollable row and never shows the sheet toggle (AE-09). */
  @media (min-width: 48rem) {
    [data-editor-more-tools] {
      display: none;
    }
  }
  /* Phone density: drop AI/spatial panel marketing blurbs; keep controls. */
  @media (max-width: 40rem) {
    [data-editor-ai-review] > p,
    [data-editor-spatial] > p,
    [data-editor-overlays] > p {
      display: none;
    }
    /* Model select already lists options; search is desktop densify only. */
    [data-editor-ai-model-search] {
      display: none;
    }
  }
  /* Disabled Finish/Generate must never look primary-ready (daisy softens). */
  [data-page-header-actions] .btn:disabled,
  [data-page-header-actions] .btn[aria-disabled="true"],
  [data-page-header-actions] .btn.btn-disabled {
    opacity: var(--opacity-muted, 0.55);
    pointer-events: none;
  }
/* Editor workbench mirrors media capture dock geometry so acquire/annotate
     share one reactive column system (content-column width,
not viewport).
     Order: optional tool rail,
canvas column (primary),
side stack (secondary). */
  .bao-editor-workbench {
    display: flex;
    flex-wrap: wrap;
    min-width: 0;
    max-width: 100%;
    min-height: 0;
    flex: 1 1 auto;
    align-items: stretch;
    gap: var(--space-section);
    overflow-x: hidden;
    overflow-y: hidden;
    overscroll-behavior-y: contain;
    container-type: inline-size;
  }
  /* The composite viewport owns one floating command dock across 2D and 3D. */
  .bao-editor-workbench {
    position: relative;
    padding-block-end: calc(var(--touch-target-min, 2.75rem) + 2 * var(--space-micro, 0.25rem));
  }
  .bao-editor-workbench > [data-editor-command-bar] {
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: var(--z-canvas-overlay, 30);
    inline-size: 100%;
    max-inline-size: 100%;
    flex: 0 0 auto;
  }
  /* Save bar clears the mobile dock + home indicator (HIG thumb zone). */
  .bao-editor-save-bar {
    padding-block-end: max(var(--space-micro, 0.25rem), env(safe-area-inset-bottom, 0px));
  }
  /* Sheet toggle exists only for the compact bottom-sheet transformation;
     the ≥48rem docked inspector column never renders a toggle (AE-07). */
  [data-inspector-toggle] {
    display: none;
  }/*
   * Dock clearance has ONE owner: #app-main. This rule used to add it again.
   *
   * Below 80rem the page container reserves the dock's height on its own bottom
   * padding (tokens-layout-page-shells-*,
the #app-main[data-main-page-type="media"]
   * branch),
which lifts everything inside it — including this sticky bar — above
   * the fixed dock. Re-reserving it here counted the same band twice: measured at
   * 390x844 the bar computed padding-block-end 68px against 4px at every wider
   * class,
so its box was 118px tall around a 54px control row. That 64px of
   * padding inside the bar is the empty band reported under Save Annotations.
   *
   * The bar keeps only the safe-area floor from the base rule above,
which is a
   * different concern (home indicator,
not dock) and is not reserved twice.
   */
  .bao-editor-workbench > .bao-editor-canvas-column {
    display: flex;
    min-width: min(100%, var(--editor-canvas-basis));
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    flex: 1 1 var(--editor-canvas-basis, 32rem);
    flex-direction: column;
    gap: var(--space-inline);
  }
  .bao-editor-workbench > [data-editor-tool-rail] {
    min-width: 0;
    max-width: 100%;
    flex: 0 1 min(12rem, 100%);
  }
  .bao-editor-workbench > [data-editor-canvas-overlays] {
    min-width: min(100%, var(--editor-inspector-basis));
    height: 100%;
    max-height: 100%;
    flex: 1 1 var(--editor-inspector-basis);
  }
  /*
   * Small browser viewports keep the workbench vertically scrollable. Column
   * priority is defined separately by the workbench container so a rail-narrowed
   * desktop follows the same stage-first contract.
   */
  @media (max-width: 64rem) {.bao-editor-workbench,
[data-session-panel="annotate"] .bao-editor-workbench {
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }
  }
  .bao-editor-spatial-form {
    margin-top: var(--space-section);
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: end;
    gap: var(--space-inline);
  }
  /*
   * Review-state FSM picker (design.md §6 selected-vs-rest): soft buttons at
   * rest; the CURRENT state owns the persistent primary fill; legal targets
   * stay quiet; illegal transitions disable without vanishing.
   */
  [data-editor-review-transitions] [data-review-transition] {
    justify-content: center;
    text-align: center;
  }
  [data-editor-review-transitions] [data-review-transition][aria-pressed="true"] {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: var(--accent-primary-content);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--accent-primary-content) 35%, transparent);
  }[data-editor-review-transitions] [data-review-transition]:disabled,
[data-editor-review-transitions] [data-review-transition][aria-disabled="true"] {
    opacity: var(--opacity-disabled, 0.5);
    box-shadow: none;
  }
  /* Two-up grid on narrow rails — never one clipped column (design.md §9.5). */
  [data-editor-review-transitions] fieldset {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
    gap: var(--space-micro, 0.25rem);
    width: 100%;
  }
  [data-editor-review-transitions] fieldset [data-review-transition] {
    width: 100%;
    min-width: 0;
  }
  /* Phone: 2 columns (x/y) with z wrapping — matches capture context-strip
     phone pattern; submit button already spans full-width via grid-column. */
  @media (max-width: 47.99rem) {
    .bao-editor-spatial-form {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }
  /* Submit spans its own row so the label never clips in the narrow inspector
     rail; coordinate fields (x/y and optional z) reflow across the row above. */
  .bao-editor-spatial-form > button {
    grid-column: 1 / -1;
  }/* The AI review dock reuses the spatial form shell but carries full-width
     controls (mode,
model,
search) rather than coordinate cells: stack them. */
  .bao-editor-spatial-form[data-editor-ai-review-form] {
    grid-template-columns: minmax(0, 1fr);
  }
  .bao-panel-scroll {
    min-width: 0;
    width: 100%;
    max-height: var(--panel-scroll-max-height);
    overflow-x: auto;
    overflow-y: auto;
  }
  .bao-sidebar-expanded {
    width: var(--sidebar-expanded);
    padding: var(--space-section);
  }/*
   * ID + class beats Tailwind overflow-x-hidden on #app-sidebar so tip +
   * focus rings escape the icon rail (daisyUI is-drawer-close pattern).
   */
  #app-sidebar.bao-sidebar-collapsed,
.bao-shell-sidebar.bao-sidebar-collapsed,
.bao-sidebar-collapsed {
    width: var(--sidebar-collapsed);
    padding: var(--space-micro);
    overflow-x: visible;
  }/* Icon-only rail: center targets,
keep ≥44px hit area on touch. */
  .bao-sidebar-collapsed .bao-nav-item {
    justify-content: center;
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    padding-inline: 0;
  }
  /*
   * Focus ring inset — outline-offset:2 clips under overflow-y:auto parents.
   * daisyUI 5 tips open on :hover / :has(:focus-visible) child only;the tip
   * class lives on the focused <a>,
so keyboard must force tip opacity here.
   */
  .bao-sidebar-collapsed .bao-nav-item:focus-visible {
    outline: 2px solid var(--color-primary, var(--accent-primary, currentColor));
    outline-offset: -2px;
    z-index: 2;
    position: relative;
  }.bao-sidebar-collapsed .bao-nav-item.tooltip:focus-visible[data-tip]::before,
.bao-sidebar-collapsed .bao-nav-item.tooltip:focus-visible::after {
    opacity: 100%;
    --tt-pos: 0rem;
  }.bao-sidebar-collapsed .bao-nav-item.menu-active,
.bao-sidebar-collapsed .bao-nav-item[aria-current="page"] {
    box-shadow: none;
    background: color-mix(in oklab, var(--accent-primary) 18%, var(--bg-panel));
  }
  /* Icon rail: hide group titles + expanded labels;tooltips carry names. */
  .bao-sidebar-collapsed .menu-title,
#app-sidebar.bao-sidebar-collapsed .menu-title {
    display: none;
  }.bao-sidebar-collapsed [data-sidebar-expanded-only],
#app-sidebar.bao-sidebar-collapsed [data-sidebar-expanded-only] {
    display: none;
  }.bao-sidebar-collapsed .bao-nav-item.tooltip::before,
.bao-sidebar-collapsed .bao-nav-item.tooltip::after,
#app-sidebar.bao-sidebar-collapsed .bao-nav-item.tooltip::before,
#app-sidebar.bao-sidebar-collapsed .bao-nav-item.tooltip::after {
    display: block;
  }
  @media (min-width: 80rem) {#app-sidebar .bao-nav-item.menu-active,
#app-sidebar .bao-nav-item[aria-current="page"] {
      background: color-mix(in oklab, var(--accent-primary) 20%, var(--bg-panel));
      box-shadow: inset 4px 0 0 0 var(--accent-primary);
      font-weight: 600;
    }
    /*
     * Permanent-rail band: smooth label/icon rescale when collapse toggles.
     * Expanded labels fade/max-width;collapsed icons recenter with a subtle
     * scale cue (design.md §8.6 — dock yields to rail at 80rem).
     */
    #app-sidebar .bao-nav-item [data-sidebar-expanded-only],
#app-sidebar .menu-title {
      transition-property: opacity, max-width, margin-inline-start;
      transition-duration: var(--duration-normal, 200ms);
      transition-timing-function: var(--easing-standard);
    }#app-sidebar .bao-nav-item svg,
#app-sidebar .bao-nav-item [aria-hidden="true"] svg {
      transition: transform var(--duration-fast, 120ms) var(--easing-standard);
      transform-origin: center;
    }.bao-sidebar-collapsed .bao-nav-item svg,
#app-sidebar.bao-sidebar-collapsed .bao-nav-item svg {
      transform: scale(1.08);
    }
  }
  @media (min-width: 80rem) and (prefers-reduced-motion: reduce) {#app-sidebar .bao-nav-item [data-sidebar-expanded-only],
#app-sidebar .menu-title,
#app-sidebar .bao-nav-item svg {
      transition: none;
    }.bao-sidebar-collapsed .bao-nav-item svg,
#app-sidebar.bao-sidebar-collapsed .bao-nav-item svg {
      transform: none;
    }
  }
  /* Phone: header-anchored customizer becomes a pinned bottom sheet
     (source popover is display:contents; runtime host is
     [data-dashboard-customizer-dropdown] next to the edit toggle). */
  @media (max-width: 47.99rem) {
    [data-dashboard-customizer-dropdown].dropdown-open {
      position: fixed;
      inset-inline: 0;
      inset-block-end: calc(var(--dock-height, 4rem) + var(--dock-safe-area-offset, 0px) + env(safe-area-inset-bottom, 0px));
      inset-block-start: auto;
      display: block;
      z-index: var(--z-shell-topbar, 30);
    }[data-dashboard-customizer-dropdown].dropdown-open > [data-dashboard-customizer-content],
[data-dashboard-customizer-popover] .dropdown-content,
[data-dashboard-customizer-content] {
      position: static;
      min-width: 0;
      border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
      border-block-end: none;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      overscroll-behavior-y: contain;
    }
  }
/* A pinned session rail can leave a desktop viewport with a tablet-width workbench. Keep the stage primary by querying its actual container rather than the browser viewport. */
@container (max-width: 64rem) {
  .bao-editor-workbench > .bao-editor-canvas-column {
    order: 0;
    width: 100%;
    min-width: 100%;
    height: auto;
    max-height: none;
    flex: 1 1 auto;
  }
  .bao-editor-workbench > [data-editor-canvas-overlays] {
    order: 1;
    width: 100%;
    min-width: 100%;
    height: auto;
    flex: 0 1 auto;
  }
  .bao-editor-workbench .bao-editor-canvas[data-state="empty"] {
    min-height: min(48vh, 30rem);
    max-height: min(48vh, 30rem);
    flex: 0 0 auto;
  }
}
[data-editor-inspector-disclosure] > summary {
  flex: none;
  min-block-size: var(--touch-target-min, 2.75rem);
  align-items: center;
}

@container (max-width: 48rem) {
  .bao-editor-workbench {
    position: relative;
    overflow: hidden;
    --editor-command-bar-block-size: calc(var(--touch-target-min, 2.75rem) + 3 * var(--space-inline, 0.5rem));
  }
  .bao-editor-workbench > .bao-editor-canvas-column,
  [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-workbench > .bao-editor-3d-column {
    order: 0;
    inline-size: 100%;
    min-inline-size: 100%;
    min-block-size: 0;
    block-size: 100%;
    max-block-size: 100%;
    flex: 1 1 auto;
  }
  [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-3d-viewport:not([data-editor-3d-collapsed="true"]) {
    min-block-size: 0;
    block-size: 100%;
    max-block-size: 100%;
    flex: 1 1 auto;
  }
  [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-workbench > .bao-editor-3d-column[data-editor-3d-collapsed="true"] {
    min-block-size: 0;
    block-size: auto;
    max-block-size: none;
    flex: 0 0 auto;
    align-self: flex-start;
  }
  .bao-editor-workbench > [data-editor-canvas-overlays] {
    display: none;
  }
  .bao-editor-workbench > [data-editor-canvas-overlays][data-inspector-expanded="true"] {
    position: absolute;
    inset-inline: 0;
    inset-block-end: calc(var(--editor-command-bar-block-size) + var(--space-micro, 0.25rem));
    z-index: var(--z-overlay, 1200);
    display: flex;
    inline-size: 100%;
    min-inline-size: 100%;
    max-block-size: var(--editor-compact-sheet-max-block, min(32dvh, 14rem));
    block-size: auto;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    border: 1px solid var(--liquid-border-regular);
    border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
    background: color-mix(in oklab, var(--bg-panel) calc(var(--liquid-fill-transient) * 100%), transparent);
    backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
    -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
    box-shadow: var(--shadow-glass-floating), var(--shadow-glass-inner);
    padding: var(--space-micro, 0.25rem);
  }
  .bao-editor-workbench > [data-editor-canvas-overlays][data-inspector-expanded="true"] > details,
  .bao-editor-workbench > [data-editor-canvas-overlays][data-inspector-expanded="true"] > [data-editor-filmstrip-disclosure] {
    flex: 0 0 auto;
  }
}




  /*
   * Dimension view modes (annotation-session / voice set_editor_dimension):
   * 2d focus hides the 3D column; 3d focus hides the 2D canvas column; dual
   * keeps both (default). Driven by data-editor-view-mode on chrome host.
   */
  [data-editor-chrome][data-editor-view-mode="2d"] .bao-editor-3d-column {
    display: none;
  }/*
   * 3D focus may hide the 2D stage ONLY when the 3D column can actually
   * paint. tokens-layout-editor-surfaces hides .bao-editor-3d-column while
   * chrome is not [data-annotation-ready="true"] (no image on the stage),
so
   * an ungated hide here put BOTH columns at display:none and the workbench
   * rendered zero visible children — measured live on /capture,
visible
   * workbench children went 1 -> 0 the moment view-mode="3d" was stamped with
   * no image loaded. That is the "3D geometry is blank" report.
   *
   * mount-lifecycle stamps annotationReady="true" exactly when the canvas
   * host reaches state "success",
so this gate already excludes the
   * empty/error canvas states without needing :has().
   */
  [data-editor-chrome][data-editor-view-mode="3d"][data-annotation-ready="true"] .bao-editor-canvas-column {
    display: none;
  }
  [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-3d-column {
    flex: 1 1 100%;
    min-width: 100%;
  }/*
   * Desktop dual-pane only: when the inspector is actually live (annotation-ready
   * + side-by-side >64rem),
provider/model chips leave the status strip to avoid
   * dual chrome. Idle empty stage keeps chips so model provenance stays visible
   * while the AI review form is zero-geometry (pointer-events/hidden until ready).
   * Phone + tablet stack the inspector (≤64rem) — always keep chips in the HUD.
   */
  @media (min-width: 64.01rem) {[data-page-type="media"] [data-editor-chrome][data-annotation-ready="true"] [data-editor-status="ai-provider"],
[data-page-type="media"] [data-editor-chrome][data-annotation-ready="true"] [data-editor-status="ai-model"] {
      display: none;
    }
  }

  /* 3D column mirrors the canvas column flex contract in the workbench. */
  .bao-editor-workbench > .bao-editor-3d-column {
    display: flex;
    min-width: min(100%, var(--editor-3d-basis, 28rem));
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    flex: 1 1 var(--editor-3d-basis, 28rem);
    flex-direction: column;
    gap: var(--space-inline);
  }/* Viewport region mirrors the 2D canvas geometry: bounded,
clipped,
full-height inside its column. Glass stays on the chrome only. */
  .bao-editor-3d-viewport {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    max-width: 100%;
    min-height: max(min(24rem, var(--editor-canvas-height, clamp(26rem, 58vh, 44rem))), min(18rem, 50vh));
    max-height: 100%;
    flex: 1 1 auto;
    height: 100%;
    overflow: hidden;
  }
  /* Stage chrome sticky-in-flow — clear glass media controls over the scene. */
  .bao-editor-3d-viewport > [data-editor-3d-stage-header] {
    position: sticky;
    top: 0;
    z-index: var(--z-canvas-overlay, 30);
    flex: 0 0 auto;
    width: 100%;
  }
  /* The WebGL canvas fills the region; OrbitControls owns its gestures. */
  .bao-editor-3d-viewport > canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    touch-action: none;
    cursor: grab;
    outline: none;
  }
  .bao-editor-3d-viewport > canvas:active {
    cursor: grabbing;
  }
  /* Keyboard focus ring on the region (roving tabindex contract). */
  .bao-editor-3d-viewport:focus-visible {
    outline: 2px solid var(--color-primary, var(--accent-primary, currentColor));
    outline-offset: -2px;
  }/* State machine: exactly one state block visible at a time. */
  .bao-editor-3d-viewport [data-editor-3d-loading],
.bao-editor-3d-viewport [data-editor-3d-empty],
.bao-editor-3d-viewport [data-editor-3d-error] {
    display: none;
  }.bao-editor-3d-viewport[data-state="loading"] [data-editor-3d-loading],
.bao-editor-3d-viewport[data-state="empty"] [data-editor-3d-empty],
.bao-editor-3d-viewport[data-state="error"] [data-editor-3d-error] {
    display: flex;
    z-index: var(--z-canvas-empty, 20);
    pointer-events: auto;
  }/* Media controls only make sense on a live scene. */
  .bao-editor-3d-viewport[data-state="loading"] [data-editor-3d-controls],
.bao-editor-3d-viewport[data-state="empty"] [data-editor-3d-controls],
.bao-editor-3d-viewport[data-state="error"] [data-editor-3d-controls] {
    display: none;
  }
  /* Collapse toggle exists only for the compact sheet transformation. */
  [data-editor-3d-toggle] {
    display: none;
  }/*
   * Narrow / compact: the workbench stacks (canvas first,
3D sheet second,
* inspector third) and scrolls instead of clipping the columns.
   */
  @media (max-width: 64rem) {
    .bao-editor-workbench > .bao-editor-3d-column {
      order: 1;
      width: 100%;
      min-width: 100%;
      height: auto;
      max-height: none;
      flex: 0 1 auto;
    }

    [data-editor-chrome]:not([data-editor-view-mode="3d"]) .bao-editor-workbench > .bao-editor-3d-column .bao-editor-3d-viewport {
      min-height: min(48vh, 30rem);
      max-height: min(60vh, 36rem);
      flex: 0 0 auto;
    }
  }
  /*
   * Compact sheet: a 44px toggle collapses the 3D sheet to its stage
   * header so the 2D canvas keeps the phone stage (HIG sheet pattern).
   */
  @container (max-width: 48rem) {
    [data-editor-3d-toggle] {
      display: inline-flex;
    }
    .bao-editor-3d-viewport[data-editor-3d-collapsed="true"] {
      min-height: 0;
      max-height: none;
      height: auto;
      flex: 0 0 auto;
    }.bao-editor-3d-viewport[data-editor-3d-collapsed="true"] > canvas,
.bao-editor-3d-viewport[data-editor-3d-collapsed="true"] > [data-editor-3d-controls] {
      display: none;
    }
    .bao-editor-3d-viewport[data-editor-3d-collapsed="true"] [data-editor-3d-toggle] svg {
      transform: rotate(180deg);
    }
    [data-editor-3d-toggle] svg {
      transition: transform var(--duration-normal, 200ms) var(--easing-standard, ease);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-editor-3d-viewport [data-editor-3d-toggle] svg {
      transition: none;
    }
  }


  @media (max-width: 47.99rem) {
    .bao-editor-3d-stage-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    .bao-editor-3d-stage-actions {
      flex: 1 1 auto;
      justify-content: flex-end;
      gap: var(--space-micro, 0.25rem);
      overflow-x: auto;
      overscroll-behavior-x: contain;
      scrollbar-width: none;
      touch-action: pan-x;
    }
    .bao-editor-3d-stage-actions::-webkit-scrollbar {
      display: none;
    }
    .bao-editor-3d-stage-actions [data-editor-3d-calibration] {
      flex: 0 0 auto;
      white-space: nowrap;
    }
    [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-workbench > .bao-editor-3d-column .bao-editor-3d-viewport:not([data-editor-3d-collapsed="true"]) {
      min-height: 0;
      height: 100%;
      max-height: 100%;
      flex: 1 1 auto;
    }
  }


  @media (max-width: 47.99rem) {
    [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-workbench > [data-editor-canvas-overlays]:not([data-inspector-expanded="true"]) {
      inset-block-end: calc(
        anchor(top, var(--editor-inspector-clearance))
        + var(--touch-target-min, 2.75rem)
        + var(--space-inline, 0.5rem)
      );
    }
  }

@container (max-width: 48rem) {
  [data-editor-chrome][data-editor-view-mode="3d"][data-editor-3d-collapsed="true"] .bao-editor-workbench {
    flex-direction: column;
  }
  [data-editor-chrome][data-editor-view-mode="3d"][data-annotation-ready="true"][data-editor-3d-collapsed="true"] .bao-editor-workbench > .bao-editor-canvas-column {
    display: flex;
    order: 0;
    inline-size: 100%;
    min-inline-size: 100%;
    min-block-size: 0;
    block-size: auto;
    max-block-size: 100%;
    flex: 1 1 0;
  }
  [data-editor-chrome][data-editor-view-mode="3d"] .bao-editor-workbench > .bao-editor-3d-column[data-editor-3d-collapsed="true"] {
    order: 1;
    inline-size: 100%;
    min-inline-size: 100%;
    align-self: stretch;
  }
}



  /* HD-aligned home skeleton tracks (dashboard-like density). */
  .bao-skeleton-home-narrow {
    min-height: 7rem;
    width: 100%;
    border-radius: var(--radius-box, 1rem);
  }
  .bao-skeleton-home-wide {
    min-height: 12rem;
    width: 100%;
    border-radius: var(--radius-box, 1rem);
  }



  /**
   * Canvas region min-height from layoutSize tokens (no arbitrary min-h-*).
   */
  .bao-canvas-region {
    min-height: var(--canvas-region-min-height, clamp(18rem, 42vh, 30rem));
  }
  /**
   * Fabric stage SSOT — layout via CSS custom properties, never Fabric
   * inline width/height/position.
   */
  .bao-fabric-stage {
    position: relative;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    max-width: 100%;
    overflow: hidden;
    touch-action: none;
    user-select: none;
  }
  /**
   * Dynamic host bound published by canvas island resolveCanvasSize —
   * custom property only (smoke bans layout inline styles).
   */
  .bao-editor-canvas-host-bounded {
    min-height: 0;
    max-height: var(--editor-canvas-host-max-height, 100%);
    overflow: hidden;
  }
  /**
   * Dropdown escape hatch — fixed to viewport when open so overflow table
   * ancestors cannot clip menus. Geometry vars published by app-shell-dropdowns
   * via adopted CSS host vars (no element style=).
   * The compound selector must out-rank the later-emitted
   * ".bao-dropdown-panel { position: absolute }" glass-chrome reset — a bare
   * class ties on specificity and loses on source order (clipped 44px menus).
   */
  /* Repeated class = (0,2,0): hosts without .bao-dropdown-panel (runtime
     customizer tray) must still out-rank daisyUI's .dropdown .dropdown-content
     position:absolute, which ties a bare class and wins on order. */
  .bao-dropdown-panel-viewport.bao-dropdown-panel-viewport,
  .bao-dropdown-panel.bao-dropdown-panel-viewport {
    position: fixed;
    z-index: var(--z-dropdown, 1000);
    max-height: var(--bao-dropdown-max-height, 50vh);
    overflow-y: auto;
    top: var(--bao-dropdown-top, auto);
    bottom: var(--bao-dropdown-bottom, auto);
    left: var(--bao-dropdown-left, auto);
    right: var(--bao-dropdown-right, auto);
    /* Popover top layer: neutralize the UA's inset:0 + margin:auto centering
       so the trigger-anchored geometry vars above stay authoritative. */
    margin: 0;
  }
  .bao-fabric-stage > .canvas-container {
    position: relative;
    box-sizing: border-box;
    width: var(--bao-canvas-css-width, 100%);
    height: var(--bao-canvas-css-height, 100%);
    max-width: 100%;
    touch-action: none;
    user-select: none;
  }
  .bao-fabric-stage .lower-canvas,
  .bao-fabric-stage .upper-canvas {
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 0;
    left: 0;
    top: 0;
    box-sizing: border-box;
    width: var(--bao-canvas-css-width, 100%);
    height: var(--bao-canvas-css-height, 100%);
    max-width: 100%;
    touch-action: none;
    user-select: none;
  }
  .bao-fabric-stage .upper-canvas {
    z-index: 1;
  }
  [data-sidebar-rail][data-expanded="true"] [data-sidebar-collapsed-only][hidden],
  [data-sidebar-rail][data-expanded="false"] [data-sidebar-expanded-only][hidden],
  [data-session-rail][data-expanded="true"] [data-session-rail-collapsed-only][hidden],
  [data-session-rail][data-expanded="false"] [data-session-rail-expanded-only][hidden] {
    display: none;
  }
  /* Session-rail width: tokens-layout-session-rail-resize (user-width clamp). */
  /*
   * DaisyUI tabs-border paints the radio .tab as a ~13px control; HIG requires
   * ≥44px. Beat daisyUI with explicit touch floor on shell right-rail tabs.
   */
  /* Session rail only — dual right-rail tabs were retired; do not revive. */
  [data-session-rail] input.tab[data-right-rail-tab] {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    height: auto;
    width: auto;
    flex: 1 1 auto;
    padding-inline: var(--space-inline);
    padding-block: var(--space-micro);
    box-sizing: border-box;
  }
  /*
   * DaisyUI collapse peer radios/checkboxes default to a ~13px hit box.
   * Stretch the peer across the full collapse row so FAQ / disclosure rows
   * meet HIG 44px (structure audit small-target gate).
   */
  .collapse > input[type="radio"],
  .collapse > input[type="checkbox"] {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: 100%;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
    z-index: 1;
  }
  .bao-modal-box-shell {
    max-height: min(var(--modal-max-height), var(--vv-height, 100dvh));
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
    min-height: 0;
  }
  /*
   * Dialog chrome wash — translucent over elevated glass shell (not a second
   * glass sheet; solid only when reduced-transparency).
   */
  .bao-dialog-chrome {
    background: color-mix(in oklab, var(--color-base-100, var(--bg-panel)) 78%, transparent);
  }
  html[data-reduced-transparency="true"] .bao-dialog-chrome,
  [data-reduced-transparency="true"] .bao-dialog-chrome {
    background: var(--color-base-100, var(--bg-panel));
  }
  @media (prefers-reduced-transparency: reduce) {
    .bao-dialog-chrome {
      background: var(--color-base-100, var(--bg-panel));
    }
  }
  /*
   * Dialog footer safe-area pad — max(section token, device inset).
   * Replaces ad-hoc pb-[max(...)] and pairs with solid footer fill.
   * Button/badge chrome uses Button.bao / Badge.bao tokens only
   * (no local bao-btn-ghost-xs / bao-badge-soft substitutes).
   */
  .bao-dialog-footer-safe {
    padding-bottom: max(
      var(--space-section, 0.75rem),
      env(safe-area-inset-bottom, 0px)
    );
  }
  /* Compact status row host for integration / MCP list items. */
  .bao-data-row {
    min-width: 0;
  }
  .bao-modal-scroll { max-height: var(--modal-scroll-max-height); overflow-y: auto; }
  .bao-modal-body-scroll {
    max-height: min(
      var(--modal-body-max-height),
      calc(var(--vv-height, 100dvh) * 0.55)
    );
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    min-height: 0;
    flex: 1 1 0%;
  }
  /*
   * Multi-step dialog indicator — WebView 101 portable.
   * daisyUI .steps uses :has() for counters; Android System WebView often
   * lacks :has and collapses to a vertical text list. Re-implement with
   * counters + grid only (no :has, no nested @layer).
   */
  .bao-dialog-steps.steps {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    width: 100%;
    max-width: 100%;
    margin-block-start: var(--space-inline, 0.5rem);
    padding: 0;
    list-style: none;
    counter-reset: bao-dialog-step;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    font-size: var(--text-2xs, 0.6875rem);
    line-height: 1.15;
  }
  .bao-dialog-steps .step {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 2rem minmax(0, auto);
    place-items: center;
    text-align: center;
    min-width: 0;
    position: relative;
    counter-increment: bao-dialog-step;
    --step-bg: var(--color-base-300);
    --step-fg: var(--color-base-content);
  }
  .bao-dialog-steps .step::before {
    content: "";
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    height: 0.125rem;
    width: 100%;
    margin-inline-start: -50%;
    background-color: var(--step-bg);
    border: 0;
    z-index: 0;
  }
  .bao-dialog-steps .step:first-child::before {
    opacity: 0;
  }
  .bao-dialog-steps .step::after {
    content: counter(bao-dialog-step);
    grid-column: 1;
    grid-row: 1;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--step-bg);
    background-color: var(--step-bg);
    color: var(--step-fg);
    font-size: var(--text-2xs, 0.6875rem);
    font-weight: 600;
    line-height: 1;
  }
  .bao-dialog-steps .step.step-primary {
    --step-bg: var(--color-primary);
    --step-fg: var(--color-primary-content);
  }
  .bao-dialog-steps .bao-dialog-step-label {
    display: block;
    grid-column: 1;
    grid-row: 2;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--color-base-content, currentColor);
    opacity: var(--opacity-medium, 0.75);
  }
  /* Phones: numbered circles only — labels remain for assistive tech. */
  @media (max-width: 47.99rem) {
    .bao-dialog-steps .bao-dialog-step-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    .bao-dialog-steps.steps {
      margin-block-start: var(--space-micro, 0.25rem);
    }
  }
  /*
   * Bottom-sheet dialogs (phone). daisyUI .modal is display:grid +
   * justify-items:center — justify-content does NOT stretch the box. Use
   * justify-items + align-items (grid axes) so the sheet pins full-width to
   * the bottom instead of a floating middle slab.
   *
   * Gated to <40rem to match the `sm:` breakpoint the markup switches on. At
   * 47.99rem this band contradicted the markup across a 128px window: every
   * sheet carries sm:modal-middle, sm:modal-end or sm:hidden, so between
   * 40rem and 47.99rem the dialog was pinned bottom and stretched while the
   * box stayed a centered capped slab with its bottom corners squared off —
   * measured at 700px, border-top radius 16px against bottom radius 0.
   *
   * The box's width, max-width and max-height are deliberately NOT set here.
   * They belong to the unlayered .modal-box rule in
   * tokens-glass-containment.css: this module emits inside @layer utilities,
   * so declaring them here lost the cascade silently and left every sheet
   * capped at 100vw - 1rem regardless of what this band asked for.
   */
  @media (max-width: 39.99rem) {
    dialog.modal.modal-bottom[open],
    dialog.modal.modal-bottom.modal-open {
      align-items: end;
      justify-items: stretch;
      justify-content: stretch;
      padding: 0;
      /* Keep sheet inside the visual viewport (keyboard / browser chrome). */
      max-height: var(--vv-height, 100dvh);
    }
    dialog.modal.modal-bottom[open] .modal-box,
    dialog.modal.modal-bottom[open] .bao-modal-box-shell,
    dialog.modal.modal-bottom.modal-open .modal-box,
    dialog.modal.modal-bottom.modal-open .bao-modal-box-shell {
      margin: 0;
      border-bottom-left-radius: 0;
      border-bottom-right-radius: 0;
    }
    /*
     * Bottom-sheet scroll bodies (keyboard-shortcuts sheet, create flow)
     * end flush at the sheet edge — pad the scroll end past the
     * home-indicator safe area (Apple HIG), same token family as
     * .bao-dialog-footer-safe. Scoped to the pinned bottom-sheet band so
     * centered modals keep flush scroll geometry.
     */
    dialog.modal.modal-bottom .bao-modal-body-scroll {
      padding-block-end: calc(var(--space-section, 0.75rem) + env(safe-area-inset-bottom, 0px));
    }
  }
  /*
   * Dense create flow: description is redundant with step labels on phone.
   * Stays on the ≤47.99rem densify gate alongside the step-label clip above —
   * this is a copy-density concern, not bottom-sheet geometry, so it does not
   * follow that band down to <40rem.
   */
  @media (max-width: 47.99rem) {
    [data-case-create-dialog] .bao-dialog-chrome > p {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
  }
  /* Discrete width changes only — no layout-property animation (design §7.4). */
  .bao-motion-width {
    transition-property: none;
  }
  .bao-session-mode-tab {
    transition-property: color, background-color, border-color, opacity;
    transition-duration: var(--duration-normal, 200ms);
    transition-timing-function: var(--easing-standard);
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-motion-width, .bao-session-mode-tab {
    }
  }
  .bao-context-badge[data-context-status="unset"] {
    color: var(--color-base-content, currentColor);
    opacity: var(--opacity-medium, 0.75);
  }
  .bao-context-badge[data-context-status="set"] {
    border-color: color-mix(in oklab, var(--color-success) 50%, transparent);
  }
  .bao-context-badge[data-context-status="warning"] {
    border-color: color-mix(in oklab, var(--color-warning) 50%, transparent);
  }
  .bao-context-badge[data-context-status="error"] {
    border-color: color-mix(in oklab, var(--color-error) 50%, transparent);
  }
  /* Session-rail tab strips only — the capture mode switcher has ONE owner
     (tokens-glass-shell-capture) and must not inherit this soft wash. */
  .bao-session-tab[aria-selected="true"]:not(.bao-session-mode-tab) {
    color: var(--color-primary, oklch(var(--p)));
    background-color: color-mix(in oklab, var(--color-primary) 16%, transparent);
    border-bottom-color: var(--color-primary, oklch(var(--p)));
    font-weight: 600;
  }


/* Session-rail width + resize grip — sole owner of expanded/collapsed width.
 * Drag/keyboard publish --session-rail-user-width via setHostCssVars.
 * position:relative on the rail is required: the grip is position:absolute and
 * the content row (.relative overflow-x-clip) must NOT be the containing block
 * or inset-inline-start pins to the main column, not the rail edge.
 */
[data-session-rail] {
  position: relative;
}

.bao-session-rail-expanded {
  inline-size: clamp(
    var(--session-rail-min),
    var(--session-rail-user-width, var(--session-rail-expanded)),
    min(
      var(--session-rail-max),
      calc(
        100cqi - var(--sidebar-expanded) - var(--session-rail-main-min)
      )
    )
  );
}

/*
 * Derived from what the rail must contain, not a magic width that almost fits.
 *
 * The collapsed rail holds exactly one control — the expand toggle — and that
 * control carries the HIG touch floor (--touch-target-min) inside the rail
 * header's inline padding (--space-section, from Rail.bao `header`). The old
 * flat --session-rail-collapsed of 3rem left 4px for a 44px target sitting in
 * 12px of padding, so the button overflowed its own rail: measured on /cases
 * at 1440x900, the control ran x=1406..1450 inside a rail ending at 1440, and
 * 10px of a primary control sat outside the viewport entirely.
 *
 * max() keeps the token as the floor, so a future wider --session-rail-collapsed
 * still wins and this never silently shrinks the rail.
 */
.bao-session-rail-collapsed {
  inline-size: max(
    var(--session-rail-collapsed),
    calc(var(--touch-target-min, 2.75rem) + 2 * var(--space-section, 0.75rem))
  );
}

.bao-session-rail-resizer {
  display: none;
}

@media (min-width: 80rem) {
  [data-session-rail][data-expanded="true"] .bao-session-rail-resizer {
    position: absolute;
    z-index: var(--z-content-overlay, 10);
    inset-block: 0;
    /* Center the grip on the rail's inline-start edge (into main by half). */
    inset-inline-start: 0;
    display: block;
    /* The splitter is an <hr>, and the UA sheet gives hr an explicit
       block-size: 0. An explicit size beats inset-block stretching, so the
       grip computed 44x0 — full width, no hit area at all. auto restores the
       stretch that inset-block: 0 is meant to produce. */
    block-size: auto;
    /* HIG 44px min touch target via full-height strip; visual pill is ::before. */
    inline-size: var(--touch-target-min, 2.75rem);
    margin-inline-start: calc(-0.5 * var(--touch-target-min, 2.75rem));
    padding: 0;
    border: 0;
    background: transparent;
    cursor: col-resize;
    touch-action: none;
    pointer-events: auto;
    /* Drag must not select rail or canvas text mid-gesture. */
    user-select: none;
    -webkit-user-select: none;
    appearance: none;
    outline-offset: calc(-1 * var(--space-2xs, 0.125rem));
  }

  [data-session-rail][data-expanded="true"] .bao-session-rail-resizer::before {
    position: absolute;
    inset-inline-start: 50%;
    inset-block: calc(var(--space-section, 0.75rem) * 2);
    inline-size: var(--space-2xs, 0.125rem);
    border-radius: var(--radius-full);
    background: color-mix(in oklab, var(--color-base-content) 28%, transparent);
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--color-base-100) 72%, transparent);
    content: "";
    transform: translateX(-50%);
    pointer-events: none;
  }

  [data-session-rail][data-expanded="true"] .bao-session-rail-resizer:hover::before,
  [data-session-rail][data-expanded="true"] .bao-session-rail-resizer:focus-visible::before,
  [data-session-rail][data-session-rail-resizing="true"] .bao-session-rail-resizer::before {
    inline-size: var(--space-micro, 0.25rem);
    background: var(--color-primary);
  }

  [data-session-rail][data-expanded="true"] .bao-session-rail-resizer:focus-visible {
    outline: var(--space-2xs, 0.125rem) solid var(--color-primary);
  }
}


/*
 * Table mode (host ≥40rem) — the ONE threshold owner. Labels vanish and the
 * value span dissolves into the cell. Never re-introduce Tailwind @md:
 * container variants on these spans: their 28rem breakpoint fights this
 * module's 40rem card threshold and the 28–40rem band renders a hybrid
 * (visible label + boxless value = overprinted text, licensing serials).
 */
.bao-table-responsive-cell > .bao-table-mobile-label {
  display: none;
}
.bao-table-responsive-cell > .bao-table-mobile-value {
  display: contents;
}

/*
 * Stack into cards only on truly narrow hosts (phone rail / quarter panel).
 * 40rem keeps desktop/tablet collection tables as real tables — previous
 * 28rem threshold card-stacked mid-width widgets and crushed columns into
 * vertical character bricks (specimens/billing screenshots).
 */
@container table (width < 40rem) {
  .bao-table-responsive-head {
    display: none;
  }
  .bao-table-responsive-body {
    display: block;
  }
  .bao-table-responsive-body > * + * {
    margin-top: var(--space-inline);
  }
  /*
   * Phone card stack — bao-case-card geometry SSOT (not a parallel DOM list).
   * Cases/Evidence/collection tables share this plate under @container <40rem.
   */
  .bao-table-responsive-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    position: relative;
    content-visibility: auto;
    contain-intrinsic-size: auto 4rem;
    border-radius: var(--radius-field, 0.75rem);
    border-width: 1px;
    padding-block: var(--space-micro);
    padding-inline: var(--space-inline);
    background-color: var(--bg-panel, var(--color-base-100));
    border-color: color-mix(in oklab, var(--border-base) 88%, var(--text-primary));
    box-shadow: 0 1px 2px color-mix(in oklab, var(--text-primary) 5%, transparent);
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    /* Match .bao-case-card press/hover SSOT — solid content plane, no glass. */
    transition:
      border-color var(--duration-fast, 120ms) var(--easing-standard),
      box-shadow var(--duration-fast, 120ms) var(--easing-standard),
      transform var(--duration-fast, 120ms) var(--easing-standard);
  }
  @media (hover: hover) and (pointer: fine) {
    .bao-table-responsive-row:hover {
      border-color: color-mix(in oklab, var(--color-primary) 40%, var(--border-base));
      box-shadow: 0 2px 8px color-mix(in oklab, var(--text-primary) 8%, transparent);
      transform: translateY(-1px);
    }
  }
  .bao-table-responsive-row:active {
    transform: scale(0.99);
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-table-responsive-row {
      transition: none;
    }
    .bao-table-responsive-row:hover,
    .bao-table-responsive-row:active {
      transform: none;
    }
  }
  /* Focus rings (checkbox / row actions) must paint outside the card plate. */
  .bao-table-responsive-row:focus-within {
    overflow: visible;
    border-color: color-mix(in oklab, var(--color-primary) 40%, var(--border-base));
  }
  /* (0,2,0) beats table-mode column width utilities (w-10 etc.) so every
     stacked card row spans the card — a leaked 2.5rem column box pinned
     the selection control into an invisible 40px corner. */
  .bao-table-responsive-row > .bao-table-responsive-cell {
    width: 100%;
  }
  .bao-table-responsive-cell {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    /* Label/value separation floor: one number, read back off the cell by
       browser-card-stack-geometry-audit. tokens-layout-table-pair-gap.ts
       stops a long value's paint eating it. */
    --bao-table-pair-gap-min: var(--space-inline, 0.5rem);
    gap: var(--bao-table-pair-gap-min);
    border-width: 0;
    padding-inline: 0;
    padding-block: var(--space-micro);
    min-width: 0;
  }
  .bao-table-responsive-cell > .bao-table-mobile-label {
    display: block;
    /* Never shrink a label below its text ("Accession" → "Ac…"): fixed
       basis with wrap, so long labels break to a second line instead. */
    flex: 0 0 auto;
    max-width: 40%;
    overflow-wrap: anywhere;
  }
  .bao-table-responsive-cell > .bao-table-mobile-value:not(.bao-table-identity-value):not(.bao-table-row-actions),
  .bao-table-responsive-cell > span:last-child:not(.bao-table-identity-value):not(.bao-table-row-actions) {
    display: block;
    min-width: 0;
    max-width: 70%;
    overflow-wrap: anywhere;
    text-align: end;
  }
  /* Identity stacks: full-width start-aligned (beats span:last-child end). */
  .bao-registry-table .bao-table-responsive-cell > .bao-table-mobile-value.bao-table-identity-value,
  .bao-table-responsive-cell > .bao-table-mobile-value.bao-table-identity-value {
    display: block;
    min-width: 0;
    max-width: 100%;
    width: 100%;
    flex: 1 1 100%;
    overflow-wrap: anywhere;
    text-align: start;
  }
  /* Action cells with forms: full-width control cluster on phone cards. */
  .bao-table-responsive-cell:has(.bao-table-row-actions) > .bao-table-mobile-label,
  .bao-table-responsive-cell:has([data-table-row-actions]) > .bao-table-mobile-label,
  .bao-table-responsive-cell:has([data-operator-role-form]) > .bao-table-mobile-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  /* Keep primary selector first so compliance scans still see max-width nearby. */
  .bao-table-responsive-cell > .bao-table-mobile-value.bao-table-row-actions {
    max-width: 100%;
    width: 100%;
    flex: 1 1 100%;
    text-align: start;
  }
  .bao-table-responsive-cell:has([data-table-row-actions]) > .bao-table-mobile-value,
  .bao-table-responsive-cell:has([data-operator-role-form]) > .bao-table-mobile-value,
  .bao-table-responsive-cell:has(form[hx-post]) > .bao-table-mobile-value {
    max-width: 100%;
    width: 100%;
    flex: 1 1 100%;
    text-align: start;
  }
  /* Card-stack CTAs: stretch primary row actions full-width on phone cards. */
  .bao-table-responsive-cell:has([data-table-row-actions]) .bao-table-row-actions .btn,
  .bao-table-responsive-cell:has([data-table-row-actions]) [data-table-row-actions] .btn,
  .bao-table-responsive-cell:has([data-table-row-actions]) > .bao-table-mobile-value > .btn,
  .bao-table-responsive-cell:has([data-table-row-actions]) > .bao-table-mobile-value > a.btn {
    width: 100%;
    justify-content: center;
  }
  /*
   * Join groups share the row instead of stacking 100% widths: two
   * width:100% CTAs in a flex-nowrap join measure 2x the card, and
   * justify-end spills the first button off the inline-start edge (billing
   * Accept clipped 71px past the card's left edge). Equal flex shares keep
   * one horizontal join row at exactly the card width.
   */
  .bao-table-responsive-cell:has([data-table-row-actions]) .bao-table-row-actions .join,
  .bao-table-responsive-cell:has([data-table-row-actions]) .bao-table-row-actions [data-billing-actions],
  .bao-table-responsive-cell:has([data-table-row-actions]) [data-table-row-actions] .join {
    width: 100%;
  }
  .bao-table-responsive-cell:has([data-table-row-actions]) .bao-table-row-actions .join .btn,
  .bao-table-responsive-cell:has([data-table-row-actions]) .bao-table-row-actions [data-billing-actions] .btn,
  .bao-table-responsive-cell:has([data-table-row-actions]) [data-table-row-actions] .join .btn {
    width: auto;
    flex: 1 1 0%;
    min-width: 0;
  }
  .bao-table-responsive-row > .bao-table-responsive-cell:has([data-operator-role-form]),
  .bao-table-responsive-row > .bao-table-responsive-cell:has(.bao-table-row-actions form) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-micro);
  }
  .bao-table-responsive-cell > .bao-table-mobile-value-start {
    text-align: start;
  }
  /*
   * Identity stacks (name + mono id secondary): card mode must not end-align
   * into a 70% track — long technical ids clipped. Label above, value full
   * width start-aligned for readable multi-line identity.
   */
  .bao-table-responsive-row > .bao-table-responsive-cell:has(> .bao-table-identity-value) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-micro);
  }
  /* Card mode end-alignment of .bao-table-cell-stack, and the clamp that keeps
     its overhang out of the label, live together in
     tokens-layout-table-pair-gap.ts — the alignment is what creates the
     overhang, so one module owns both halves. */
  /* Card mode: the bulk-select control pins to the card corner instead of
     burning a full label/value row on an empty stripe (iOS edit affordance). */
  .bao-table-responsive-row > td:has([data-row-select]) {
    display: flex;
    position: absolute;
    inset-block-start: var(--space-micro);
    inset-inline-end: var(--space-micro);
    justify-content: flex-end;
    border-width: 0;
    padding: 0;
    width: auto;
    z-index: 1;
  }
  /*
   * Pin geometry must not let label.sr-only participate in hit-testing —
   * flex + unclipped sr-only expands the corner box and occludes the
   * accession control center (structure: occluded-control @phone-narrow).
   */
  .bao-table-responsive-row > td:has([data-row-select]) > label.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    pointer-events: none;
  }
  .bao-table-responsive-row > td:has([data-row-select]) > [data-row-select] {
    position: relative;
    z-index: 1;
  }
  /*
   * HIG 44pt touch floor in card mode: the daisyUI .checkbox paints a
   * 1.5rem (24px) box — under the 44pt minimum. Grow the TAP area to
   * --touch-target-min with a transparent ::after hit slop (same floor
   * mechanism as .bao-touch-target); the visual control and desktop table
   * density are unchanged. The card plate clips overflow, so the slop
   * reaches the card edge (never past it) on the pinned sides and extends
   * inward where the first content row already reserves
   * --touch-target-min clearance. ::after is free — daisyUI draws the
   * checkmark on ::before.
   */
  .bao-table-responsive-row > td:has([data-row-select]) > [data-row-select]::after {
    content: "";
    position: absolute;
    inset-block-start: calc(-1 * var(--space-micro, 0.25rem));
    inset-inline-end: calc(-1 * var(--space-micro, 0.25rem));
    inset-block-end: calc(var(--size, 1.5rem) + var(--space-micro, 0.25rem) - var(--touch-target-min, 2.75rem));
    inset-inline-start: calc(var(--size, 1.5rem) + var(--space-micro, 0.25rem) - var(--touch-target-min, 2.75rem));
  }
  /* First content row clears the pinned control so values never slide under it. */
  .bao-table-responsive-row > td:has([data-row-select]) + td > .bao-table-mobile-value,
  .bao-table-responsive-row > td:has([data-row-select]) + td > span:last-child {
    padding-inline-end: var(--touch-target-min, 2.75rem);
  }
  /* Action cells: keep Accept/Reject/Capture on one horizontal join row. */
  .bao-table-responsive-cell .join,
  .bao-table-responsive-cell [data-billing-actions],
  .bao-table-responsive-cell [data-pathologist-workflow] {
    display: inline-flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0;
    max-width: none;
  }
  /*
   * Card stack densify (phone / quarter panel): demotable columns (captures,
   * updated, secondary metrics) already hide on tablet tables — mirror that
   * here so each case card is accession+patient+status+priority+actions only
   * (Apple Mail / Files density; labels stay on primary rows).
   *
   * Specificity must beat the card-mode responsive-cell display:flex rule
   * and zebra pin paints — triple-class + element form is intentional (LDL:
   * a weaker selector left Captures/Updated visible on phone cards).
   */
  tr.bao-table-responsive-row > td.bao-table-column-demotable.bao-table-responsive-cell,
  .bao-table-responsive-row > .bao-table-column-demotable.bao-table-responsive-cell {
    display: none;
  }
  /*
   * Pinned action cells un-stick in card-stack mode (Table.bao
   * bao-table-pinned-unstick token). ONE threshold owner: the retired
   * Tailwind @max-md: variant resolved at 28rem and fought this 40rem
   * threshold, leaving a stuck cell + orphan divider inside stacked cards.
   *
   * Zebra / pin plate background is table-mode chrome only — a solid
   * base-200 band under the action join on even cards reads as a grey
   * swipe rail (visual defect on cases/evidence phone stacks).
   */
  .bao-table-pinned-unstick {
    position: static;
  }
  tr.bao-table-responsive-row > td.bao-table-pinned-action,
  tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row:nth-child(even) > .bao-table-pinned-action,
  .table > tbody > tr.bao-table-responsive-row:hover > .bao-table-pinned-action,
  .table > tbody > tr.bao-table-responsive-row:focus-within > .bao-table-pinned-action {
    --bao-table-pinned-action-background: transparent;
    background-color: transparent;
    background-image: none;
    box-shadow: none;
  }
  /* Action row: icon join is self-describing — hide "Actions" mobile label. */
  .bao-table-responsive-cell:has(.join) > .bao-table-mobile-label,
  .bao-table-responsive-cell:has([data-pathologist-workflow]) > .bao-table-mobile-label,
  .bao-table-responsive-cell:has([data-billing-actions]) > .bao-table-mobile-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

/*
 * Select-all touch floor (table mode): the head hides in card mode, so
 * select-all is only reachable on touch in table mode (coarse-pointer
 * tablets). Symmetric ::after hit slop grows the 1.5rem checkbox tap area
 * to --touch-target-min on coarse pointers only — fine-pointer desktop
 * density is untouched (visual control unchanged either way).
 */
@media (pointer: coarse) {
  .bao-table-responsive-head [data-table-select-all]::after {
    content: "";
    position: absolute;
    inset: calc((var(--size, 1.5rem) - var(--touch-target-min, 2.75rem)) / 2);
  }
}

/*
 * Scroll-edge affordance: one shared owner — .bao-scroll-edge-x in
 * tokens-utilities.ts. The wrap only pins its surface color.
 */
.bao-table-scroll-x {
  --scroll-edge-bg: var(--color-base-100, var(--bg-panel));
}

/*
 * Tablet band (40–64rem host): secondary columns demote instead of clipping
 * mid-badge under the scroll edge (design.md §8.3 — no half-crushed columns;
 * the card threshold below 40rem owns narrow hosts). Marked cells carry
 * .bao-table-column-demotable from the row/head .bao templates. Column floor
 * relaxation lives with the floor owner (tokens-layout-registry-surfaces.ts).
 */
@container table (min-width: 40rem) and (max-width: 64rem) {
  .bao-table-column-demotable {
    display: none;
  }
}

/*
 * Viewport dual-path for card densify — container queries own panel reflow,
 * but phone shells also need a reliable viewport gate when a table sits
 * inside a nested scroll host whose CQ context is ambiguous. Never hide
 * demotable columns on tablet/desktop viewports (real tables keep them
 * until the container band above demotes).
 */
@media (max-width: 39.99rem) {
  tr.bao-table-responsive-row > td.bao-table-column-demotable.bao-table-responsive-cell,
  .bao-table-responsive-row > .bao-table-column-demotable.bao-table-responsive-cell {
    display: none;
  }
  tr.bao-table-responsive-row > td.bao-table-pinned-action,
  tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row:nth-child(even) > .bao-table-pinned-action {
    --bao-table-pinned-action-background: transparent;
    background-color: transparent;
    background-image: none;
    box-shadow: none;
  }
}

/*
 * Identity stacks (name + mono secondary): always start-align, even when
 * card-stack container queries are ambiguous. Attribute selector beats
 * generic mobile-value end-align without !important.
 */
[class~="bao-table-identity-value"] {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  text-align: start;
}

/*
 * Empty rows in TABLE mode — keep table-row / table-cell so HTML colspan
 * spans every column. Forcing display:block here (previous bug) made the
 * empty cell shrink to the first column track (~88px tall brick).
 */
.bao-table-responsive-empty-cell,
.bao-table-empty-row > td,
.bao-table-responsive-row[data-state="empty"] > td,
tr[data-state="empty"].bao-table-responsive-row > td {
  border-width: 0;
  padding-inline: var(--space-inline);
  padding-block: var(--space-inline);
  box-sizing: border-box;
  text-align: center;
  vertical-align: middle;
}
.bao-table-empty-panel {
  display: flex;
  width: 100%;
  max-width: 36rem;
  min-height: 6rem;
  margin-inline: auto;
  box-sizing: border-box;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-micro, 0.25rem);
  padding-inline: var(--space-section);
  padding-block: var(--space-inline, 0.5rem);
  text-align: center;
}

/*
 * Empty rows in CARD-STACK mode — colspan is ignored under display:grid /
 * display:block body, so force a full-bleed block panel.
 */
@container (width < 40rem) {
  .bao-table-responsive-empty-cell,
  .bao-table-empty-row > td,
  .bao-table-responsive-row[data-state="empty"] > td,
  tr[data-state="empty"].bao-table-responsive-row > td {
    display: block;
    width: 100%;
    max-width: 100%;
    grid-column: 1 / -1;
  }
  .bao-table-responsive-row[data-state="empty"],
  .bao-table-empty-row,
  tr[data-state="empty"].bao-table-responsive-row {
    display: block;
    width: 100%;
    min-width: 0;
    padding: var(--space-inline);
    border-radius: var(--radius-box);
    border-width: 1px;
    border-style: solid;
    background-color: var(--color-base-100, var(--bg-panel));
    border-color: var(--border-base, color-mix(in oklab, var(--color-base-content) 12%, transparent));
    box-shadow: none;
  }
}

/*
 * Billing decision row chrome (S04/S20): solid surfaces + semantic border,
 * never opacity-* wash over row text (design.md §2.10).
 */
[data-billing-decision="accepted"] {
  border-color: var(--color-success, var(--status-success));
  background-color: color-mix(in oklab, var(--color-success, var(--status-success)) 8%, var(--color-base-100, var(--bg-panel)));
}
[data-billing-decision="rejected"] {
  border-color: var(--border-base, color-mix(in oklab, var(--color-base-content) 12%, transparent));
  background-color: var(--color-base-200, var(--bg-surface));
  color: var(--color-base-content);
}
[data-billing-decision="rejected"] .text-base-content,
[data-billing-decision="rejected"] td {
  color: inherit;
}

/*
 * Pagination join SSOT — phone content columns are ~20rem; five 44px
 * touch targets + labels already press the edge. Controls own max-width
 * + horizontal scroll so page numbers never paint past #app-main (HIG
 * dense toolbar; never stretch join-items with flex-1).
 */
[data-pagination] [data-api-registry-page-buttons],
[data-pagination] .join,
[data-primitive="Pagination"] .join {
  max-width: 100%;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
[data-pagination] [data-api-registry-page-buttons]::-webkit-scrollbar,
[data-pagination] .join::-webkit-scrollbar,
[data-primitive="Pagination"] .join::-webkit-scrollbar {
  display: none;
}
@media (max-width: 39.99rem) {
  /* Phone: prev/current/next only — hide non-current page jumps to free
     the strip for 44px targets without horizontal thrash. */
  [data-pagination] button[data-api-registry-page-number]:not([aria-current="page"]),
  [data-primitive="Pagination"] button[data-api-registry-page-number]:not([aria-current="page"]) {
    display: none;
  }
  [data-pagination] .join > span[aria-hidden="true"],
  [data-primitive="Pagination"] .join > span[aria-hidden="true"] {
    display: none;
  }
  /* Viewport dual-path: identity stacks when CQ host is ambiguous. */
  .bao-table-responsive-row > .bao-table-responsive-cell:has(> .bao-table-identity-value) {
    flex-direction: column;
    align-items: stretch;
  }
  .bao-table-responsive-cell > .bao-table-mobile-value.bao-table-identity-value {
    max-width: 100%;
    width: 100%;
    text-align: start;
  }
}



/*
 * Column resize (Table.bao resizeHandle/resizeColResizing tokens).
 * The handle is an APG separator pinned to the th end edge; width travels
 * via --col-width host vars (css-host-vars.ts — never inline style=).
 * table-layout: fixed engages only after the first explicit width so
 * unsized tables keep the daisyUI auto layout.
 */
.bao-registry-table[data-table-resized] {
  table-layout: fixed;
}
.bao-registry-table[data-table-resized] > thead > tr > th[data-col-resizable] {
  width: var(--col-width, auto);
  min-width: var(--col-min, 4rem);
  max-width: var(--col-max, 32rem);
}
.bao-registry-table thead > tr > th:has(> .bao-table-resize-handle) {
  position: relative;
}
.bao-table-resize-handle {
  position: absolute;
  inset-block: 0;
  inset-inline-end: calc(-1 * var(--space-micro, 0.25rem));
  width: var(--space-inline, 0.5rem);
  padding: 0;
  border: 0;
  background: transparent;
  cursor: col-resize;
  touch-action: none;
  z-index: 1;
}
.bao-table-resize-handle::after {
  content: "";
  position: absolute;
  inset-block: 20%;
  inset-inline-start: 50%;
  width: 2px;
  translate: -50% 0;
  border-radius: var(--radius-selector, 0.25rem);
  background: color-mix(in oklab, var(--text-primary) 18%, transparent);
  transition: background var(--duration-fast, 120ms) var(--easing-standard);
}
@media (pointer: coarse) {
  /* Coarse-pointer hit floor: widen the grab strip to the touch target. */
  .bao-table-resize-handle {
    width: var(--touch-target-min, 2.75rem);
    inset-inline-end: calc(-0.5 * var(--touch-target-min, 2.75rem));
  }
}
.bao-table-resize-handle:hover::after,
.bao-table-resize-handle:focus-visible::after,
.bao-table-resize-handle.bao-table-resize-handle-active::after {
  background: var(--color-primary);
}
.bao-table-resize-handle:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}
@media (prefers-reduced-motion: reduce) {
  .bao-table-resize-handle::after {
    transition: none;
  }
}
/* Column boundary feedback while a drag/keyboard resize is in flight. */
.bao-registry-table thead > tr > th.bao-table-col-resizing {
  box-shadow: inset -2px 0 0 var(--color-primary);
}

/*
 * Multi-sort (Table.bao sortControl/sortIndicator/sortPrecedence tokens).
 * The control wraps the existing header copy (label = column name); the
 * indicator glyph and precedence badge are decorative siblings exposed via
 * aria-sort on the columnheader.
 */
.bao-table-sort-control {
  display: inline-flex;
  align-items: center;
  gap: var(--space-micro, 0.25rem);
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  /*
   * Never inherit a muted thead / text-label colour — axe color-contrast on
   * the label span (button > span:nth-child(1)) failed /cases and dashboard
   * when inherit resolved to ~2.x:1 against the header plate.
   */
  color: var(--color-base-content);
  text-align: start;
  cursor: pointer;
  user-select: none;
}
/* Explicit label span: first child is the column name from decorateHeader. */
.bao-table-sort-control > span:first-child {
  color: var(--color-base-content);
  opacity: 1;
}
.bao-table-sort-control:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-selector, 0.25rem);
}
.bao-table-sort-indicator {
  flex: 0 0 auto;
  font-size: var(--text-2xs, 0.6875rem);
  line-height: 1;
  color: var(--color-primary);
}
th[aria-sort="none"] .bao-table-sort-indicator,
.bao-table-sort-control > span.bao-table-sort-indicator {
  /*
   * WCAG 1.4.11 non-text ≥3:1 vs base-100. Full base-content (not a muted
   * mix) so text-contrast rules that treat glyphs as text also pass.
   */
  color: var(--color-base-content);
}
.bao-table-sort-precedence {
  flex: 0 0 auto;
  min-width: 1em;
  padding-inline: var(--space-micro, 0.25rem);
  border-radius: var(--radius-selector, 0.25rem);
  background: var(--color-primary);
  color: var(--color-primary-content);
  font-size: var(--text-2xs, 0.6875rem);
  line-height: 1.4;
  text-align: center;
  tabular-nums: 1;
}
/* End-aligned action headers keep their control at the inline end. */
th.text-end > .bao-table-sort-control {
  justify-content: flex-end;
}

/*
 * Inline edit (Table.bao inlineEditCell/inlineEditEditor/inlineEditEditing
 * tokens). The editor swaps into the cell, inherits cell typography, and
 * keeps focus rings inside the table density.
 */
td.bao-table-inline-edit-cell {
  cursor: default;
}
td.bao-table-inline-editing {
  padding-block: var(--space-micro, 0.25rem);
}
.bao-table-inline-editor {
  width: 100%;
  min-width: 0;
  font: inherit;
  color: inherit;
  background: var(--color-base-100);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-field, 0.5rem);
  padding-inline: var(--space-inline, 0.5rem);
  padding-block: var(--space-micro, 0.25rem);
}
.bao-table-inline-editor:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

/*
 * Long-press selection mode (Table.bao selectionModeActive token).
 * Coarse pointers suppress the iOS callout/text magnifier over selectable
 * rows so the 500ms gesture is not preempted; selected-row tint follows the
 * same base-200 fill the desktop rowSelected token paints.
 */
@media (pointer: coarse) {
  [data-table-selection-host] tbody {
    -webkit-touch-callout: none;
  }
  [data-table-selection-host].bao-table-selection-mode tbody {
    user-select: none;
    -webkit-user-select: none;
  }
}
[data-table-selection-host].bao-table-selection-mode tr[data-row-id] {
  cursor: pointer;
}
.bao-registry-table > tbody > tr:has(> td [data-row-select]:checked) > td {
  background-color: var(--color-base-200);
}

/* Inline-edit activator is a real button — meet SC 2.5.8 (24) and HIG 44. */
[data-inline-edit-activator],
td.bao-table-inline-edit-cell > button,
td.bao-table-inline-edit-cell > [role="button"] {
  min-height: var(--touch-target-min, 2.75rem);
  min-width: var(--touch-target-min, 2.75rem);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  padding-block: var(--space-micro, 0.25rem);
  padding-inline: var(--space-inline, 0.5rem);
  line-height: 1.25;
  height: auto;
}



@container table (width < 40rem) {
  /*
   * Card mode: a start-aligned flex-col cell link (patient name + MRN) flips to
   * end-alignment so the value sits under the right-hand card column, matching
   * the label-left/value-right stacked convention. Table mode keeps it
   * start-aligned under its left header. This declaration is what produces the
   * inline-start overhang, so it lives beside the clamp that bounds it.
   */
  .bao-table-responsive-cell .bao-table-cell-stack {
    align-items: flex-end;
    text-align: end;
  }
  /*
   * Clamp first: a flex item whose cross size resolves to fit-content is still
   * clamped by `max-width`, so 100% pins every value descendant to the value
   * box and the `truncate` child finally ellipsizes instead of overhanging.
   * Row-action cells are excluded — their value spans host the join cluster and
   * the row overflow menu, both of which own their own width contract.
   */
  .bao-table-responsive-cell > .bao-table-mobile-value:not(.bao-table-row-actions) > *,
  .bao-table-responsive-cell > span:last-child:not(.bao-table-row-actions) > *,
  .bao-table-responsive-cell .bao-table-cell-stack > * {
    min-width: 0;
    max-width: 100%;
  }
  /*
   * Clip second, as the backstop for value content this module does not own.
   * `overflow-x: clip` pairs with a visible block axis, so nothing changes for
   * row shadows, badge rings or the pinned select control; it only forbids
   * inline-axis paint from leaving the value box. `overflow-clip-margin`
   * reserves exactly one focus ring (width + offset) so a focused cell link
   * still shows a whole ring. Where the property is unsupported the margin
   * degrades to 0 — a tighter ring, never a re-opened collision.
   */
  .bao-table-responsive-cell > .bao-table-mobile-value:not(.bao-table-row-actions),
  .bao-table-responsive-cell > span:last-child:not(.bao-table-row-actions) {
    overflow-x: clip;
    overflow-clip-margin: calc(var(--focus-ring-width, 2px) + var(--focus-ring-offset, 2px));
  }
}




  /* Borders */
  .border { border-width: 1px; border-style: solid; }
  .border-t { border-top-width: 1px; border-style: solid; }
  .border-b { border-bottom-width: 1px; border-style: solid; }
  .border-l { border-left-width: 1px; border-style: solid; }
  .border-r { border-right-width: 1px; border-style: solid; }
  .border-dashed { border-style: dashed; }

  /* Typography/target/radius vars come ONLY from tokens.bao (@layer base
     :root emission) — never re-declare them here: an unlayered duplicate
     silently forks the scale (text-2xs shipped 0.625rem vs 0.6875rem). */
  .text-2xs { font-size: var(--text-2xs); line-height: 0.875rem; }
  .text-xs { font-size: var(--text-xs); line-height: 1rem; }
  .text-sm { font-size: var(--text-sm); line-height: 1.25rem; }
  .text-base { font-size: var(--text-base); line-height: 1.5rem; }
  .text-lg { font-size: var(--text-lg); line-height: 1.75rem; }
  .text-xl { font-size: var(--text-xl); line-height: 1.75rem; }
  .text-3xl { font-size: var(--text-3xl); line-height: 2.25rem; }
  .font-medium { font-weight: 500; }
  .font-semibold { font-weight: 600; }
  .font-bold { font-weight: 700; }
  /* Typography SSOT — tokens.bao font stacks must reach the document. */
  .font-sans { font-family: var(--font-sans); }
  .font-brand { font-family: var(--font-brand); }
  .font-mono { font-family: var(--font-mono); }
  .leading-none { line-height: 1; }
  .leading-tight { line-height: 1.25; }
  .tracking-tight { letter-spacing: var(--tracking-tight, -0.025em); }
  .tracking-wider { letter-spacing: var(--tracking-wider, 0.05em); }
  .tracking-wide { letter-spacing: var(--tracking-wide, 0.025em); }
  /* Opacity wash for body/row mute is banned (S04/S20). Disabled only. */

  /* Transitions and interactions — aliases of bao-motion-* tokens (no raw durations). */
  .transition-colors { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-timing-function: var(--easing-standard); transition-duration: var(--duration-normal); }
  .transition-transform { transition-property: transform; transition-timing-function: var(--easing-standard); transition-duration: var(--duration-normal); }
  /* Prefer .bao-motion-transition-colors in markup; this stays as TW utility alias only. */
  .disabled\:pointer-events-none:disabled { pointer-events: none; }
  .disabled\:opacity-50:disabled { opacity: var(--opacity-disabled, 0.5); }
  .placeholder\:text-base-content\/60::placeholder { color: var(--text-secondary); }
  .file\:border-0::file-selector-button { border-width: 0; }
  .file\:bg-transparent::file-selector-button { background-color: transparent; }
  .file\:text-sm::file-selector-button { font-size: var(--text-sm, 0.875rem); }
  .file\:font-medium::file-selector-button { font-weight: 500; }
  .translate-x-0 { transform: translateX(0); }
  .translate-x-4 { transform: translateX(1rem); }
  /* One pulse keyframe owner (bao-pulse-alpha, tokens-glass-motion) — the old
     duplicate 'pulse' keyframe forked the same 1↔0.5 opacity animation. */
  .animate-pulse { animation: bao-pulse-alpha var(--duration-pulse-alpha, 1000ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1)) infinite; }
  .blur { filter: blur(var(--liquid-blur-clear)); }

  /* The former responsive flex-direction "escape" blocks are gone with the
     structural duplicates that made them necessary — Tailwind's own
     breakpoint-gated variants apply again in their native order. */

  /*
   * Nested solid data panels: table wraps inside form/widget cards must not
   * re-apply card chrome (double border / double shadow). Apple clarity:
   * one surface owns the panel; nested scroll hosts stay transparent.
   */
  .bao-data-panel .bao-data-panel.overflow-x-auto,
  .card .bao-data-panel.overflow-x-auto,
  [data-dashboard-widget] .bao-data-panel.overflow-x-auto,
  [data-training-plans-panel] .bao-data-panel.overflow-x-auto,
  [data-training-jobs-table] .bao-data-panel.overflow-x-auto,
  [data-model-lifecycle-section] .bao-data-panel.overflow-x-auto {
    border-width: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  /* One owner per fact: the connector strip announces the SELECTED
     provider's state, so the selected roster row hides its duplicate
     state label (probe dots on non-selected rows stay). */
  [data-provider-selected="true"] [data-provider-state-label] {
    display: none;
  }
}

@layer utilities {
  .max-w-page-standard { max-width: var(--page-standard); }
  .max-w-page-wide { max-width: var(--page-wide); }
  .max-w-page-full { max-width: var(--page-full); }
  .max-w-page-conversation { max-width: var(--page-conversation); }
  .max-w-description { max-width: var(--description); }
  .max-w-overlay-panel { max-width: var(--overlay-panel); }
  .max-w-auth { max-width: var(--auth); }
  .max-w-form { max-width: var(--form); }
}

/*
 * Registry table structure must stay UNLAYERED: inside @layer utilities the
 * per-column width floors lose to every unlayered min-width reset and the
 * Operations column collapses to letter-wrap (cascade layers beat specificity).
 */

  /*
   * Responsive SSOT lives on these classes — not Tailwind md:hidden/md:block.
   * A later unscoped display:grid was beating md:hidden and left desktop stuck
   * on the mobile card dump with the table permanently display:none.
   */
  .bao-registry-mobile-list {
    display: grid;
    min-width: 0;
    gap: var(--space-section);
  }
  .bao-registry-desktop-scroll {
    display: none;
    min-width: 0;
    width: 100%;
  }
  @media (min-width: 48rem) {
    .bao-registry-mobile-list {
      display: none;
    }
    .bao-registry-desktop-scroll {
      display: block;
    }
  }
  .bao-registry-meta-grid {
    display: grid;
    min-width: 0;
    gap: var(--space-inline);
  }
  .bao-registry-meta-row {
    display: grid;
    min-width: 0;
    grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
    align-items: start;
    gap: var(--space-inline);
  }
  .bao-registry-mobile-entry {
    min-width: 0;
  }
  .bao-registry-table {
    width: 100%;
    /* Never force a 64rem floor — that inflated #app-main.scrollWidth on phone. */
    min-width: 0;
    max-width: 100%;
    table-layout: auto;
  }
  /* Discovery/widget card bodies must not expand main past the viewport. */
  [data-page="models"] .card-body,
  [data-page="models"] [data-widget-value] {
    min-width: 0;
    max-width: 100%;
  }
  .bao-registry-table th,
  .bao-registry-table td {
    min-width: 0;
    vertical-align: top;
  }
  /*
     The pinned cell takes the row's own background rather than recomputing
     the stripe. It used to derive base-100/base-200 from :nth-child itself,
     which made the row and its pinned cell two owners of one colour, and they
     disagreed the moment anything restyled the row: the dashboard's
     home-page case-list rule sets rows transparent at specificity (0,2,2)
     and beats daisyUI's zebra at (0,1,2), so the rows
     went flat while the Actions column carried on painting a stripe nothing
     else had — a plate of white and base-200 floating against unstriped rows,
     and a white header cell beside a base-200 header row.

     `inherit` resolves against the row's computed background, so zebra, the
     dashboard's deliberate transparency, and hover tint all reach the pinned
     cell without a second declaration to keep in sync.
  */
  .table > tbody > tr > .bao-table-pinned-action {
    background-color: inherit;
  }
  /*
     Same rule one level up: the header ROW owns the header surface and every
     header cell inherits it, pinned one included. Previously each th carried
     its own bg-base-200 while the header row stayed white, so the sticky cell
     had nothing correct to inherit and rendered white against base-200
     siblings. One owner per row removes the choice.
  */
  .table > thead > tr {
    background-color: var(--bg-surface);
  }
  .table > thead > tr > th {
    background-color: inherit;
  }
  /*
     No hover rule for the pinned cell. Hover tint belongs to the row
     (.bao-hover-row:hover, .table tbody tr:hover) and `inherit` above carries
     it into the pinned cell already. Re-mixing the tint here was the second
     half of the same two-owner bug: it composited against the recomputed
     stripe rather than against whatever the row actually painted.
  */
  /*
   * Column floors belong to TABLE mode only, so they share the card-stack
   * owner's 40rem CONTAINER threshold (tokens-layout-table-responsive.ts)
   * on the NAMED table container. The extra viewport guard closes the
   * hybrid band: at phone viewports (<48rem) the dock densify module card-
   * stacks registry rows by MEDIA query, and a 640–767px container inside
   * that band satisfied width>=40rem — floors then crushed the first
   * stacked cell to 5.5rem ("Accession" → "Ac…" on dashboard cards).
   */
  @media (min-width: 48rem) {
  @container table (width >= 40rem) {
    .bao-registry-table th:nth-child(1),
    .bao-registry-table td:nth-child(1) {
      width: 5.5rem;
      white-space: nowrap;
    }
    .bao-registry-table th:nth-child(2),
    .bao-registry-table td:nth-child(2) {
      min-width: 14rem;
    }
    .bao-registry-table th:nth-child(3),
    .bao-registry-table td:nth-child(3) {
      min-width: 9rem;
    }
    .bao-registry-table th:nth-child(4),
    .bao-registry-table td:nth-child(4),
    .bao-registry-table th:nth-child(5),
    .bao-registry-table td:nth-child(5) {
      min-width: 8rem;
    }
    .bao-registry-table th:nth-child(6),
    .bao-registry-table td:nth-child(6) {
      min-width: 8rem;
    }
    .bao-registry-table th:nth-child(7),
    .bao-registry-table td:nth-child(7),
    .bao-registry-table th:nth-child(8),
    .bao-registry-table td:nth-child(8) {
      min-width: 10rem;
    }
    .bao-registry-table th:nth-child(9),
    .bao-registry-table td:nth-child(9) {
      min-width: 12rem;
    }
    .bao-registry-table th:nth-child(10),
    .bao-registry-table td:nth-child(10) {
      min-width: 10rem;
    }
    .bao-registry-table th:nth-child(11),
    .bao-registry-table td:nth-child(11) {
      min-width: 12rem;
    }
  }
  }
  /*
   * Tablet band (40–64rem host): the floors above total far beyond the band
     (cases: 5.5+14+9+8+8+8+10 = 62.5rem before the pinned action column), so
     they force the Priority badge under the scroll clip mid-glyph. Relax to
     content-driven widths; the scroll edge still protects true overflow.
   */
  @container table (min-width: 40rem) and (max-width: 64rem) {
    .bao-registry-table th:nth-child(2),
    .bao-registry-table td:nth-child(2),
    .bao-registry-table th:nth-child(3),
    .bao-registry-table td:nth-child(3),
    .bao-registry-table th:nth-child(4),
    .bao-registry-table td:nth-child(4),
    .bao-registry-table th:nth-child(5),
    .bao-registry-table td:nth-child(5),
    .bao-registry-table th:nth-child(6),
    .bao-registry-table td:nth-child(6),
    .bao-registry-table th:nth-child(7),
    .bao-registry-table td:nth-child(7),
    .bao-registry-table th:nth-child(8),
    .bao-registry-table td:nth-child(8),
    .bao-registry-table th:nth-child(9),
    .bao-registry-table td:nth-child(9),
    .bao-registry-table th:nth-child(10),
    .bao-registry-table td:nth-child(10),
    .bao-registry-table th:nth-child(11),
    .bao-registry-table td:nth-child(11) {
      min-width: 0;
    }
  }
  .bao-registry-cell-mono {
    max-width: 20rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .bao-registry-cell-prose {
    max-width: 22rem;
    min-width: 0;
    line-height: 1.4;
    overflow-wrap: anywhere;
    word-break: break-word;
  }



/* ============================================================
   Apple Liquid Glass — base layer
   ============================================================
   All glass surfaces share these properties so the highlight ring
   and inner illumination compose consistently across variants. The
   base class is intentionally non-applied (use the named variants
   `.bao-surface-glass-regular`, `.bao-surface-glass-clear`,
   `.bao-surface-glass-strong`, `.bao-surface-glass-modal`, or
   `.bao-surface-glass-solid`).
   Enhanced with better depth, shadows, and accessibility per Apple HIG. */

.bao-surface-glass-regular,
.bao-surface-glass-clear,
.bao-surface-glass-strong,
.bao-surface-glass-modal,
.bao-surface-glass-solid {
  isolation: isolate;
  border-style: solid;
  border-width: 1px;
  /* Solid first — WebView 101 drops color-mix; translucent fills layer later. */
  background-color: var(--bg-panel);
  transition-property: background-color, border-color;
  transition-timing-function: var(--easing-glass);
  transition-duration: var(--duration-glass);
  /* IMP-017: Subtle grain/noise texture for enhanced depth perception (design.pdf §2.8) */
  background-image: var(--glass-noise-texture, none);
  background-size: var(--glass-noise-size, 128px);
  background-blend-mode: var(--glass-noise-blend, overlay);
}

/* design.pdf §2.8: grain is an enhancement, never a requirement — hosts
   flagged low-performance (product override) drop the texture entirely. */
html[data-low-performance="true"] {
  --glass-noise-texture: none;
}

/* Establish the pseudo-element containing block in the component layer so
   explicit positioning utilities retain cascade ownership. */
@layer components {
  :where(.bao-surface-glass-regular, .bao-surface-glass-clear, .bao-surface-glass-strong, .bao-surface-glass-modal) {
    position: relative;
  }
}

/*
 * daisyUI drawer-side is position:fixed (closed) / sticky (drawer-open).
 * Unlayered glass position:relative would win the cascade and leave a
 * max-content grid track on mobile, crushing #app-main. Never put glass
 * on .drawer-side — glass belongs on #app-sidebar / panel chrome only.
 */
.drawer-side.bao-surface-glass-regular,
.drawer-side.bao-surface-glass-clear {
  position: fixed;
}

/* Specular highlight ring (top edge brightness + bottom edge dim).
   The angle is set by `--lg-pointer-angle` (driven by JS, default
   135deg per Apple HIG NW light direction). The gradient uses
   mask-composite to render only as a hairline ring.
   Enhanced with better opacity values for refined depth perception. */
.bao-surface-glass-regular::before,
.bao-surface-glass-clear::before,
.bao-surface-glass-strong::before,
.bao-surface-glass-modal::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: var(--border-width-hairline, 1px);
  background: linear-gradient(
    var(--lg-pointer-angle, var(--liquid-light-angle)),
    var(--glass-highlight-strong) 0%,
    var(--glass-highlight-soft) 40%,
    var(--glass-highlight-clear) 70%,
    var(--glass-edge-fade) 100%
  );
  -webkit-mask: linear-gradient(var(--glass-mask-source) 0 0) content-box, linear-gradient(var(--glass-mask-source) 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  mix-blend-mode: screen;
  /* Pointer angle updates must be instant — transitioning background
     while --lg-pointer-angle changes every frame flashes the pane. */
  transition-property: none;
  /* Specular sits above fill, BELOW interactive children (z-index 3).
     pointer-events:none is required so chrome controls stay clickable. */
  z-index: 2;
}

/* Inner illumination — radial soft-light from upper-left that gives
   the pane its gel-like glow. Must sit BEHIND in-flow content so
   screen-blend does not wash tabpanels / rail copy.
   Enhanced with better gradient stops for refined depth (Apple HIG). */
.bao-surface-glass-regular::after,
.bao-surface-glass-clear::after,
.bao-surface-glass-strong::after,
.bao-surface-glass-modal::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(
    ellipse 120% 80% at 20% 0%,
    var(--glass-illumination-strong) 0%,
    var(--glass-illumination-soft) 50%,
    var(--glass-highlight-clear) 80%
  );
  mix-blend-mode: screen;
  opacity: 1;
  transition-property: opacity;
  transition-timing-function: var(--easing-glass);
  transition-duration: var(--duration-glass);
  z-index: 0;
}

/* Child stacking above specular: tokens-glass-fill.ts (S100 narrow hosts). */

/* ============================================================
   Regular variant — adaptive, works over any content
   ============================================================
   Apple HIG: "Regular is the most versatile and the one you will
   be using the most. This variant gives you all the visual and
   adaptive effects". Use for shell chrome (topbar, sidebar,
   rightRail, command palette, dialogs, sheets). */

/* CANON MAPPING (design.pdf §2.2/§3) — do not let these semantics drift:
   --liquid-fill-* is a TRANSPARENCY fraction, not an opacity: the panel
   alpha is (1 - tint). Canon requires dark glass to run MORE opaque than
   light (it must suppress bright content underneath), so every dark tint
   value is LOWER than its light twin in tokens.bao, and compact/coarse
   tints are lower still ("fewer blurred surfaces, stronger tint").
   Ladder (panel alpha): opacity rises clear < regular < strong < modal
   with the depth layer — a higher layer never has a weaker edge/shadow. */

.bao-surface-glass-regular {
  background: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-persistent) * 100%),
    transparent
  );
  backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
  -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
  border-color: var(--liquid-border-regular);
  /* Depth: broad soft lift + inner shadow for glass depth (Apple HIG). */
  box-shadow:
    var(--shadow-glass-elevated),
    var(--shadow-glass-inner);
}

/* design.md §10–§11: solid fallback first. Engines without backdrop-filter
   keep an opaque surface — never transparency without blur. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .bao-surface-glass-regular,
  .bao-surface-glass-clear,
  .bao-surface-glass-strong,
  .bao-surface-glass-modal,
  .bao-surface-glass-strong,
  .bao-surface-glass-lens {
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* Density modifier — apply alongside .bao-surface-glass-regular to
   scale the blur and tint for the surface footprint. Apple HIG
   says glass morphs to thicker when larger. Enhanced with inner shadow
   for consistent depth across density variants. */
.bao-surface-glass-regular.bao-glass-large {
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-regular));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-regular));
  border-color: var(--liquid-border-regular);
}
.bao-surface-glass-regular.bao-glass-small {
  backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-regular));
  -webkit-backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-regular));
  border-color: var(--liquid-border-regular);
  box-shadow: var(--shadow-glass-elevated), var(--shadow-glass-inner);
}

/* design.md §8.2 compact glass treatment: container-query blur reduction on the
   shell host (not viewport width). Blur steps reuse existing scale tokens;
   tint uses dedicated coarse tokens from tokens.bao color.liquid.
   Pair coarse-pointer tint with @media (pointer: coarse) — not width. */
@container shell (max-width: 48rem) {
  .bao-surface-glass-regular {
    backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-mobile));
    -webkit-backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-mobile));
  }
  .bao-surface-glass-regular.bao-glass-large {
    backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-mobile));
    -webkit-backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-mobile));
  }
}

@media (pointer: coarse) {
  :root {
    --liquid-fill-persistent: var(--liquid-fill-persistent-coarse);
  }
}

/* Tablet optimization: intermediate blur for balanced performance/quality */
@container shell (min-width: 48rem) and (max-width: 64rem) {
  .bao-surface-glass-regular {
    backdrop-filter: blur(var(--liquid-blur-tablet)) saturate(var(--liquid-saturate-tablet));
    -webkit-backdrop-filter: blur(var(--liquid-blur-tablet)) saturate(var(--liquid-saturate-tablet));
  }
  .bao-surface-glass-regular.bao-glass-large {
    backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-tablet));
    -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-tablet));
  }
}

/*
 * Dense multi-line hosts solidify in tokens-glass-fill.ts (pathologist
 * report, evidence lists, recent-cases) so this base stays under 400 LOC.
 */

/* ============================================================
   Clear variant — permanently transparent, requires dim layer
   ============================================================
   Apple HIG: "Clear should only be used when these 3 conditions
   are met: element is on top of media-rich content; the content
   layer won't be negatively impacted by a dimming layer; the
   content sitting above it is bold and bright." The dim layer
   (.bao-surface-glass-clear-dim) must be applied to the
   text-bearing child container so text never sits directly on
   glass. */

/* Clear consumes its own --liquid-fill-clear, NOT --liquid-fill-control.
   Sharing that token inverted the ladder: tint is a transparency fraction
   (alpha = 1 - tint), so subtle's 0.08 rendered Clear at 92% alpha — the most
   OPAQUE material in the system, above regular (84%), strong (86%) and
   elevated (88%). Clear is applied over the capture preview and annotation
   canvas, exactly where the media must read through, so the inversion was
   most visible on the worst surfaces.
   --liquid-fill-control is deliberately left alone: .bao-surface-glass-clear-dim
   below consumes it as a literal OPACITY for the text scrim, so retuning it
   would have darkened that companion in the opposite direction. */
.bao-surface-glass-clear {
  background: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-clear) * 100%),
    transparent
  );
  /*
   * The dimming layer Apple requires, composed into the Clear fill itself.
   * HIG: Clear "is permanently more transparent" and needs a dimming layer for
   * legibility. `.bao-surface-glass-clear-dim` was defined for that job and
   * shipped in ZERO partials — every Clear surface (annotation command bar,
   * canvas toolbars, 3D controls, capture preview overlay) put text straight
   * onto glass over arbitrary specimen media, so contrast was whatever the
   * slide happened to be.
   *
   * Applied as a background-image layer rather than a child element: these
   * hosts are flex containers whose children are the controls themselves, and
   * their ::before/::after already carry the specular ring and edge. A layered
   * paint needs no markup change and stacks under the content exactly as the
   * companion class would have. The a11y suppressions set `background:`
   * (shorthand), which resets background-image too, so reduced-transparency,
   * increased-contrast and forced-colors still drop it to a solid surface.
   */
  background-image: linear-gradient(
    0deg,
    color-mix(in oklab, var(--text-primary) calc(var(--liquid-fill-control) * 100%), transparent),
    color-mix(in oklab, var(--text-primary) calc(var(--liquid-fill-control) * 100%), transparent)
  );
  backdrop-filter: blur(var(--liquid-blur-clear)) saturate(var(--liquid-saturate-clear));
  -webkit-backdrop-filter: blur(var(--liquid-blur-clear)) saturate(var(--liquid-saturate-clear));
  border-color: var(--liquid-border-clear);
  box-shadow: var(--shadow-glass-clear);
}

/* Dimming companion for Clear-variant glass — apply to text containers
   that overlay rich/vibrant content. No visible effect when the parent
   is not Clear glass; safe to attach on text hosts defensively. */
.bao-surface-glass-clear-dim {
  background: color-mix(
    in oklab,
    var(--text-primary) calc(var(--liquid-fill-control) * 100%),
    transparent
  );
  border-radius: inherit;
}


@container shell (max-width: 48rem) {
  .bao-surface-glass-clear {
    backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-mobile));
    -webkit-backdrop-filter: blur(var(--liquid-blur-mobile)) saturate(var(--liquid-saturate-mobile));
  }
}

@container shell (min-width: 48rem) and (max-width: 64rem) {
  .bao-surface-glass-clear {
    backdrop-filter: blur(var(--liquid-blur-tablet)) saturate(var(--liquid-saturate-tablet));
    -webkit-backdrop-filter: blur(var(--liquid-blur-tablet)) saturate(var(--liquid-saturate-tablet));
  }
}


/* ============================================================
   Refraction lens modifier (opt-in)
   ============================================================
   Apply .bao-surface-glass-lens alongside a glass variant on hero
   surfaces (command palette, dialog, sheet). The SVG filter
   #bao-liquid-lens must be present in the document — see
   apps/server/public/bao-liquid-lens.svg (compiled from tokens.bao
   color.liquid.liquid-lens-*). Browsers without backdrop-filter
   url() support keep the preceding plain blur declaration
   (Safari/Firefox per the backdrop-filter url() spec). */

.bao-surface-glass-lens {
  backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
  -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
}

/*
 * Density + lens: .bao-glass-large is 0,2,0 and was wiping url(#bao-liquid-lens)
 * from plain .bao-surface-glass-lens (0,1,0). Dialogs use large+elevated+lens
 * per design-canon — compound selector must keep refraction + nav blur.
 */
.bao-surface-glass-regular.bao-glass-large.bao-surface-glass-lens {
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
}
.bao-surface-glass-regular.bao-glass-small.bao-surface-glass-lens {
  backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
  -webkit-backdrop-filter: blur(var(--liquid-blur-small)) saturate(var(--liquid-saturate-regular)) url(#bao-liquid-lens);
}

/* ============================================================
   Strong variant — menus, sheets, form panels (design.pdf §3)
   ============================================================
   30-55% tint opacity, 20-32px blur. Use for transient surfaces that
   need stronger visual separation from content: dropdown menus,
   bottom sheets, form panels, and contextual overlays. */

.bao-surface-glass-strong {
  background: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
    transparent
  );
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  border-color: var(--liquid-border-regular);
  box-shadow: var(--shadow-glass-floating), var(--shadow-glass-inner);
}

/* ============================================================
   Modal variant — dialogs above scrim (design.pdf §3)
   ============================================================
   50-75% tint opacity, 24-40px blur. Use for blocking dialogs and
   modals that sit above a dimming scrim. Higher opacity ensures
   content beneath the scrim cannot bleed through.
   Panel alpha consumes --liquid-fill-blocking (light 88% / dark 92%
   opaque) so the fill ladder stays monotonic —
   control < clear < persistent < persistent-coarse < transient <
   blocking — and dark glass runs MORE opaque per §2.2.
   The 88% floor is load-bearing, not decorative: browser-structure-audit
   rejects any .modal-box whose computed alpha falls under 0.82
   (scripts/lib/browser-structure-audit-chrome.mjs), because an
   enterprise/medical dialog must never let the page read through when
   backdrop-filter is suppressed. Blur rides on top of that fill so the
   material still refracts wherever the filter is live. */

.bao-surface-glass-modal {
  background: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-blocking) * 100%),
    transparent
  );
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  border-color: var(--liquid-border-regular);
  box-shadow: var(--shadow-glass-floating), var(--shadow-glass-inner);
}

/* ============================================================
   Solid variant — accessibility/performance fallback (design.pdf §3)
   ============================================================
   Opaque surface with no blur. Use when backdrop-filter is unsupported,
   disabled via prefers-reduced-transparency, or when performance
   budget prohibits blur layers. Provides visual consistency without
   the glass effect. */

.bao-surface-glass-solid {
  background: var(--bg-panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--border-base);
  box-shadow: var(--shadow-md);
}

/* Opaque fallback for modal-box with solid material. */
.modal-box.bg-base-100 {
  background-color: var(--color-base-100, var(--bg-panel));
}


/* ============================================================
   Hover-reveal — progressive disclosure for row/card actions
   ============================================================
   Fine-pointer devices: secondary actions fade in on host hover or any
   keyboard focus inside the host. Touch / no-hover devices: always
   visible (parity law — no hover-only affordances). */

@media (hover: hover) and (pointer: fine) {
  .bao-hover-reveal-host .bao-hover-reveal {
    opacity: 0;
    transition-property: opacity;
    transition-timing-function: var(--easing-standard);
    transition-duration: var(--duration-normal);
  }
  .bao-hover-reveal-host:hover .bao-hover-reveal,
  .bao-hover-reveal-host:focus-within .bao-hover-reveal {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bao-surface-glass-regular,
  .bao-surface-glass-clear,
  .bao-surface-glass-strong,
  .bao-surface-glass-lens {
    transition-duration: var(--reduced-motion-min-duration, 0.001ms);
  }
  .bao-surface-glass-regular::before,
  .bao-surface-glass-clear::before,
  .bao-surface-glass-regular::after,
  .bao-surface-glass-clear::after {
    transition-duration: var(--reduced-motion-min-duration, 0.001ms);
  }
  .bao-hover-reveal-host .bao-hover-reveal {
    transition-duration: var(--reduced-motion-min-duration, 0.001ms);
  }
}


/* ============================================================
   Reactive lensing — hover/focus strengthens the material
   ============================================================
   Ported from the baobeep glass canon: pointer hover (or keyboard
   focus-within) deepens the tint and sharpens the border so
   interactive glass reads as touchable. Material tint/edge/shadow
   states are owned HERE; transforms and the shared transition list
   are owned by tokens-glass-motion.ts (one owner per concern — the
   transition channel list there excludes blur filters, so blur never
   churns; design.md §7.4). Hover is gated to fine pointers so touch
   devices never trap a stuck hover state (design.md §6). */
@media (hover: hover) and (pointer: fine) {
  /* design.md §6/§7.4: hover is a fine-pointer enhancement — tint, edge,
     and shadow carry the interactive strengthening; blur stays fixed.
     Enhanced with inner shadow for better depth perception (Apple HIG). */
  .bao-surface-glass-reactive:hover {
    background: color-mix(
      in oklab,
      var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
      transparent
    );
    border-color: var(--liquid-border-contrast);
    box-shadow:
      var(--shadow-glass-floating, var(--shadow-glass-elevated)),
      var(--shadow-glass-inner);
  }
}

/* Focus strengthening never depends on hover capability (design.md §6).
   Enhanced with inner shadow for better visual feedback (Apple HIG). */
.bao-surface-glass-reactive:focus-within {
  background: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
    transparent
  );
  border-color: var(--liquid-border-contrast);
  box-shadow:
    var(--shadow-glass-floating, var(--shadow-glass-elevated)),
    var(--shadow-glass-inner);
}

/* ============================================================
   Disabled glass surfaces — reduced chroma/elevation (design.md §6/§2.10)
   ============================================================
   Disabled glass keeps text legible by dropping elevation and chroma
   instead of crushing opacity. Blur reduces to subtle, border fades,
   shadows flatten, and interactive states are suppressed.
   design.md §6: "disabled keeps the label readable — drop elevation/chroma
   instead of crushing text via opacity" */
.bao-surface-glass-regular:disabled,
.bao-surface-glass-regular[aria-disabled="true"],
.bao-surface-glass-clear:disabled,
.bao-surface-glass-clear[aria-disabled="true"],
.bao-surface-glass-strong:disabled,
.bao-surface-glass-strong[aria-disabled="true"],
.bao-surface-glass-modal:disabled,
.bao-surface-glass-modal[aria-disabled="true"] {
  /* Reduce blur to subtle level — maintains glass identity without visual weight */
  backdrop-filter: blur(var(--liquid-blur-clear, 8px)) saturate(var(--liquid-saturate-clear, 1.35));
  -webkit-backdrop-filter: blur(var(--liquid-blur-clear, 8px)) saturate(var(--liquid-saturate-clear, 1.35));
  /* Fade border to base-300 — reduces chroma without disappearing */
  border-color: var(--color-base-300);
  /* Flatten shadows — remove depth cues for non-interactive state */
  box-shadow: none;
  /* Suppress pointer events — no hover/focus states on disabled surfaces */
  pointer-events: none;
  /* Maintain text legibility via color-mix instead of opacity reduction */
  color: color-mix(in oklab, var(--color-base-content) 60%, var(--color-base-200));
}

/* Suppress hover/focus on disabled glass surfaces */
.bao-surface-glass-regular:disabled:hover,
.bao-surface-glass-regular[aria-disabled="true"]:hover,
.bao-surface-glass-regular:disabled:focus-within,
.bao-surface-glass-regular[aria-disabled="true"]:focus-within,
.bao-surface-glass-clear:disabled:hover,
.bao-surface-glass-clear[aria-disabled="true"]:hover,
.bao-surface-glass-clear:disabled:focus-within,
.bao-surface-glass-clear[aria-disabled="true"]:focus-within,
.bao-surface-glass-strong:disabled:hover,
.bao-surface-glass-strong[aria-disabled="true"]:hover,
.bao-surface-glass-strong:disabled:focus-within,
.bao-surface-glass-strong[aria-disabled="true"]:focus-within,
.bao-surface-glass-modal:disabled:hover,
.bao-surface-glass-modal[aria-disabled="true"]:hover,
.bao-surface-glass-modal:disabled:focus-within,
.bao-surface-glass-modal[aria-disabled="true"]:focus-within {
  /* Inherit disabled state — no visual change on interaction */
  backdrop-filter: blur(var(--liquid-blur-clear)) saturate(var(--liquid-saturate-clear));
  -webkit-backdrop-filter: blur(var(--liquid-blur-clear)) saturate(var(--liquid-saturate-clear));
  border-color: var(--color-base-300);
  box-shadow: none;
}

/* ============================================================
   Loading glass surfaces — skeleton shimmer overlay (design.md §2.10)
   ============================================================
   Loading state reduces glass clarity and adds a skeleton shimmer
   overlay to indicate async content loading. Blur increases slightly
   to soften the surface, border becomes more prominent, and a subtle
   shimmer animation sweeps across the surface. */
.bao-surface-glass-regular[data-state="loading"],
.bao-surface-glass-clear[data-state="loading"],
.bao-surface-glass-strong[data-state="loading"],
.bao-surface-glass-modal[data-state="loading"] {
  /* Increase blur to soften surface during loading */
  backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
  -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
  /* Strengthen border for better definition */
  border-color: var(--color-base-300);
  /* Add skeleton shimmer overlay via ::after pseudo-element */
  position: relative;
  overflow: hidden;
}

.bao-surface-glass-regular[data-state="loading"]::after,
.bao-surface-glass-clear[data-state="loading"]::after,
.bao-surface-glass-strong[data-state="loading"]::after,
.bao-surface-glass-modal[data-state="loading"]::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: linear-gradient(
    90deg,
    transparent 25%,
    color-mix(in oklab, var(--bg-surface-hover, var(--color-base-300)) 40%, transparent) 50%,
    transparent 75%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: bao-skeleton-sweep var(--duration-skeleton) var(--easing-standard) infinite;
  pointer-events: none;
  z-index: 1;
}

/* ============================================================
   Error glass surfaces — visual error indicator (design.md §2.10)
   ============================================================
   Error state strengthens the border with error color and adds a
   subtle error indicator. Glass maintains its blur/shadow depth
   but the border shifts to error color to signal failure state. */
.bao-surface-glass-regular[data-state="error"],
.bao-surface-glass-clear[data-state="error"],
.bao-surface-glass-strong[data-state="error"],
.bao-surface-glass-modal[data-state="error"] {
  /* Strengthen border with error color — clear visual signal */
  border-color: var(--color-error);
  border-width: 2px;
  /* Maintain glass depth — keep shadows intact */
  box-shadow:
    var(--shadow-glass-elevated, var(--shadow-md)),
    var(--shadow-glass-inner, 0 0 0 0);
}

/* ============================================================
   Glass-on-glass containment (design.md §4 / §10 / §14)
   ============================================================
   A backdrop-filter ancestor establishes a new backdrop root, so a
   nested glass surface (e.g. a dropdown panel inside the glass topbar)
   can never re-blur the page — it renders as raw transparency without
   blur, bleeding unblurred content through menu items. Contained
   surfaces inside glass therefore keep their border/shadow but paint
   as the canonical solid fill plate instead of a second material. */
.bao-surface-glass-regular .bao-surface-glass-regular,
.bao-surface-glass-regular .bao-surface-glass-clear,
.bao-surface-glass-regular .bao-surface-glass-strong,
.bao-surface-glass-regular .bao-surface-glass-modal,
.bao-surface-glass-clear .bao-surface-glass-regular,
.bao-surface-glass-clear .bao-surface-glass-clear,
.bao-surface-glass-clear .bao-surface-glass-strong,
.bao-surface-glass-clear .bao-surface-glass-modal,
.bao-surface-glass-strong .bao-surface-glass-regular,
.bao-surface-glass-strong .bao-surface-glass-clear,
.bao-surface-glass-strong .bao-surface-glass-strong,
.bao-surface-glass-strong .bao-surface-glass-modal,
.bao-surface-glass-modal .bao-surface-glass-regular,
.bao-surface-glass-modal .bao-surface-glass-clear,
.bao-surface-glass-modal .bao-surface-glass-strong,
.bao-surface-glass-modal .bao-surface-glass-modal {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: var(--glass-fill-bg, var(--bg-panel));
}

/* ============================================================
   Touch target — minimum 44x44dp per Apple HIG
   ============================================================ */

.bao-touch-target {
  min-height: 2.75rem;
  min-width: 2.75rem;
}

/* Gated hover utilities, scrim SSOT, and readable-disabled live in
   tokens-glass-hover-scrim.ts (400-LOC monolith gate). */



/* ============================================================
   Glass control — baobeep nested chrome (no glass-on-glass)
   ============================================================
   Toolbar / workflow controls nested inside a glass shell use a
   vibrancy fill without a second backdrop-filter layer. Tokens from
   resources/bao/tokens/tokens.bao (color.glass + color.liquid). */

.bao-glass-control {
  min-height: var(--touch-target-min, 2.75rem);
  min-width: var(--touch-target-min, 2.75rem);
  background-color: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-control) * 100%),
    transparent
  );
  border-color: var(--liquid-border-clear);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* iOS and iPadOS zoom the whole viewport whenever a focused form control
   computes under 16px, and this product ships into WKWebView through the
   Tauri mobile shell. daisyUI sizes -sm at 0.875rem and -xs smaller still,
   so the badge, PIN, search and settings fields all yanked the layout out
   from under the operator the moment the keyboard opened. Coarse pointer
   rather than viewport width: an iPad at desktop width zooms identically,
   and a narrow desktop window does not. Doubled class selectors outrank
   daisyUI's single-class sizing on specificity, so the floor holds on
   specificity alone — no priority override, no dependence on load order. */
@media (pointer: coarse) {
  .input,
  .select,
  .textarea,
  .file-input,
  .input.input-xs,
  .input.input-sm,
  .select.select-xs,
  .select.select-sm,
  .textarea.textarea-xs,
  .textarea.textarea-sm,
  .file-input.file-input-xs,
  .file-input.file-input-sm {
    font-size: var(--text-base);
  }
}

@media (hover: hover) and (pointer: fine) {
  .bao-glass-control:hover {
    background-color: color-mix(
      in oklab,
      var(--color-primary) 18%,
      transparent
    );
    border-color: var(--liquid-border-regular);
    box-shadow: inset 0 1px 0 var(--glass-highlight-soft);
  }
}
.bao-glass-control:focus-visible {
  background-color: color-mix(
    in oklab,
    var(--color-primary) 18%,
    transparent
  );
  border-color: var(--liquid-border-regular);
  box-shadow: inset 0 1px 0 var(--glass-highlight-soft);
  outline: 2px solid var(--color-primary, currentColor);
  outline-offset: 2px;
}

/* One dimension control morphs from a flat plane to a spatial volume. */
.bao-dimension-toggle {
  color: var(--text-primary);
}
.bao-dimension-toggle.swap-active {
  color: var(--color-primary);
  background-color: color-mix(in oklab, var(--color-primary) 20%, var(--glass-control-fill));
  border-color: color-mix(in oklab, var(--color-primary) 42%, var(--liquid-border-clear));
}
@media (prefers-reduced-motion: reduce) {
  .bao-dimension-toggle .swap-on,
  .bao-dimension-toggle .swap-off {
    transition: none;
  }
}

/* Nested soft buttons on glass shells: auto glass-control (no second frost). */
.bao-surface-glass-regular .btn-soft.bao-toolbar-action,
.bao-surface-glass-regular .btn-ghost.bao-toolbar-action,
.bao-surface-glass-regular .btn-soft:not(.btn-primary):not(.btn-error),
.bao-surface-glass-clear .btn-soft.bao-toolbar-action,
.bao-surface-glass-clear .btn-ghost.bao-toolbar-action {
  background-color: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-control) * 100%),
    transparent
  );
  border-color: var(--liquid-border-clear);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Transient menus enter the top layer so their blur samples page content, not parent glass. */
.bao-dropdown-panel:popover-open,
.bao-dropdown-panel[data-popover-open="true"] {
  background-color: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
    transparent
  );
  border-color: var(--liquid-border-regular);
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  box-shadow: var(--shadow-glass-floating);
}

/* Scroll-edge: sticky glass topbar gains separation as content scrolls under. */
#app-header.bao-surface-glass-regular {
  box-shadow:
    var(--shadow-glass-elevated, 0 8px 32px -4px var(--glass-shadow-strong), 0 2px 8px -2px var(--glass-shadow-soft)),
    0 1px 0 0 color-mix(in oklab, var(--color-base-300) 60%, transparent);
}

/* Scrolled state (data-scroll-edge from app-shell-glass.ts): deeper, broader
   shadow + stronger bottom edge while content passes beneath using the canonical scroll-edge state. */
#app-header.bao-surface-glass-regular[data-scroll-edge="true"] {
  box-shadow:
    var(--shadow-glass-floating, 0 12px 48px -8px var(--glass-shadow-strong), 0 4px 16px -4px var(--glass-shadow-soft)),
    0 1px 0 0 color-mix(in oklab, var(--color-base-300) 85%, transparent);
  backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
  -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
}

/* Page content column: reclaim vertical chrome waste from the canonical density tokens. */
.bao-page-track {
  padding-block: var(--space-micro, 0.25rem);
  padding-inline: var(--space-inline, 0.5rem);
  max-width: 100%;
}
.bao-page-header + [data-case-journey-host],
.bao-page-header + section[data-case-journey-host] {
  margin-top: 0;
}

/* Nested grid/flex hosts must not invent columns — fill the panel cell. */
.bao-grid-reactive-panels > * {
  min-width: 0;
  min-height: 0;
}
/* Widget projection: a lone card/primitive fills its panel cell. Stacked
   panel content (e.g. FilterBar above a list section) must NOT inherit the
   fill — percentage heights inside the auto-height panel inflate the first
   child and strand its controls at the bottom of a blank band. */
.bao-grid-reactive-panels > * > .card:only-child,
.bao-grid-reactive-panels > * > [data-dashboard-widget]:only-child,
.bao-grid-reactive-panels > * > [data-primitive]:only-child {
  height: 100%;
  min-height: 0;
}

/* ============================================================
   Composite chrome (LAW2) — one class replaces ≥8-token utility clones
   ============================================================ */

/* Empty / error / denied / offline host stack (page state surfaces).

   No :not([hidden]) guard and no [hidden] counter-rule: the global
   [hidden]:not([hidden="until-found"]) owner in tokens-daisyui-emission
   outranks this (0,1,0) display, so the attribute means what it says here
   without this block restating it. */
.bao-state-stack,
[data-primitive="EmptyState"],
[data-primitive="ErrorState"],
[data-primitive="DeniedState"] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-micro, 0.25rem);
  text-align: center;
  padding-block: var(--space-section);
  padding-inline: var(--space-section);
}

/* Case-queue / dropdown menu row chrome. */
.bao-menu-item {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-inline, 0.5rem);
  border-radius: var(--radius-field, 0.75rem);
  padding-inline: var(--space-section);
  padding-block: var(--space-micro, 0.25rem);
  cursor: pointer;
  min-width: 0;
}

.bao-menu-item:focus-visible {
  background-color: var(--color-base-300, var(--bg-surface));
}

/* Page header chrome edge (border + density) — layout is grid SSOT above. */
.bao-page-header {
  flex-shrink: 0;
  border-block-end: 1px solid color-mix(in oklab, var(--color-base-300, var(--border-base)) 40%, transparent);
  padding-block-end: var(--space-micro, 0.25rem);
}

/* Header action cluster. */
.bao-page-header-actions {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs, 0.125rem);
  justify-content: flex-end;
}

@media (max-width: 39.99rem) {
  .bao-page-header-actions {
    width: 100%;
  }
}

/* Card/panel body density — flex column fill with micro gap. */
.bao-panel-body,
.card-body.bao-panel-body {
  display: flex;
  min-height: 0;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--space-micro, 0.25rem);
  padding: var(--space-inline, 0.5rem);
}

/* Stat / data panel shell — card + metric host stack. */
.bao-stat-panel {
  position: relative;
  display: flex;
  min-width: 0;
  flex-direction: column;
  overflow-x: clip;
  background-color: var(--color-base-100, var(--bg-panel));
  scroll-margin-top: var(--space-section, 0.75rem);
}

.bao-stat-panel:focus-within {
  outline: none;
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--color-primary, var(--accent-primary)) 40%, transparent);
}

/* Form fieldset density for filter/search bars. */
.bao-field-stack {
  display: flex;
  min-width: 0;
  width: 100%;
  flex: none;
  flex-shrink: 0;
  flex-direction: column;
  gap: var(--space-micro, 0.25rem);
}

@media (min-width: 40rem) {
  .bao-field-stack {
    flex: 1 1 auto;
  }
}

/* Trailing action cluster in filter bars / headers. */
.bao-action-end {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-micro, 0.25rem);
  align-self: flex-end;
}

@media (max-width: 39.99rem) {
  .bao-action-end {
    width: 100%;
  }
}

/* Dialog chrome header row. */
.bao-dialog-chrome {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-inline, 0.5rem);
  padding-inline: var(--space-section);
  padding-block: var(--space-micro, 0.25rem);
  border-block-end: 1px solid var(--color-base-300, var(--border-base));
}

/* Full-width control fill (select/input density). */
.bao-control-fill {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  background-color: var(--color-base-100, var(--bg-panel));
  color: var(--color-base-content, var(--text-primary));
}

/* Sidebar nav item density chrome (gap/padding/radius). */
.bao-nav-item-chrome {
  gap: var(--space-inline, 0.5rem);
  border-radius: var(--radius-field, 0.75rem);
  padding-inline: var(--space-inline, 0.5rem);
  padding-block: var(--space-micro, 0.25rem);
}

.bao-nav-item-chrome:focus-visible {
  background-color: var(--color-base-200, var(--bg-surface));
}

/* Shell list / overflow menu row. */
.bao-shell-list-row {
  display: flex;
  width: 100%;
  min-width: 0;
  align-items: center;
  gap: var(--space-inline, 0.5rem);
  border-radius: var(--radius-field, 0.75rem);
  padding-inline: var(--space-section);
  padding-block: var(--space-micro, 0.25rem);
  text-align: start;
}

/* Responsive trailing action stack (filter footers, form actions). */
.bao-action-end-stack {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-inline, 0.5rem);
}

@media (max-width: 39.99rem) {
  .bao-action-end-stack {
    flex-direction: column;
    align-items: stretch;
  }
}

/* Card action row at panel foot. */
.bao-card-actions {
  position: relative;
  z-index: var(--z-table-action, 1);
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-micro, 0.25rem);
  padding-inline: var(--space-inline, 0.5rem);
  padding-bottom: var(--space-inline, 0.5rem);
  padding-top: 0;
  min-width: 0;
}

/* Compact bordered inline toolbar (widget tool strips). */
.bao-inline-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-micro, 0.25rem);
  border-radius: var(--radius-field, 0.75rem);
  border: 1px solid color-mix(in oklab, var(--color-base-300, var(--border-base)) 70%, transparent);
  background-color: var(--color-base-100, var(--bg-panel));
  padding: var(--space-inline, 0.5rem);
  min-width: 0;
}

/* Shell topbar overflow menu panel. */
.bao-shell-dropdown {
  z-index: var(--z-shell-topbar, 40);
  border-radius: var(--radius-box, 1rem);
  border: 1px solid var(--color-base-300, var(--border-base));
  background-color: color-mix(
    in oklab,
    var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
    transparent
  );
  backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
  padding: var(--space-micro, 0.25rem);
  width: var(--sidebar-expanded, 16rem);
  gap: var(--space-micro, 0.25rem);
}

@media (prefers-reduced-transparency: no-preference) and (prefers-contrast: no-preference) {
  html:not([data-reduced-transparency="true"]):not([data-high-contrast="true"]) .btn.bao-glass-control:not([hidden]):not(.hidden) {
    background-color: var(--glass-control-fill);
    border-color: var(--liquid-border-clear);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}


/* Pathologist workflow current step — page SSOT via data-page + session-mode.
   Capture sets data-session-mode (acquire|annotate) from app-shell-session-mode. */
[data-page="capture"][data-session-mode="annotate"] [data-pathologist-workflow-step="annotate"],
[data-page="capture"][data-session-mode="acquire"] [data-pathologist-workflow-step="capture"],
[data-page="capture"]:not([data-session-mode]) [data-pathologist-workflow-step="capture"],
[data-page="ai-analysis"] [data-pathologist-workflow-step="ai-analysis"],
[data-page="evidence"] [data-pathologist-workflow-step="evidence"],
[data-page="reports"] [data-pathologist-workflow-step="reports"],
[data-page="cases"] [data-pathologist-workflow-step="cases"] {
  border-color: color-mix(in oklab, var(--color-primary) 50%, transparent);
  background-color: color-mix(in oklab, var(--color-primary) 12%, var(--bg-panel));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-primary) 30%, transparent);
}

[data-page="capture"][data-session-mode="annotate"] [data-pathologist-workflow-step="annotate"] .badge,
[data-page="capture"][data-session-mode="acquire"] [data-pathologist-workflow-step="capture"] .badge,
[data-page="capture"]:not([data-session-mode]) [data-pathologist-workflow-step="capture"] .badge,
[data-page="ai-analysis"] [data-pathologist-workflow-step="ai-analysis"] .badge,
[data-page="evidence"] [data-pathologist-workflow-step="evidence"] .badge,
[data-page="reports"] [data-pathologist-workflow-step="reports"] .badge {
  background-color: var(--color-primary);
  color: var(--color-primary-content);
  border-color: transparent;
}

/*
 * Case-row next actions (join) — not a page content workflow panel.
 * Clinical path SSOT is shell sidebar order; row CTAs are contextual shortcuts.
 */
.bao-pathologist-journey {
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 0;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in oklab, var(--color-base-content) 28%, transparent) transparent;
}

.bao-pathologist-journey .bao-toolbar-action.btn-square,
.bao-pathologist-journey .bao-toolbar-action {
  width: auto;
}

/*
 * Progressive path CTAs: icon-first on fine pointer; labels on hover/focus
 * (design.md hover-to-reveal). Touch keeps ≥44px targets with labels hidden
 * under compact width so the row does not steal case-card geometry.
 */
.bao-pathologist-journey .bao-toolbar-label,
.bao-pathologist-journey .join-item.btn .bao-toolbar-label,
.bao-pathologist-journey .join-item.btn .text-2xs {
  display: inline-block;
  max-width: 4.5rem;
  margin-inline-start: var(--space-micro, 0.25rem);
  overflow: hidden;
  white-space: nowrap;
}
@media (hover: hover) and (pointer: fine) {
  .bao-pathologist-journey .bao-toolbar-label,
  .bao-pathologist-journey .join-item.btn .bao-toolbar-label,
  .bao-pathologist-journey .join-item.btn .text-2xs {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
  }
  .bao-pathologist-journey .join-item.btn:hover .bao-toolbar-label,
  .bao-pathologist-journey .join-item.btn:focus-visible .bao-toolbar-label,
  .bao-pathologist-journey .join-item.btn:hover .text-2xs,
  .bao-pathologist-journey .join-item.btn:focus-visible .text-2xs {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    margin-inline-start: var(--space-micro, 0.25rem);
    clip: auto;
    max-width: 4.5rem;
    opacity: 1;
  }
}

.bao-table-responsive-cell .bao-pathologist-journey,
td .bao-pathologist-journey {
  justify-content: flex-end;
  max-width: min(100%, 28rem);
}
/* Phone density: keep ≥44px targets. Labels stay visible on coarse pointers
   (design.md: essentials must not depend on hover); fine-pointer hover rules
   above still densify desktop. */
@media (max-width: 47.99rem) {
  .bao-pathologist-journey .join-item.btn {
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: var(--space-inline, 0.5rem);
  }
  @media (hover: hover) and (pointer: fine) {
    .bao-pathologist-journey .join-item.btn .text-2xs,
    .bao-pathologist-journey .join-item.btn .bao-toolbar-label {
      display: none;
    }
  }
}

/* Slim case-journey host — solid sticky strip (content plane, no glass).
   Sticky pin offset is 0 WITHIN #app-main. */
[data-case-journey-host] {
  min-width: 0;
  margin-block: 0;
  position: sticky;
  top: 0;
  z-index: calc(var(--z-sticky, 1100) + 1);
  padding-block: var(--space-2xs, 0.125rem);
  padding-inline: var(--space-micro, 0.25rem);
  border-radius: var(--radius-field, 0.5rem);
  border-block-end: 1px solid color-mix(in oklab, var(--color-base-300) 50%, transparent);
}
[data-case-journey-host] > h2 {
  display: none;
}
/* One-row journey band: rail + next-step hint share the line — a stacked
   hint row doubled the sticky band height (design.md §5.1 density). */
[data-case-journey] {
  min-width: 0;
  width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-micro, 0.25rem) var(--space-section, 0.75rem);
}
[data-case-journey] > * {
  margin-block: 0;
}
[data-case-journey] [data-case-journey-hint],
[data-case-journey] > p {
  flex: 1 1 auto;
  min-width: 8rem;
  margin: 0;
  text-align: end;
}
[data-case-journey] [data-case-journey-hint]:empty {
  display: none;
}
/*
 * text-align:end above is correct while the paragraph trails the steps rail.
 * The empty state renders no steps, so the paragraph becomes the *leading*
 * element of the row and end-alignment shoved the lone sentence up against the
 * action button, leaving a ragged gap at the inline start.
 */
[data-case-journey][data-state="empty"] > p {
  text-align: start;
}
/*
 * Dense horizontal journey — re-implement daisyUI steps as a fixed 5-track
 * grid (WebView 101 / design.md density). Caps width so connectors never
 * leave huge voids on wide workbenches.
 */
[data-case-journey] .steps,
[data-case-journey] ul.steps,
[data-case-journey] ul[data-case-journey-steps] {
  display: grid;
  /* Auto column tracks — the rail carries however many steps the journey
     declares; a fixed count would silently clip added steps. */
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  width: 100%;
  max-width: min(100%, 40rem);
  margin-inline: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  gap: 0;
  padding-inline: var(--space-micro, 0.25rem);
  min-height: 3.5rem;
  align-items: start;
  justify-items: stretch;
}
/* Journey steps — densify labels, truncate, keep touch targets. */
[data-case-journey] .step {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2xs, 0.125rem);
  min-width: 0;
  max-width: 100%;
  min-height: 0;
  flex: unset;
  grid-template-columns: unset;
  grid-template-rows: unset;
  place-items: center;
  font-size: var(--text-2xs, 0.6875rem);
  line-height: 1.2;
  padding-inline: var(--space-2xs, 0.125rem);
  padding-block-start: 0;
  overflow: hidden;
  text-align: center;
}
/* Kill daisyUI step min-width / row geometry that forces sprawl. State glyphs
   come from the fragment's data-content (done "✓", current "●", else empty) —
   never a step number (workflow labels stay unnumbered). */
[data-case-journey] .step::before {
  content: attr(data-content);
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  min-width: 1.75rem;
  min-height: 1.75rem;
  margin-inline: auto;
  border-radius: var(--radius-full);
  background: var(--color-base-200, var(--bg-surface));
  color: var(--color-base-content, inherit);
  font-size: var(--text-2xs, 0.6875rem);
  font-weight: 600;
  line-height: 1;
  z-index: 1;
}
[data-case-journey] .step.step-primary::before,
[data-case-journey] .step[data-journey-state="current"]::before,
[data-case-journey] .step[data-journey-state="done"]::before {
  background: var(--color-primary, var(--accent-primary));
  color: var(--color-primary-content, var(--accent-primary-content));
}
/* Connector between equal tracks — half of each cell, never full viewport. */
[data-case-journey] .step::after {
  content: "";
  position: absolute;
  top: 0.85rem;
  inset-inline-start: calc(50% + 0.9rem);
  inset-inline-end: calc(-50% + 0.9rem);
  height: 2px;
  background: color-mix(in oklab, var(--color-base-300, var(--border-base)) 80%, transparent);
  z-index: 0;
}
/* Dark-mode contrast: --color-base-300 at 80% transparent is barely
   distinguishable from --color-base-100 (both are near-black blues in the
   capture-dx-dark palette). Lift the alpha so the upcoming-path connector
   stays readable as "not-yet" without overdrawing the "done" path. */
[data-theme="capture-dx-dark"] [data-case-journey] .step::after,
[data-theme="capture-dx-dark"] [data-case-journey] .step:not([data-journey-state="done"])::after {
  background: color-mix(in oklab, var(--color-base-300, var(--border-base)) 96%, transparent);
}
[data-theme="capture-dx-dark"] [data-case-journey] .step[data-journey-state="done"]::after {
  background: color-mix(in oklab, var(--color-primary, var(--accent-primary)) 80%, transparent);
}
[data-case-journey] .step:last-child::after {
  display: none;
}
/* Completed spans read as progress: the connector leaving a done step fills
   with the primary accent instead of the idle border tone. */
[data-case-journey] .step[data-journey-state="done"]::after {
  background: color-mix(in oklab, var(--color-primary, var(--accent-primary)) 60%, transparent);
}
[data-case-journey] .step a,
[data-case-journey] .step > span,
[data-case-journey] .step .truncate,
[data-case-journey] .step .btn,
[data-case-journey] .step .bao-journey-step-label {
  display: block;
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: inherit;
  line-height: 1.2;
  height: auto;
  min-height: 0;
  padding-inline: 0.125rem;
  background: transparent;
  border: 0;
  box-shadow: none;
}
/* Grow the step-label tap target past the WCAG 2.5.8 24px floor (Fitts): the
   13px text alone was untappable. Block padding stays below the daisyUI
   numbered circle (which flows above the label), so the label never collides
   with the circle — the tap area just extends down into the step cell. */
[data-case-journey] .step .bao-journey-step-label {
  padding-block: 0.5rem 0.375rem;
  margin-block-start: calc(-1 * var(--space-micro, 0.25rem));
}
@media (max-width: 47.99rem) {
  [data-case-journey] .steps,
  [data-case-journey] ul.steps,
  [data-case-journey] ul[data-case-journey-steps] {
    max-width: 100%;
    grid-auto-columns: minmax(3.5rem, 1fr);
  }
  /* Narrow cells: wrap step labels to two lines instead of ellipsizing
     ("Finish Ca…"). Clamp keeps the rail geometry stable. */
  [data-case-journey] .step a,
  [data-case-journey] .step .bao-journey-step-label {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    white-space: normal;
    overflow-wrap: anywhere;
    text-align: center;
  }
}
/*
 * §8.6 canonical transformation — phone shells swap the five-step rail +
 * meta row for ONE capsule: current-step glyph, visible label (closed sins:
 * journey steps stay labeled), n/m progress, plus accession identity (not a
 * restated "Next: {step}" — the capsule already names the continue target).
 * The capsule deep-links to the current step; the rail returns at ≥40rem.
 */
.bao-journey-compact {
  display: none;
}
@media (max-width: 39.99rem) {
  /* nav+ul selectors out-spec the 47.99rem rail rules that pin display:grid. */
  nav[data-case-journey] ul[data-case-journey-steps],
  [data-case-journey] ul[data-case-journey-steps],
  [data-case-journey] [data-case-journey-steps],
  nav[data-case-journey] p[data-case-journey-hint-row],
  [data-case-journey] [data-case-journey-hint-row] {
    display: none;
  }
  .bao-journey-compact {
    display: flex;
    align-items: center;
    gap: var(--space-inline, 0.5rem);
    min-width: 0;
  }
  .bao-journey-capsule {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    align-items: center;
    gap: var(--space-inline, 0.5rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: var(--space-inline, 0.5rem);
    border-radius: var(--radius-field, 0.75rem);
    border: 1px solid var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
    background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
  }
  /*
   * Capsule names the step (label + n/m). compactHint carries the active
   * accession identity (handler SSOT) — surface it as a trailing mono chip
   * so operators know WHICH case the journey applies to (not a second
   * "Next: …" verb; not sr-only dead chrome). Empty nodes collapse.
   *
   * The plate must be a BLOCK container, not a flex one. text-overflow
   * applies only to a block container's own inline content; a flex container
   * wraps its text in an anonymous item that inherits white-space but not
   * overflow, so under display:inline-flex the ellipsis declaration below
   * was inert and overflow:hidden hard-sliced the accession mid-glyph at
   * the plate edge (scrollWidth 146 vs clientWidth 132 at 320px —
   * "SEED-GASTRIC-SFT-1" losing its trailing digit with no ellipsis and no
   * scroll affordance). As a flex item of .bao-journey-compact the plate is
   * still blockified and still centred by the strip's align-items; the
   * content is one nowrap line, so line-height centres it inside the
   * touch-target plate exactly, less the 1px border pair declared below.
   */
  .bao-journey-compact-hint {
    display: inline-block;
    min-width: 0;
    max-width: min(42vw, 9rem);
    min-height: var(--touch-target-min, 2.75rem);
    line-height: calc(var(--touch-target-min, 2.75rem) - 2px);
    padding-inline: var(--space-inline, 0.5rem);
    border-radius: var(--radius-field, 0.75rem);
    border: 1px solid var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
    background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs, 0.75rem);
    color: var(--color-base-content);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .bao-journey-compact-hint:empty {
    display: none;
  }
}


/* ============================================================
   Motion transition primitives
   ============================================================
   All motion goes through happydumpling tokens. No raw transitions
   in consumer code. */

.bao-motion-transition-colors {
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-normal);
}

.bao-motion-transition-opacity {
  transition-property: opacity;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-normal);
}

.bao-motion-transition-transform {
  transition-property: transform;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-normal);
}
.bao-motion-transition {
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-normal);
}

/* design.md §6 press/hover for glass chrome and touch controls.
 * Context chips: color/border only — never transform/opacity. HTMX outerHTML
 * remounts + translateY/scale transitions = WebView 101 vibration thrash. */
.bao-surface-glass-reactive,
.bao-touch-target,
.bao-touch-target.bao-motion-transition-colors,
.btn.bao-touch-target,
.btn:not([hidden]):not(.hidden),
.bao-toolbar-action,
.bao-nav-item {
  transition-timing-function: var(--easing-standard);
}

.bao-context-card,
[data-capture-context],
[data-chat-context-card],
.bao-shell-context-chip {
  transition-property: color, background-color, border-color, box-shadow;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-fast, 120ms);
  transform: none;
}

@media (hover: hover) and (pointer: fine) {
  /* Transform only — material tint/edge/shadow for the hover state is
     owned by tokens-glass-base.ts (one owner per concern). */
  .bao-surface-glass-reactive:hover,
  .bao-nav-item:hover,
  .btn:not([hidden]):not(.hidden):not(:disabled):not([aria-disabled="true"]):hover {
    transform: translateY(-1px);
  }
}

.bao-surface-glass-reactive:active,
.btn.bao-touch-target:active:not(:disabled),
.btn:not([hidden]):not(.hidden):not(:disabled):not([aria-disabled="true"]):active,
.bao-nav-item:active {
  transform: scale(0.985);
}


@media (prefers-reduced-motion: reduce) {
  .bao-surface-glass-reactive:hover,
  .bao-surface-glass-reactive:active,
  .btn.bao-touch-target:active:not(:disabled),
  .btn:not([hidden]):not(.hidden):hover,
  .btn:not([hidden]):not(.hidden):active {
    transform: none;
  }
  .bao-nav-item:hover,
  .bao-nav-item:active {
    transform: scale(1);
  }
}

/* Coarse pointers: never rely on hover-only chrome; keep focus-visible primary. */
@media (hover: none), (pointer: coarse) {
  .bao-surface-glass-reactive:hover,
  .bao-nav-item:hover {
    transform: scale(1);
    background-color: inherit;
  }
}

/* ============================================================
   Shared pulse utility (Bao-Translate rememberPulseFloat contract)
   One class for recording halo, empty mic, model-init — DRY.
   Reduced motion: static rest (no keyframes registered).
   ============================================================ */

@keyframes bao-pulse-scale {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(var(--pulse-scale-peak, 1.12)); }
}

@keyframes bao-pulse-alpha {
  0%, 100% { opacity: 1; }
  50% { opacity: var(--pulse-alpha-floor, 0.55); }
}

.bao-pulse {
  transform-origin: center;
}

.bao-pulse-scale {
  animation: bao-pulse-scale var(--duration-pulse, 800ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1)) infinite;
}

.bao-pulse-alpha {
  animation: bao-pulse-alpha var(--duration-pulse-alpha, 1000ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1)) infinite;
}

.bao-pulse-scale.bao-pulse-alpha {
  animation:
    bao-pulse-scale var(--duration-pulse, 800ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1)) infinite,
    bao-pulse-alpha var(--duration-pulse-alpha, 1000ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1)) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .bao-pulse,
  .bao-pulse-scale,
  .bao-pulse-alpha,
  .animate-pulse {
    animation: none;
    opacity: 1;
    will-change: auto;
  }
}

/*
 * Reduced-motion contract. layout-shell-html.ts writes the preference
 * onto html[data-reduced-motion="true"] so server-rendered chrome and
 * JS-disabled surfaces honor the same floor as the OS-level
 * prefers-reduced-motion query. All interactive shell elements stop
 * motion (transitions, pulse halos, view transitions).
 */
html[data-reduced-motion="true"] *,
html[data-reduced-motion="true"] *::before,
html[data-reduced-motion="true"] *::after {
  animation-duration: var(--reduced-motion-min-duration, 0.001ms);
  animation-iteration-count: 1;
  transition-duration: var(--reduced-motion-min-duration, 0.001ms);
  scroll-behavior: auto;
}
html[data-reduced-motion="true"] .bao-pulse,
html[data-reduced-motion="true"] .bao-pulse-scale,
html[data-reduced-motion="true"] .bao-pulse-alpha,
html[data-reduced-motion="true"] [data-motion-pulse] {
  animation: none;
  opacity: 1;
}

/* Press acknowledgment — sub-100ms, transform-only (design.md §6). */
button.bao-touch-target,
a.bao-touch-target,
summary.bao-touch-target,
label.bao-touch-target[for] {
  transition-property: transform;
  transition-duration: var(--duration-fast, 120ms);
  transition-timing-function: var(--easing-standard);
}

button.bao-touch-target:active,
a.bao-touch-target:active,
summary.bao-touch-target:active,
label.bao-touch-target[for]:active {
  transform: scale(0.985);
}

@media (prefers-reduced-motion: reduce) {
  button.bao-touch-target:active,
  a.bao-touch-target:active,
  summary.bao-touch-target:active,
  label.bao-touch-target[for]:active {
    transform: none;
  }
}
/* Product override uses scale(1); media block owns the sole none (S62). */
html[data-reduced-motion="true"] button.bao-touch-target:active,
html[data-reduced-motion="true"] a.bao-touch-target:active,
html[data-reduced-motion="true"] summary.bao-touch-target:active,
html[data-reduced-motion="true"] label.bao-touch-target[for]:active {
  transform: scale(1);
}

/* Table row action menus — pin cells stay hittable. */
.bao-table-pinned-action,
td.bao-table-pinned-action,
th.bao-table-pinned-action {
  overflow: visible;
}

/*
 * Pinned column divider (design.md §5.5 sticky-column depth): the old hard
 * border-s + opaque white read as a floating white band when the row zebra
 * did not match. Instead the sticky cell inherits its row's own background
 * (occludes scrolled content) and casts a soft LEFT shadow that only reads
 * as depth when content sits behind it — no hard line, no colour mismatch.
 * The zebra background wiring (tokens-layout-registry-surfaces) still sets
 * the per-row tint; this default covers non-zebra tables.
 */
.bao-table-pinned-divider {
  /* Depth only — this class does not choose a colour.

     It used to resolve a token that fell back to base-100 no matter what the
     surrounding row painted, so the pinned header cell rendered white beside
     base-200 siblings. Inheriting was no better: the header row is itself
     white and each header cell carries its own bg-base-200, so `inherit`
     picked up the row and reproduced the mismatch. Whoever owns the cell's
     colour keeps it — bg-base-200 on the header, the row via `inherit` in
     the body (tokens-layout-registry-surfaces) — and this rule stays out of
     it. */
  box-shadow: -0.5rem 0 0.5rem -0.5rem color-mix(in oklab, var(--color-base-content) 12%, transparent);
}
@container (width < 40rem) {
  /* Card mode: the pinned column is not sticky (bao-table-pinned-unstick) —
     drop the depth shadow so a stacked card row has no orphan edge.
     Beat zebra pin plate (table.table-zebra … .bao-table-pinned-action)
     which otherwise paints a solid base-200 action strip on even cards. */
  .bao-table-pinned-divider,
  tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row > .bao-table-pinned-action,
  .table.table-zebra > tbody > tr.bao-table-responsive-row:nth-child(even) > .bao-table-pinned-action {
    box-shadow: none;
    background-color: transparent;
    --bao-table-pinned-action-background: transparent;
  }
}

@media (max-width: 47.99rem) {
  [data-primitive="DataTable"] .bao-table-pinned-action,
  [data-primitive="DataTable"] details.dropdown {
    position: relative;
    z-index: 1;
  }
  [data-primitive="DataTable"] tr:focus-within,
  [data-primitive="DataTable"] tr:has(details[open]) {
    z-index: 2;
    position: relative;
  }
}

/* Sheet / dialog motion: spring default; fade when reduced-motion. */
.bao-motion-sheet-spring,
.bao-sheet-panel {
  transition-property: transform, opacity;
  transition-duration: var(--duration-normal, 200ms);
  transition-timing-function: var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1));
}
.bao-motion-sheet-fade {
  transition-property: opacity;
  transition-duration: var(--duration-fast, 120ms);
  transition-timing-function: var(--easing-standard);
}
@media (prefers-reduced-motion: reduce) {
  .bao-motion-sheet-spring,
  .bao-sheet-panel {
    transition-property: opacity;
    transition-duration: var(--duration-fast, 120ms);
    transform: none;
  }
}
/* Product override uses scale(1); media block owns the sole none (S62). */
html[data-reduced-motion="true"] .bao-motion-sheet-spring,
html[data-reduced-motion="true"] .bao-sheet-panel {
  transition-property: opacity;
  transform: scale(1);
}

/* Capability-filtered sidebar empty CTA. */
ul[data-widget="sidebar"][data-state="empty"] [data-sidebar-empty] {
  display: list-item;
}
ul[data-widget="sidebar"][data-state="success"] [data-sidebar-empty],
ul[data-widget="sidebar"] [data-sidebar-empty][hidden] {
  display: none;
}
/* Collapsed rail: tooltips on; expanded: hide tip chrome (label visible). */
[data-sidebar-expanded="true"] .bao-nav-item.tooltip::before,
[data-sidebar-expanded="true"] .bao-nav-item.tooltip::after,
.bao-sidebar-expanded .bao-nav-item.tooltip::before,
.bao-sidebar-expanded .bao-nav-item.tooltip::after {
  display: none;
}


/* ============================================================
   Nested glass fill plate (baobeep concentric anatomy)
   ============================================================
   Apply to text/form/list hosts nested inside a glass shell.
   Never put bare body copy on .bao-surface-glass-*.
   Content widgets use solid bao-data-panel — do not co-tag glass. */

.bao-glass-fill {
  background-color: var(--glass-fill-bg, var(--bg-panel, var(--color-base-100)));
  border: 1px solid var(--glass-fill-border, color-mix(in oklab, var(--text-primary) 8%, transparent));
  color: var(--text-primary, var(--color-base-content));
  border-radius: calc(var(--radius-box, 1rem) - 2px);
  box-shadow: inset 0 1px 0 var(--glass-highlight-soft);
}

/*
 * S100 — stack fill + interactive hosts above specular (z=2). Never
 * position:relative on every child (breaks absolute/fixed/sticky geometry).
 */
.bao-surface-glass-regular > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]),
.bao-surface-glass-clear > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]),
.bao-surface-glass-strong > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]),
.bao-surface-glass-modal > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]) {
  z-index: 3;
}
.bao-surface-glass-regular > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]):where(:not(.absolute):not(.fixed):not(.sticky)),
.bao-surface-glass-clear > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]):where(:not(.absolute):not(.fixed):not(.sticky)),
.bao-surface-glass-strong > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]):where(:not(.absolute):not(.fixed):not(.sticky)),
.bao-surface-glass-modal > :is(.bao-glass-fill,.btn,a,button,input,select,textarea,summary,.menu,.navbar-start,.navbar-center,.navbar-end,.bao-touch-target,.bao-shell-brand,.bao-shell-context-strip,[data-primitive],[role="toolbar"],[role="navigation"],search,[role="search"],[role="menu"],[role="listbox"]):where(:not(.absolute):not(.fixed):not(.sticky)) {
  position: relative;
}

/* KPI / metric card body plate — nested under glass metric shells. */
[data-primitive="Card"][data-variant="metric"] .bao-glass-fill,
.card.bao-surface-glass-regular .bao-glass-fill {
  min-width: 0;
}

/*
 * L0 workbench under L1 shell glass — AnnotationWorkbench + AIAssistant
 * content planes stay solid (design-canon liquidGlass.placementRule.content).
 * L2 floating toolbars/menus keep Toolbar/DropdownMenu glass SSOT.
 */
#app-main [data-primitive="AnnotationWorkbench"] .bao-data-panel,
#app-main [data-primitive="AIAssistant"] .bao-data-panel,
#app-main [data-primitive="AIAssistant"] .card.card-border,
[data-editor-chrome] .bao-data-panel,
[data-capture-chrome] .bao-data-panel {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background-color: var(--bg-panel, var(--color-base-100));
}

/* Reduced transparency / high contrast: fill plate is already solid. */
@media (prefers-reduced-transparency: reduce) {
  .bao-glass-fill {
    background-color: var(--bg-surface, var(--bg-panel));
    box-shadow: none;
  }
}
html[data-reduced-transparency="true"] .bao-glass-fill {
  background-color: var(--bg-surface, var(--bg-panel));
  box-shadow: none;
}



/* ============================================================
   Page chrome — sticky strip only (blur comes from glass classes)
   ============================================================
   Never re-apply raw backdrop-filter here. Toolbars already carry
   .bao-surface-glass-regular.bao-glass-small so one sheet blurs. */

.bao-page-chrome {
  position: sticky;
  top: 0;
  z-index: 10;
  box-shadow: 0 1px 0 0 color-mix(in oklab, var(--border-base) 55%, transparent);
}

.bao-page-chrome::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -0.5rem;
  height: 0.5rem;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    color-mix(in oklab, var(--bg-panel) 35%, transparent),
    transparent
  );
  opacity: var(--opacity-near, 0.9);
}

/*
 * Scroll-edge adaptation (design.md §5.1/§7.5) lives with the topbar owners:
 * shadow in tokens-glass-control-chrome.ts, tint in
 * tokens-glass-shell-webview-theme.ts. State: app-shell-glass.ts.
 */


/* ============================================================
   Toolbar actions — labeled page chrome + compact icon chrome
   ============================================================
   Page toolbars (data-toolbar-density="labeled"): always show verb labels.
   Compact chrome (btn-square / floating / table row actions): icon + tooltip at
   rest; hover/focus expands label. Soft glass wash on ghost/chrome only. */

.bao-toolbar-action {
  align-items: center;
  justify-content: center;
  gap: var(--space-compact, 0.375rem);
  overflow: hidden;
  border-radius: var(--radius-selector, 0.75rem);
  transition-property: background-color, color, box-shadow, border-color, max-width, padding;
  transition-duration: var(--duration-normal);
}
/* Table/row icon chrome: keep HIG 44px hit area on touch (btn-xs square is smaller). */
@media (pointer: coarse) {
  .bao-toolbar-action.btn-square,
  .bao-toolbar-action.btn-xs {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
  }
  /* Queue action clusters: labeled soft buttons, never tooltip-only. */
  .bao-queue-actions[data-toolbar-density="labeled"] .bao-toolbar-action .bao-toolbar-label {
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
    max-width: var(--toolbar-label-max-width, 8rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    clip: auto;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: var(--text-xs, 0.75rem);
  }
}

/* Floating panels opt back into overlay positioning after the glass material
   establishes a positioning context for its specular pseudo-elements.
   The viewport-escape state (app-shell-dropdowns pins open menus so overflow
   ancestors cannot clip them) must keep position: fixed — this reset is
   unlayered and would otherwise beat the layered escape rule. */
.bao-dropdown-panel:not(.bao-dropdown-panel-viewport) {
  position: absolute;
}

/* Glass wash is for chrome/ghost actions only — never bleach filled
   semantic buttons (primary create, error revoke, etc.) or settings tabs
   (active destination uses primary fill; wash would white-on-white). */
@media (hover: hover) and (pointer: fine) {
  .bao-toolbar-action:not(.btn-primary):not(.btn-secondary):not(.btn-accent):not(.btn-error):not(.btn-warning):not(.btn-success):not(.btn-info):not(.bao-settings-tab):not(.bao-session-mode-tab):hover,
  .bao-toolbar-action:not(.btn-primary):not(.btn-secondary):not(.btn-accent):not(.btn-error):not(.btn-warning):not(.btn-success):not(.btn-info):not(.bao-settings-tab):not(.bao-session-mode-tab):focus-visible {
    background: color-mix(in oklab, var(--bg-panel) 72%, transparent);
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--liquid-border-regular) 80%, transparent);
  }
}

/*
 * Compact chrome: collapse labels until hover/focus (btn-square only).
 *
 * Excludes any surface that declared labeled density. This file holds BOTH
 * the default-hide and the labeled-reveal, and the default was winning its own
 * argument: .bao-toolbar-action.btn-square .bao-toolbar-label is (0,3,0) while
 * [data-toolbar-density="labeled"] .bao-toolbar-label below is (0,2,0), so a
 * surface that explicitly asked for labels was overruled by the generic
 * default three-to-two.
 *
 * That is why the editor dock rendered as anonymous glyphs while declaring
 * data-toolbar-density="labeled" — measured at 390, 768 and 1280, collapsed
 * and expanded, max-width computed to 0px every time. Patching the four other
 * modules that also clipped this label changed nothing, because none of them
 * was the rule that won.
 *
 * An explicit declaration must outrank a default. The exclusion is by
 * contract, so no surface has to be named here.
 */
.bao-toolbar-action.btn-square:not([data-toolbar-density="labeled"] *) .bao-toolbar-label {
  display: inline-block;
  max-width: 0;
  margin-inline-start: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 500;
  letter-spacing: var(--tracking-label, 0.01em);
  transition-property: max-width, opacity, margin-inline-start;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-normal);
}

/* Touch surfaces keep permanent verbs — hover-to-reveal requires a hover.
   Shell icon chrome (theme/voice/user) stays glyph-only by design. */
@media (pointer: coarse) {
  [data-toolbar-density="icon"] .bao-toolbar-action:not(.bao-shell-icon-action) .bao-toolbar-label {
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
    clip: auto;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    overflow: hidden;
    white-space: nowrap;
  }
}

/* Labeled page toolbars: always show verb text. */
.bao-toolbar-action:not(.btn-square) .bao-toolbar-label,
[data-toolbar-density="labeled"] .bao-toolbar-label {
  display: inline-block;
  /*
   * flex: none breaks a circular sizing dependency. Without it the reveal is
   * cosmetic: the label carries overflow: hidden, so its min-content
   * contribution is zero and as a flex item it shrinks to nothing; the button
   * then sizes itself to that collapsed content and settles at 44px — which is
   * the very width that made the label shrink. Measured: max-width 192px,
   * opacity 1, rendered width 0, natural width 59px. Revealed and invisible.
   */
  flex: none;
  max-width: var(--toolbar-label-reveal-max-width, 12rem);
  margin-inline-start: 0;
  opacity: 1;
  overflow: hidden;
  white-space: nowrap;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 500;
  letter-spacing: var(--tracking-label, 0.01em);
}

/*
 * A revealed label needs a box to live in — at EVERY pointer type.
 *
 * daisyUI's .btn-square pins width AND height to var(--size), so a control
 * that reveals its caption still paints a 2.75rem square and clips it:
 * measured "Rectangle" at scrollWidth 59px inside a clientWidth of 26px. The
 * caption was visible and unreadable, which is worse than hidden.
 *
 * This sits OUTSIDE any media query on purpose. The coarse-pointer copy of
 * this rule is invisible to any environment reporting pointer: fine — which
 * includes the browser pane at phone width — so a touch-only fix reads as no
 * fix at all while a real device would have been fine. Labeled density is a
 * declaration about the CONTROL, not about the input device.
 */
[data-toolbar-density="labeled"] .bao-toolbar-action.btn-square {
  width: auto;
  max-width: none;
  min-width: var(--touch-target-min, 2.75rem);
  min-height: var(--touch-target-min, 2.75rem);
  height: auto;
  padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
}

[data-primitive="FilterBar"] .filter-reset,
[data-primitive="FilterBar"] [data-case-filter-reset],
form[data-case-filters] [data-case-filter-reset] {
  width: auto;
  max-width: none;
  min-width: var(--touch-target-min, 2.75rem);
  min-height: var(--touch-target-min, 2.75rem);
  justify-content: center;
  gap: var(--space-micro, 0.25rem);
  padding-inline: var(--space-inline, 0.5rem);
}
[data-primitive="FilterBar"] .filter-reset .bao-toolbar-label,
[data-primitive="FilterBar"] [data-case-filter-reset] .bao-toolbar-label,
form[data-case-filters] [data-case-filter-reset] .bao-toolbar-label {
  display: inline-block;
  max-width: 12rem;
  margin-inline-start: var(--space-micro, 0.25rem);
  opacity: 1;
  overflow: hidden;
  white-space: nowrap;
  position: static;
  width: auto;
  height: auto;
  clip: auto;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 500;
}


.bao-toolbar-action.btn-square:hover .bao-toolbar-label,
.bao-toolbar-action.btn-square:focus-visible .bao-toolbar-label,
.bao-toolbar-action.btn-square:focus-within .bao-toolbar-label,
.bao-toolbar-action.btn-square[aria-expanded="true"] .bao-toolbar-label,
.bao-toolbar-action.btn-square[aria-pressed="true"] .bao-toolbar-label {
  max-width: var(--toolbar-label-reveal-max-width, 12rem);
  margin-inline-start: var(--space-compact, 0.375rem);
  opacity: 1;
}

/*
 * Compact square actions expand width when a visible label is shown.
 * Shell icon-only chrome (theme/voice/user/menu) must never expand —
 * aria-pressed=true on the theme toggle would left-offset the glyph.
 */
.bao-toolbar-action.btn-square:not(.bao-shell-icon-action):hover,
.bao-toolbar-action.btn-square:not(.bao-shell-icon-action):focus-visible,
.bao-toolbar-action.btn-square:not(.bao-shell-icon-action):focus-within,
.bao-toolbar-action.btn-square:not(.bao-shell-icon-action)[aria-pressed="true"],
.bao-toolbar-action.btn-square:not(.bao-shell-icon-action)[aria-expanded="true"] {
  width: auto;
  min-width: var(--toolbar-action-min-width, 2.5rem);
  padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
}

/*
 * Touch / no-hover chrome densify (Apple HIG + WCAG 2.5.8):
 * Secondary icon actions stay icon-only with aria-label + title/data-tip —
 * never expand labels that reflow toolbars off-screen. Primary form verbs
 * (not btn-square) keep visible labels. Hit targets stay ≥44px.
 */
@media (hover: none),
(pointer: coarse) {/*
   * Same labeled-density exclusion as the fine-pointer default above.
   * Without it this rule contradicted the law stated at the top of this very
   * block — touch surfaces keep permanent verbs — by removing the caption from
   * the box tree entirely on exactly the devices that have no hover to reveal
   * it. display:none also destroys the accessible name,
since the glyph is
   * aria-hidden and S123 forbids a second name via aria-label.
   */
  .bao-toolbar-action.btn-square:not([data-toolbar-density="labeled"] *) .bao-toolbar-label {
    display: none;
    max-width: 0;
    margin-inline-start: 0;
    opacity: 0;
  }/*
   * The square LOCK is part of the same contract as the label clip above.
   * Revealing a caption inside a box pinned to 2.75rem square just truncates
   * it — measured: "Rectangle" reveals with max-width 192px and still paints
   * 20px wide. A surface asking for labels needs room for them,
so labeled
   * density keeps the 44px floor as a MINIMUM and lets the control grow.
   */
  .bao-toolbar-action.btn-square:not([data-toolbar-density="labeled"] *) {
    width: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    max-width: var(--touch-target-min, 2.75rem);
    padding-inline: 0;
  }
  [data-toolbar-density="labeled"] .bao-toolbar-action.btn-square {
    width: auto;
    max-width: none;
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
  }
  /*
   * Non-square secondary chrome on phone: hide verb text in dense toolbars,
   * keep icon + tip. Page-header clinical CTAs (finish/generate/customize)
   * always keep labels — design.md: essentials must not depend on hover —
   * so the page-header action row is excluded here outright;its density is
   * owned by glass-shell-page-header + layout-dock (soft/primary labeled,
* ghost/outline icon-first).
   * Settings destinations keep labels (Apple list) — exclude bao-settings-tab.
   * Session mode tabs (Acquire/Annotate) own their reveal in capture-preview
   * tokens (active tab shows its verb) — exclude bao-session-mode-tab.
   * Filter clear/reset keeps a verb — owned by the .filter-reset block above.
   * :has(> svg) gates to icon-bearing actions: a label-only button must never
   * densify into a blank pill (engines without :has() keep labels — safe).
   */
  /* Exclude chat context-more + header overflow: those are often the sole
     mobile chrome control (model chip hidden / finish collapsed) — a bare
     chevron or ⋯ fails discoverability (Apple HIG labeled essentials). */
  /*
   * Soft clinical secondaries (btn-soft) keep permanent verbs — never hide
   * labels for soft/primary essentials (evidence Generate report,
etc.).
   * Sticky labeled rails own density=labeled and must never densify to blank.
   */
  /*
   * Hidden the sr-only way,
NOT display:none. Densifying to an icon must not
   * strip the control's accessible name: display:none removes the label from
   * the accessibility tree,
and these controls carry no aria-label because
   * S123 forbids naming attrs on a control that has a visible label. The two
   * rules together produced genuinely nameless buttons in the phone band
   * (axe button-name,
critical,
on the annotation editor toolbar).
   *
   * Clipping instead keeps the name readable to assistive tech while staying
   * invisible and out of flow,
so both contracts hold at every width.
   */
  .bao-toolbar-action:not(.btn-primary):not(.btn-soft):not(.btn-error):not(.btn-success):not(.bao-settings-tab):not(.bao-session-mode-tab):not(.filter-reset):not([data-case-filter-reset]):not([data-page-header-actions] *):not([data-chat-context-more] > summary):not([data-header-action-overflow] > summary):not([role="toolbar"].bao-sticky-toolbar *):has(> svg)
    .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  .bao-toolbar-action:not(.btn-primary):not(.btn-error):not(.btn-success):not(.bao-settings-tab):not(.filter-reset):not([data-case-filter-reset]).btn {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
  }
  [data-page-header-actions] .bao-toolbar-action .bao-toolbar-label,
  [data-primitive="PageHeader"] [data-page-header-actions] .bao-toolbar-action .bao-toolbar-label,
  [data-page-header-actions] .bao-toolbar-action.btn-square .bao-toolbar-label,
  [data-chat-context-more] > summary .bao-toolbar-label,
  /* Non-media header overflow keeps verb; media phone densify owns hide. */
  [data-page-type]:not([data-page-type="media"]) [data-header-action-overflow] > summary .bao-toolbar-label,
  [data-toolbar] .bao-toolbar-action.btn-primary .bao-toolbar-label,
  [data-toolbar] .bao-toolbar-action.btn-soft:not(.btn-square):not([data-dashboard-edit-toggle]) .bao-toolbar-label,
  [data-primitive="FilterBar"] .bao-toolbar-action:not(.btn-primary):not(.btn-error):not(.btn-success):not(.filter-reset):not([data-case-filter-reset]):has(> svg) .bao-toolbar-label,
  /*
   * Sticky clinical action rails (evidence verify/custody/bundle/finish):
   * icon-only squares fail discoverability on phone (Apple HIG labeled
   * essentials). Always paint verb text;horizontal scroll owns overflow.
   */
  [role="toolbar"].bao-sticky-toolbar .bao-toolbar-action .bao-toolbar-label,
[role="toolbar"].bao-sticky-toolbar .bao-toolbar-action.btn-square .bao-toolbar-label {
    display: inline-block;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    overflow: hidden;
    white-space: nowrap;
  }[data-page-header-actions] .bao-toolbar-action.btn-square,
[role="toolbar"].bao-sticky-toolbar .bao-toolbar-action.btn-square {
    width: auto;
    max-width: none;
    min-width: var(--touch-target-min, 2.75rem);
    padding-inline: var(--space-inline, 0.5rem);
  }/* Page header actions: wrap,
never overflow the title row. */
  [data-page-header-actions],
[data-primitive="PageHeader"] [data-page-header-actions] {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-micro);
    max-width: 100%;
  }
}


/*
 * Phone viewport (not only coarse pointer): headed desktop Chrome emulates
 * mobile width with fine pointer + hover, which skipped the coarse block and
 * left evidence sticky rails icon-only. Width gate restores labeled verbs.
 * Specificity beats .bao-toolbar-action.btn-square .bao-toolbar-label max-width:0.
 */
@media (max-width: 47.99rem) {
  #app-main [role="toolbar"].bao-sticky-toolbar .bao-toolbar-action .bao-toolbar-label,
  #app-main [role="toolbar"].bao-sticky-toolbar .bao-toolbar-action.btn-square .bao-toolbar-label,
  #app-main [role="toolbar"].bao-sticky-toolbar .btn.bao-toolbar-action.btn-square .bao-toolbar-label,
  #app-main [role="toolbar"].bao-sticky-toolbar[data-toolbar-density="labeled"] .bao-toolbar-action .bao-toolbar-label {
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
    clip: auto;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    overflow: hidden;
    white-space: nowrap;
  }
  #app-main [role="toolbar"].bao-sticky-toolbar .bao-toolbar-action.btn-square,
  #app-main [role="toolbar"].bao-sticky-toolbar .btn.bao-toolbar-action.btn-square {
    width: auto;
    max-width: none;
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: var(--space-inline, 0.5rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bao-toolbar-action.btn-square:not(.bao-shell-icon-action) .bao-toolbar-label {
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-compact, 0.375rem);
    opacity: 1;
    transition-duration: var(--reduced-motion-min-duration, 0.001ms);
  }
  .bao-toolbar-action.btn-square:not(.bao-shell-icon-action),
  .bao-toolbar-action.btn-square:not(.bao-shell-icon-action)[aria-pressed="true"],
  .bao-toolbar-action.btn-square:not(.bao-shell-icon-action)[aria-expanded="true"] {
    width: auto;
    min-width: var(--toolbar-action-min-width, 2.5rem);
    padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
  }
}

/*
 * Tablet band (48–79.99rem): page toolbars paint icon + label at rest
 * (imp-t09 — discoverability; phone keeps icon-only densify, ≥80rem keeps
 * fine-pointer hover-reveal). Scoped to #app-main page toolbars: shell chrome
 * (theme/voice/user) stays glyph-only, and the editor tool dock owns its own
 * per-band label density (tokens-layout-editor-compact/surfaces-phone).
 * #app-main (ID) outranks the long :not() densify chains in the coarse block
 * above, so labels win at any pointer type inside the band.
 */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  #app-main [role="toolbar"]:not([data-editor-toolbar]) .bao-toolbar-action:not(.bao-shell-icon-action) .bao-toolbar-label {
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
    clip: auto;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    overflow: hidden;
    white-space: nowrap;
  }
  #app-main [role="toolbar"]:not([data-editor-toolbar]) .bao-toolbar-action.btn-square:not(.bao-shell-icon-action) {
    width: auto;
    max-width: none;
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
  }
}


/* ============================================================
   Nav items — reactive solid wash (never a second glass sheet)
   ============================================================ */

.bao-nav-item {
  display: flex;
  align-items: center;
  /* Compact desktop density; touch viewports get HIG 44px below. */
  min-height: var(--touch-target-compact, 2.25rem);
  gap: var(--space-inline);
  border-radius: var(--radius-control, 0.75rem);
  transition-property: background-color, color, box-shadow;
  transition-duration: var(--duration-normal);
}
@media (max-width: 64rem) {
  .bao-nav-item {
    min-height: var(--touch-target-min, 2.75rem);
  }
}
@media (pointer: coarse) {/* Touch latches :hover until the next tap elsewhere,
so a tapped nav item
     keeps the hover fill and reads as permanently selected. Hybrid devices
     (touch laptops,
tablets with a trackpad) report hover:hover AND
     pointer:coarse,
so the fine-pointer gate below does not cover them.
     Active/current items keep their own fill — only the latched hover clears. */
  .bao-nav-item:not(.menu-active):not([aria-current="page"]):hover {
    background: transparent;
  }
  .bao-nav-item {
    min-height: var(--touch-target-min, 2.75rem);
  }
}

@media (hover: hover) and (pointer: fine) {
  .bao-nav-item:hover {
    background: color-mix(in oklab, var(--bg-surface-hover) 80%, transparent);
  }
}
.bao-nav-item:focus-visible {
  background: color-mix(in oklab, var(--bg-surface-hover) 80%, transparent);
}.bao-nav-item.menu-active,
.bao-nav-item[aria-current="page"] {
  background: color-mix(in oklab, var(--accent-primary) 16%, var(--bg-panel));
  color: var(--text-primary);
  box-shadow: inset 3px 0 0 0 var(--accent-primary);
  font-weight: 600;
}.bao-nav-item.menu-active svg,
.bao-nav-item[aria-current="page"] svg {
  stroke-width: 2.1;
  color: var(--accent-primary, var(--color-primary));
}

/* The mobile dock reuses the sidebar's `active` class string from
   resources/bao/partials/sidebar.bao (`menu-active bg-primary/10
   text-primary`). .bao-nav-item reclaims the label colour just above;
   .bao-mobile-dock-item had no such rule, so `text-primary` painted the
   11px dock label in brand blue over the tinted active pill — measured
   4.1:1, under the 4.5:1 WCAG 1.4.3 floor for text that size, and on the
   primary mobile navigation's current item. The tint, weight and icon
   still signal "current"; only the label reverts to readable text. */
.bao-mobile-dock-item[aria-current="page"] .dock-label {
  color: var(--text-primary);
}

/*
 * Sidebar rail header (brand + collapse) — only when permanent rail is in-flow.
 * Matches drawer permanent breakpoint (80rem). Never lg: (phones report ≥1024).
 */
.bao-sidebar-rail-header {
  display: none;
}
@media (min-width: 80rem) {
  .bao-sidebar-rail-header {
    display: flex;
  }
}

/* ============================================================
   Settings surface — one solid sheet; labeled tabs
   ============================================================
   Always show tab labels so settings groups stay scannable.
   Compact icon-only was unreadable with 10+ tabs. */

.bao-settings-tab {
  width: auto;
  min-width: var(--touch-target-compact, 2.25rem);
  flex-shrink: 0;
  padding-inline: var(--space-inline, 0.5rem);
}/* Desktop: labeled settings tabs (scannable groups). */
.bao-settings-tab .bao-toolbar-label,
.bao-settings-tab.tab-active .bao-toolbar-label,
.bao-settings-tab[aria-selected="true"] .bao-toolbar-label {
  max-width: var(--toolbar-label-reveal-max-width, 12rem);
  margin-inline-start: var(--space-compact, 0.375rem);
  opacity: 1;
}

/*
 * Phone settings tabs stay in one horizontally scrollable destination rail.
 * Labels remain visible and native scrolling preserves all destinations.
 */
@media (max-width: 47.99rem) {[data-page-type="form"] .bao-settings-tab,
[data-page="settings"] .bao-settings-tab {
    min-width: max-content;
    width: auto;
    min-height: var(--touch-target-min, 2.75rem);
    justify-content: center;
  }/*
   * Compact collection tab rails (debug 3-up): equal share of the row via
   * flex — never full-bleed single-tab scroll. Token spacing only.
   */
  [data-debug-tabs] .tabs.bao-tabs-scroll,
[data-page="debug"] .tabs.bao-tabs-scroll {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    overflow-x: auto;
    mask-image: none;
    -webkit-mask-image: none;
  }[data-debug-tabs] .bao-settings-tab,
[data-debug-tabs] .tab.bao-settings-tab,
[data-page="debug"] .bao-settings-tab,
[data-page="debug"] .tab.bao-settings-tab {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    max-width: none;
    justify-content: center;
    padding-inline: var(--space-micro);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }[data-page-type="form"] .bao-settings-tab .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab .bao-toolbar-label,
[data-page-type="form"] .bao-settings-tab.tab-active .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab.tab-active .bao-toolbar-label,
[data-page-type="form"] .bao-settings-tab[aria-selected="true"] .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab[aria-selected="true"] .bao-toolbar-label,
[data-page-type="form"] .bao-settings-tab:not(.tab-active):not([aria-selected="true"]) .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab:not(.tab-active):not([aria-selected="true"]) .bao-toolbar-label {
    position: static;
    width: auto;
    height: auto;
    max-width: none;
    margin: 0;
    margin-inline-start: 0;
    padding: 0;
    overflow: hidden;
    clip: auto;
    white-space: nowrap;
    text-overflow: ellipsis;
    opacity: 1;
  }
}


.bao-settings-panels-host {
  /* Single solid sheet for all settings tabpanels — no nested panels. */
  min-width: 0;
}

/* Inactive tabpanel concealment has ONE owner:
   tokens-glass-shell-settings-panels-part-1.css, whose rule covers these three
   selectors plus the [data-page="settings"] [data-settings-panel] variants.
   The mirror that used to sit here was a second owner for the same question
   (LAW1) and is deliberately gone — not emptied by accident. */

/* Settings groups tile via panel grid; sibling margin would double-gap. */
.bao-settings-group + .bao-settings-group {
  margin-block-start: 0;
}

/* API registry mobile entries — dense list items, not nested data panels. */


/* ============================================================
   Solid data panels — content plane (design.md §4)
   ============================================================ */

.bao-data-panel {
  background-color: var(--color-base-100, var(--bg-panel));
  border: 1px solid color-mix(in oklab, var(--border-base) 70%, transparent);
  border-radius: var(--radius-box, 1rem);
  transition-property: border-color, box-shadow, transform;
  transition-duration: var(--duration-glass, 280ms);
  transition-timing-function: var(--easing-glass, cubic-bezier(0.32, 0.72, 0, 1));
}

@media (hover: hover) and (pointer: fine) {
  .bao-data-panel:hover {
    border-color: color-mix(in oklab, var(--border-base) 70%, var(--accent-primary));
    box-shadow: var(--shadow-md);
  }
}
.bao-data-panel:focus-within {
  border-color: color-mix(in oklab, var(--border-base) 70%, var(--accent-primary));
  box-shadow: var(--shadow-md);
}

@media (prefers-reduced-motion: reduce) {
  .bao-data-panel:hover,
  .bao-data-panel:focus-within {
    transform: none;
  }
}



.modal-box {
  box-sizing: border-box;
  /*
   * Inline track, viewport gutter and block cap are custom properties so a
   * sheet variant retunes them by declaration instead of trying to
   * out-specify this rule.
   *
   * This rule is UNLAYERED, so every full-bleed attempt written into
   * @layer utilities lost to it regardless of specificity: the
   * `w-full max-w-full` utilities every .modal-box carries, daisyUI's own
   * modal-bottom width reset, and two @media bands in
   * tokens-layout-fabric-stage.css. All eight phone bottom sheets stayed
   * capped at 100vw - 1rem — measured 359px in a 375px viewport and 684px in
   * a 700px one, leaving a 16px strip of dimmed page down the trailing edge
   * because the sheet is start-aligned. Layer order is not a specificity
   * contest, so the variant has to live here, beside the declaration it
   * retunes.
   */
  --modal-box-track: var(--settings-dialog-max-width, 56rem);
  --modal-box-gutter: 1rem;
  --modal-box-max-block: min(100dvh - 1.5rem, var(--modal-max-height, min(92dvh, 92vh)));
  width: min(100%, var(--modal-box-track));
  max-width: min(100vw - var(--modal-box-gutter), var(--modal-box-track));
  max-height: var(--modal-box-max-block);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/*
 * Phone bottom sheet is full-bleed: a compact-width sheet spans the screen and
 * pins to the bottom edge, so a viewport gutter reads as a misaligned panel
 * rather than as a sheet.
 *
 * Gated to <40rem to match the `sm:` breakpoint the markup switches on — every
 * bottom sheet carries sm:modal-middle, sm:modal-end or sm:hidden, so at
 * >=40rem the centered track above is the correct one and this band must not
 * reach it.
 *
 * --vv-height tracks the visual viewport (app-shell-case-create.ts binds it on
 * visualViewport resize/scroll for the whole document), so the sheet shrinks
 * to clear a software keyboard instead of hiding its own controls behind it.
 */
@media (max-width: 39.99rem) {
  dialog.modal.modal-bottom > .modal-box {
    --modal-box-track: 100%;
    --modal-box-gutter: 0px;
    --modal-box-max-block: min(92dvh, var(--vv-height, 92vh));
  }
}
/* Tablet band (t10): centered modal with a token-driven cap instead of the
   near-full-width desktop track — sm:modal-middle already centers the grid,
   so only the width track tightens. */
@media (min-width: 48rem) and (max-width: 64rem) {
  .modal-box {
    --modal-box-track: var(--modal-tablet-max-width, 40rem);
    --modal-box-gutter: 2rem;
  }
}
.bao-modal-body-scroll,
.modal-box > [class*="dialogBody"],
.modal-box > .px-space-section {
  min-width: 0;
  max-width: 100%;
  overflow-x: hidden;
}
.bao-data-panel fieldset.bao-settings-group.bao-data-panel,
.bao-data-panel .bao-settings-group.bao-data-panel {
  background: transparent;
  border-width: 0;
  box-shadow: none;
  padding: 0;
  border-radius: 0;
}
/* An open dropdown explains itself — its summary tooltip would overlap
   (and clip against chrome edges above) the menu it just opened. */
details[open] > summary.tooltip::before,
details[open] > summary.tooltip::after {
  display: none;
}
@media (hover: hover) and (pointer: fine) {
  /*
   * Hover-to-reveal is icon-first densify ONLY: a button whose sole content
   * is its label must never collapse to a blank pill (design.md §6 —
   * hover is an enhancement; essential labels stay visible). :has(> svg)
   * gates the collapse to icon-bearing actions; engines without :has()
   * drop the rule and keep labels visible — the safe direction.
   */
  /*
   * Exclude filter clear/reset — a full-width bare × is not scannable on
   * collection pages (Cases, Training). Verb stays painted without hover.
   */
  /*
   * :not([data-toolbar-density="labeled"] *) is the density opt-out.
   *
   * This rule is the one that actually decided the question. It carries
   * roughly (0,8,0) of specificity against (0,2,0) for
   * [data-toolbar-density="labeled"] .bao-toolbar-label in
   * tokens-glass-chrome-body.ts, and both sit unlayered — so the hover-reveal
   * default silently outvoted an explicit declaration four to one. Measured
   * live: the editor dock declared labeled and its labels still computed to
   * max-width: 0px; opacity: 0 at 390, 768 and 1280, collapsed AND expanded.
   *
   * An explicit density declaration must beat a default. Excluding it by
   * contract rather than by surface means no future dock, sheet, or rail has
   * to be added here by name.
   */
  .btn-soft.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):not([data-chat-context-more] > summary):not([data-header-action-overflow] > summary):not([data-toolbar-density="labeled"] *):has(> svg) .bao-toolbar-label,
  .btn-ghost.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):not([data-chat-context-more] > summary):not([data-header-action-overflow] > summary):not([data-toolbar-density="labeled"] *):has(> svg) .bao-toolbar-label {
    display: inline-block;
    max-width: 0;
    margin-inline-start: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    transition-property: max-width, opacity, margin-inline-start;
    transition-timing-function: var(--easing-standard);
    transition-duration: var(--duration-normal);
  }
  .btn-soft.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):has(> svg):hover .bao-toolbar-label,
  .btn-soft.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):has(> svg):focus-visible .bao-toolbar-label,
  .btn-ghost.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):has(> svg):hover .bao-toolbar-label,
  .btn-ghost.bao-toolbar-action:not(.btn-primary):not(.filter-reset):not([data-case-filter-reset]):has(> svg):focus-visible .bao-toolbar-label {
    max-width: 12rem;
    margin-inline-start: 0.375rem;
    opacity: 1;
  }
  /*
   * Desktop fine pointer: sole overflow/context disclosures keep a permanent
   * verb (orphanOverflows gate). Phone densify lives in chrome-body coarse.
   */
  [data-chat-context-more] > summary.bao-toolbar-action .bao-toolbar-label,
  [data-header-action-overflow] > summary.bao-toolbar-action .bao-toolbar-label {
    display: inline-block;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin-inline-start: var(--space-micro, 0.25rem);
    opacity: 1;
    overflow: hidden;
    white-space: nowrap;
  }
}


/*
 * Toolbar zones. Primary claims the free inline space so the secondary cluster
 * and the compact overflow trigger dock to the trailing edge; both stay
 * shrinkable (min-width:0) so a long verb label wraps inside its zone instead of
 * widening the toolbar and forcing the row to scroll.
 */
.bao-toolbar-primary {
  flex: 1 1 auto;
  min-width: 0;
}
.bao-toolbar-secondary {
  flex: 0 1 auto;
  min-width: 0;
  justify-content: flex-end;
}/*
 * FilterBar secondary dimensions (status,
priority,
…) as opposed to the
 * primary search field. They pair up beneath the full-width search on the
 * compact 2-column grid,
and the floor keeps each control at the 44px Apple HIG
 * target once its legend is painted.
 */
.bao-filter-secondary {
  min-width: 0;
}.bao-filter-secondary > .select,
.bao-filter-secondary > select,
.bao-filter-secondary > .input,
.bao-filter-secondary > input {
  min-height: var(--touch-target-min, 2.75rem);
  min-width: 0;
  width: 100%;
}/*
 * Overflow host. At >=48rem it stays display:contents so its children take part
 * directly in the FilterBar grid. On compact it must become a real box,
or the
 * col-span-full it carries has nothing to apply to — display:contents generates
 * no box and cannot be a grid item. Becoming a grid row is what lets collapsed
 * secondary dimensions stack full width under the search.
 */
@media (max-width: 47.99rem) {
  .bao-filter-overflow {
    display: grid;
    grid-column: 1 / -1;
    gap: var(--space-micro);
    min-width: 0;
  }
}

/*
 * Collection FilterBar — sticky filter band under page header / journey host.
 * Primitive classes carry sticky;this pins z-index + solid surface so table
 * scroll never paints over filter controls (one owner,
every collection page).
 */
[data-primitive="FilterBar"] {
  position: sticky;
  top: 0;
  z-index: calc(var(--z-sticky, 1100) - 1);
  background-color: var(--bg-panel, var(--color-base-100));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/*
 * Toolbar phone overflow — secondary zone hides; overflow trigger opens a
 * bottom sheet with full-width touch targets (Apple HIG dense toolbar).
 */
@media (max-width: 47.99rem) {
  [data-primitive="Toolbar"] .bao-toolbar-secondary {
    display: none;
  }
  [data-primitive="Toolbar"] [data-toolbar-overflow]:not(:checked) ~ .bao-toolbar-overflow-panel {
    display: none;
  }
  [data-primitive="Toolbar"] [data-toolbar-overflow]:checked ~ .bao-toolbar-overflow-panel {
    display: flex;
  }
  [data-primitive="Toolbar"] .bao-toolbar-overflow-panel {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: var(--z-modal, 50);
    flex-direction: column;
    gap: var(--space-micro);
    padding: var(--space-section);
    padding-block-end: max(var(--space-section), env(safe-area-inset-bottom, 0px));
    background: var(--color-base-100, var(--bg-panel));
    border-block-start: 1px solid color-mix(in oklab, var(--color-base-content) 12%, transparent);
    box-shadow: 0 -4px 24px color-mix(in oklab, var(--color-base-content) 8%, transparent);
  }
  [data-primitive="Toolbar"] .bao-toolbar-overflow-panel .btn {
    width: 100%;
    justify-content: flex-start;
    min-height: var(--touch-target-min, 2.75rem);
  }
}


/*
 * App-shell drawer host — mobile-first, WebView-hardened (BlueStacks ships
 * Chromium ~101; do not rely solely on daisyUI is-drawer-close variants).
 * - Side rail is position:fixed off-canvas until the checkbox opens it
 * - ≥80rem permanent in-flow rail (never daisyUI lg:drawer-open)
 * - Content fills remaining track; no stacked dual sidebars
 */
.bao-shell-drawer {
  position: relative;
  display: flex;
  width: 100%;
  max-width: 100%;
  overflow: hidden;
  container-type: inline-size;
  container-name: shell;
}

.bao-shell-drawer > .drawer-toggle {
  position: fixed;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}

.bao-shell-drawer > .drawer-content {
  display: flex;
  min-width: 0;
  flex-direction: column;
  width: 100%;
  max-width: 100%;
}

/*
 * Closed rail host: fixed, fully opaque host (children own the slide).
 * daisyUI sets opacity:0% / visibility:hidden on .drawer-side — reset both.
 */
.bao-shell-drawer > .drawer-side {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  /*
   * Off-canvas rank: above the mobile dock (--z-overlay: 1200) AND above the
   * sticky topbar (--z-shell-topbar: 1250). Neither may paint over an open
   * rail/scrim. One named rank, shared with the peek overlay below, so the
   * two transient rail presentations can never disagree.
   */
  z-index: var(--z-shell-drawer-overlay, 1300);
  display: flex;
  flex-direction: column;
  width: min(var(--shell-drawer-width), var(--shell-drawer-max-viewport));
  max-width: var(--shell-drawer-width);
  height: 100%;
  margin: 0;
  translate: none;
  opacity: 1;
  visibility: visible;
  pointer-events: none;
  overscroll-behavior: contain;
  background-color: transparent;
  transition-property: none; transition-duration: var(--duration-instant, 0ms);
}

/*
 * Panel slides; host stays painted so open/close is not an opacity fade.
 * Background MUST stay transparent: #app-sidebar owns Liquid Glass material
 * (bao-surface-glass-*). A solid base-100 here kills glass (Apple dual-layer ban
 * is glass-on-glass; solid-over-glass is also wrong — it replaces the material).
 * Reduced-transparency a11y solidifies the glass class itself (tokens-glass-a11y).
 */
.bao-shell-drawer > .drawer-side > *:not(.drawer-overlay) {
  pointer-events: none;
  height: 100%;
  width: 100%;
  max-width: 100%;
  /* Closed: clip layout overflow so mobile smoke does not count off-canvas nav. */
  overflow: hidden;
  visibility: hidden;
  overscroll-behavior: contain;
  /* Transparent — #app-sidebar glass material must show; never solid-over-glass. */
  background: transparent;
  color: var(--text-primary, var(--color-base-content));
  transform: translate3d(-100%, 0, 0);
  translate: none;
  transition-property: transform; transition-duration: var(--duration-normal, 200ms); transition-timing-function: var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
}

/* Promote during the open/close transition only — reverts to auto at rest. */
.bao-shell-drawer:has(> .drawer-toggle:checked) > .drawer-side > *:not(.drawer-overlay),
.bao-shell-drawer:has(> .drawer-toggle:focus) > .drawer-side > *:not(.drawer-overlay) {
  will-change: transform;
}

.bao-shell-drawer:not(:has(> .drawer-toggle:checked)):not(:has(> .drawer-toggle:focus))
  > .drawer-side
  > *:not(.drawer-overlay) {
  will-change: auto;
}

.bao-shell-drawer > .drawer-side > .drawer-overlay {
  position: fixed;
  inset: 0;
  /* Scrim must cover the viewport for tap-outside-to-close.
     z-index -1 painted under the rail panel but still receives hits
     across the full viewport once pointer-events are enabled. */
  z-index: 0;
  /* Theme-safe scrim — never raw black-only; readable on light/dark. */
  background: color-mix(in oklab, var(--color-base-content) 40%, transparent);
  opacity: 0;
  pointer-events: none;
  transition-property: opacity; transition-duration: var(--duration-normal, 200ms); transition-timing-function: var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
  cursor: pointer;
}

/* Open: checkbox checked (mobile menu / dock More) */
.bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side {
  pointer-events: auto;
  /* Expand hit host to full viewport so the fixed scrim is reachable. */
  width: 100%;
  max-width: none;
}

/* Hide dock while the rail owns the viewport — prevents dock-on-scrim stacking. */
.bao-shell-drawer > .drawer-toggle:checked ~ .drawer-content .bao-mobile-dock,
.bao-shell-drawer:has(> .drawer-toggle:checked) .bao-mobile-dock {
  visibility: hidden;
  pointer-events: none;
}

.bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > *:not(.drawer-overlay) {
  pointer-events: auto;
  overflow-y: auto;
  visibility: visible;
  transform: translate3d(0, 0, 0);
  translate: none;
  /* Keep panel at mobile rail width while host spans viewport for scrim. */
  width: min(var(--shell-drawer-width), var(--shell-drawer-max-viewport));
  max-width: var(--shell-drawer-width);
  z-index: 1;
}

.bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > .drawer-overlay {
  opacity: 1;
  pointer-events: auto;
}

/*
 * Permanent rail from 80rem (1280px) only.
 * Phones report 1080 CSS px (hits Tailwind lg: 1024) — must stay off-canvas.
 * Do not use daisyUI lg:drawer-open on phones; this media query is the SSOT.
 *
 * Topbar hamburger SSOT (matches webview-drawer; single owner):
 * - Phone (<48rem): dock "More" opens drawer — hide header hamburger.
 * - Tablet (48–79.99rem): persistent icon rail owns nav — hamburger retired
 *   (the drawer retires in this band; see the tablet rail block below).
 * - Permanent rail (≥80rem): hide header hamburger — #desktop-sidebar-collapse
 *   in the sidebar owns collapse (no dual collapse controls).
 */


/*
 * Sidebar peek affordance (imp-t02) — geometry SSOT. Hidden by default; the
 * tablet band (48–79.99rem) reveals it while the rail is unpinned, and the
 * peek/pinned states below retire it again. Fixed at the rail's inline-end
 * edge, block-centered, above page content but under rails/overlays.
 */
/*
 * Docked to the RAIL COLUMN, never over content. Previously this was pinned to
 * the rail's inline-end edge and block-centered — but the rail ends exactly
 * where the content column begins, so the whole 44px target sat ON the content
 * and covered ~7 nodes including the data panel at every tablet width.
 *
 * Centring it inside the rail instead would cover a nav icon, since the rail is
 * icons top-to-bottom. The resolution is to occupy the rail's full width at its
 * block-end AND reserve the same amount of scroll padding on the rail (below),
 * so the affordance owns dedicated space and overlaps neither content nor an
 * icon — the in-flow guarantee without moving it in the DOM.
 */
.bao-shell-sidebar-peek-trigger {
  position: fixed;
  inset-inline-start: 0;
  inset-block-end: 0;
  width: var(--sidebar-collapsed);
  min-height: var(--touch-target-min, 2.75rem);
  z-index: var(--z-content-overlay, 10);
  border-radius: 0;
}
/*
 * Visibility ladder. The trigger carries the btn class, whose shell-wide
 * .btn:not([hidden]):not(.hidden) display rule is (0,3,0) — it silently
 * re-asserted inline-flex over a (0,1,0) display:none, so the affordance
 * painted at DESKTOP on top of the expanded sidebar's nav items (it covered
 * the "Chat" entry at 90rem). Every rung repeats the class to outrank it,
 * mirroring the .bao-fab precedent in tokens-layout-dock-base.ts.
 */
.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger {
  display: none;
}

/*
 * Tablet band (48–79.99rem): persistent in-flow icon rail — the canonical
 * dock → rail → sidebar transformation (design.pdf §8.3). The rail IS the
 * collapsed sidebar vocabulary rendered in-flow: grid track at
 * --sidebar-collapsed, labels/titles hidden, tooltips carry names. Mirrors
 * the .bao-sidebar-collapsed SSOT rules in tokens-layout-editor-workbench.ts
 * so the band reads identically whether the stored pref is expanded or
 * collapsed; the band (not the pref) owns rail state, so the hamburger and
 * the in-rail collapse toggle both retire here.
 */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  .bao-shell-drawer {
    display: grid;
    grid-template-columns: var(--sidebar-collapsed) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
  }

  .bao-shell-drawer > .drawer-content {
    grid-column: 2;
    grid-row: 1;
    width: auto;
    max-width: none;
    min-height: 0;
  }

  .bao-shell-drawer > .drawer-side,
  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side {
    position: relative;
    inset: auto;
    grid-column: 1;
    grid-row: 1;
    z-index: 1;
    width: 100%;
    max-width: none;
    height: 100%;
    min-height: 0;
    align-self: stretch;
    translate: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    overflow: hidden;
  }

  .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay),
  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > *:not(.drawer-overlay) {
    pointer-events: auto;
    visibility: visible;
    overflow-y: auto;
    transform: none;
    translate: none;
    will-change: auto;
    transition-property: none; transition-duration: var(--duration-instant, 0ms);
    height: 100%;
    width: 100%;
    max-width: none;
  }

  .bao-shell-drawer > .drawer-side > .drawer-overlay,
  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > .drawer-overlay {
    display: none;
    opacity: 0;
    pointer-events: none;
  }

  /* Solid content-plane rail (same treatment as the ≥80rem permanent
     sidebar): glass stays on off-canvas overlay + transient chrome only.
     Padding stays with .bao-shell-sidebar/p-space-micro — no shorthand here
     or the safe-area-aware block padding is clobbered on notched tablets. */
  #app-sidebar,
  #app-sidebar.bao-surface-glass-regular,
  #app-sidebar.bao-sidebar-expanded,
  #app-sidebar.bao-sidebar-collapsed {
    width: 100%;
    overflow-x: visible;
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  #app-sidebar.bao-surface-glass-regular::before,
  #app-sidebar.bao-surface-glass-regular::after {
    display: none;
  }

  /* Icon-rail presentation (mirror of the .bao-sidebar-collapsed vocabulary). */
  #app-sidebar .menu-title,
  #app-sidebar [data-sidebar-expanded-only] {
    display: none;
  }
  #app-sidebar .bao-nav-item {
    justify-content: center;
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    padding-inline: 0;
  }
  #app-sidebar .bao-nav-item.tooltip::before,
  #app-sidebar .bao-nav-item.tooltip::after {
    display: block;
  }

  /* One affordance per function: the band owns rail state. */
  #app-header .bao-drawer-toggle-control,
  #app-header [data-drawer-toggle],
  #app-sidebar [data-sidebar-rail-toggle] {
    display: none;
  }

  /*
   * Sidebar peek (imp-t02): the edge affordance rides the collapsed rail
   * while unpinned; peeking or pinning retires it (the expanded sidebar
   * itself then owns the pin control).
   */
  .bao-shell-drawer .bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger {
    display: inline-flex;
  }
  .bao-shell-drawer.bao-shell-sidebar-peek .bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger,
  .bao-shell-drawer.bao-shell-sidebar-pinned .bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger.bao-shell-sidebar-peek-trigger {
    display: none;
  }

  /*
   * Peeking: the rail leaves its grid track and floats as a non-modal overlay
   * at the expanded width — the grid template is untouched, so content NEVER
   * reflows. No scrim: unhover/Escape/tap-outside dismiss (non-modal overlay,
   * not a dialog). Motion: the band already sets transition-property:none on
   * the rail panel, so the overlay appears instantly (reduced-motion safe).
   */
  .bao-shell-drawer.bao-shell-sidebar-peek > .drawer-side,
  .bao-shell-drawer.bao-shell-sidebar-peek > .drawer-toggle:checked ~ .drawer-side {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    /*
     * --z-overlay (1200) ranked BELOW --z-shell-topbar (1250), so the topbar
     * painted over the top 46px of the peek panel: its glass plate and the
     * workspace switcher covered the rail brand row and the first nav items
     * (measured 200x46px of occlusion at 900px wide). A panel that overlays
     * content must outrank the chrome it overlays — same rank as the
     * off-canvas rail above.
     */
    z-index: var(--z-shell-drawer-overlay, 1300);
    width: var(--sidebar-expanded);
    max-width: var(--sidebar-expanded);
    box-shadow: var(--shadow-lg);
  }

  /*
   * Pinned (explicit expanded preference via the existing rail toggle + the
   * ui.sidebar.expanded seam): the band yields its fixed icon track to the
   * expanded track. The pin is a deliberate user action — reflow is intended.
   */
  .bao-shell-drawer.bao-shell-sidebar-pinned {
    grid-template-columns: var(--sidebar-expanded) minmax(0, 1fr);
  }

  /*
   * Expanded vocabulary while peeking/pinned — mirrors the ≥80rem expanded
   * sidebar: group titles and labels paint, nav items align inline-start.
   * Scoped selectors (drawer class + #app-sidebar) outrank the band's
   * icon-rail presentation rules above.
   */
  .bao-shell-drawer.bao-shell-sidebar-peek #app-sidebar .menu-title,
  .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .menu-title {
    display: list-item;
  }
  .bao-shell-drawer.bao-shell-sidebar-peek #app-sidebar [data-sidebar-expanded-only],
  .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-expanded-only] {
    display: inline;
  }
  .bao-shell-drawer.bao-shell-sidebar-peek #app-sidebar .bao-nav-item,
  .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item {
    justify-content: flex-start;
    padding-inline: var(--space-inline, 0.5rem);
  }
  /* The pin control lives inside the expanded sidebar while peeking/pinned. */
  .bao-shell-drawer.bao-shell-sidebar-peek #app-sidebar [data-sidebar-rail-toggle],
  .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-rail-toggle] {
    display: inline-flex;
  }
}


@media (min-width: 80rem) {

  .bao-drawer-toggle-control {
    display: none;
  }

  /*
   * Dock ↔ permanent-rail CQ ownership (viewport rem SSOT, unlayered):
   * phone (<48rem) paints #mobile-dock; tablet (48–79.99rem) paints the
   * in-flow icon rail (dock already hidden in tokens-layout-dock-base);
   * ≥80rem replaces both with the full in-flow left sidebar. Must stay unlayered —
   * @layer utilities hide was dropped on WebView 101 so the dock still
   * painted at 1280 beside the rail.
   */

  /* Right session rail: CSS owns visibility (never Tailwind xl:flex). */
  #session-rail.bao-shell-session-rail,
  #session-rail {
    display: flex;
  }

  /* Session-rail sheet trigger retires once the permanent rail is in-flow.
     The selector must carry the full icon-geometry weight
     (#id + .navbar-end + .bao-shell-topbar-actions + attr + .btn +
     .bao-shell-icon-action). The plain "#app-header [attr]" form here scored
     (1,1,0) and lost every cascade to the unlayered icon-geometry rules at
     (1,4,0), so this hide NEVER applied: the phone sheet trigger shipped in
     the desktop topbar next to the permanent rail's own collapse control —
     two visible session-rail affordances at >=80rem. The <40rem band in
     tokens-glass-shell-topbar-zones-narrow.ts already matches this weight;
     this band had been left behind. */
  #app-header .navbar-end .bao-shell-topbar-actions [data-session-rail-sheet-open].btn.bao-shell-icon-action {
    display: none;
  }

  /*
   * These two style the shell host itself, so this band must stay @media.
   * .bao-shell-drawer declares container-name: shell, and a container query
   * never matches the element establishing the container — nesting them in
   * @container shell (min-width: 80rem) dropped them silently: base
   * display:flex won, .drawer-side kept width:100%, .drawer-content
   * collapsed to 0px, and the desktop shell painted as a full-viewport
   * sidebar with no content.
   */
  .bao-shell-drawer {
    display: grid;
    /* The rail grid track is the ONE owner of sidebar width. Collapse swaps
       the track var (drawer-level class from applySidebarRailState) — aside
       children are width:100% of the track and must never fight it. */
    grid-template-columns: var(--shell-rail-track, minmax(14rem, 16rem)) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
  }

  .bao-shell-drawer.bao-shell-rail-collapsed {
    --shell-rail-track: var(--sidebar-collapsed);
  }
  /*
   * Collapsing the rail narrowed the grid track and nothing else, so the nav
   * kept painting full-width labels inside a 56px rail and the track clipped
   * them mid-word — "Primary" rendered as "Primar", "Annotation Review" as
   * "An". A collapsed rail is an ICON rail (design.pdf 8.3: medium width
   * becomes a navigation rail), so label text and section headings stop
   * painting at that width.
   *
   * Safe to remove from the box rather than sr-only-clip: every .bao-nav-item
   * carries its own aria-label AND the .tooltip class, so the accessible name
   * survives and hover/focus still reveals the text (9.6 "icon-only controls
   * must retain accessible names").
   */
  .bao-shell-drawer.bao-shell-rail-collapsed .bao-nav-item > span {
    display: none;
  }
  .bao-shell-drawer.bao-shell-rail-collapsed .menu-title {
    display: none;
  }
  .bao-shell-drawer.bao-shell-rail-collapsed .bao-nav-item {
    justify-content: center;
    padding-inline: var(--space-micro, 0.25rem);
  }
  /*
   * The 44px touch floor on .bao-nav-item is non-negotiable, so the list
   * padding has to yield instead: the expanded rail's inline padding left the
   * item starting 17px in, pushing its right edge to 61px inside a 56px rail
   * and clipping the glyph. Trim the inset at rail width so the full target
   * fits (design.pdf 8.2 large touch areas).
   */
  .bao-shell-drawer.bao-shell-rail-collapsed .menu,
  .bao-shell-drawer.bao-shell-rail-collapsed .bao-shell-sidebar > nav,
  .bao-shell-drawer.bao-shell-rail-collapsed .bao-shell-sidebar {
    padding-inline: var(--space-micro, 0.25rem);
  }

  .bao-shell-drawer > .drawer-toggle {
    display: none;
  }

  .bao-shell-drawer > .drawer-content {
    grid-column: 2;
    grid-row: 1;
    width: auto;
    max-width: none;
    min-height: 0;
  }

  .bao-shell-drawer > .drawer-side {
    position: relative;
    inset: auto;
    grid-column: 1;
    grid-row: 1;
    z-index: 1;
    width: 100%;
    max-width: none;
    height: 100%;
    min-height: 0;
    align-self: stretch;
    translate: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    flex-shrink: 0;
    overflow: hidden;
  }

  /*
   * Permanent rail is never toggle-checked. Base closed rules leave the panel
   * at pointer-events:none + visibility:hidden — without this override the
   * entire sidebar is dead on desktop (clicks fall through).
   */
  .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay) {
    pointer-events: auto;
    visibility: visible;
    overflow-y: auto;
    /* Closed-state off-canvas shift uses transform (not translate) — reset
       BOTH or the permanent rail renders as an empty reserved track. */
    transform: none;
    translate: none;
    will-change: auto;
    transition-property: none; transition-duration: var(--duration-instant, 0ms);
    height: 100%;
    width: 100%;
    max-width: none;
  }

  .bao-shell-drawer > .drawer-side > .drawer-overlay {
    display: none;
    pointer-events: none;
  }

  /* Permanent left sidebar (≥80rem) is solid content-plane; glass stays on
     off-canvas overlay below 80rem and transient chrome only. Removes one
     dominant-plane blur and the full-height permanent backdrop cost. */
  #app-sidebar.bao-surface-glass-regular {
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  #app-sidebar.bao-surface-glass-regular::before,
  #app-sidebar.bao-surface-glass-regular::after {
    display: none;
  }

  /* Permanent right session rail (≥80rem) is a dense list pane — solid base;
     glass only when the rail is not a permanent divider. */
  #session-rail.bao-surface-glass-regular {
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  #session-rail.bao-surface-glass-regular::before,
  #session-rail.bao-surface-glass-regular::after {
    display: none;
  }
}

/* Below permanent-rail: session rail out of flow (CSS SSOT, not xl:).
 * display:none + visibility:hidden so HTMX intersect never fires and AT
 * skips the complementary pane until ≥80rem. JS mirrors inert (HTML). */
@media (max-width: 79.99rem) {
  #session-rail.bao-shell-session-rail,
  #session-rail {
    display: none;
    visibility: hidden;
    pointer-events: none;
  }

  /* RTL: off-canvas from the inline end (mobile / tablet only) */
  [dir="rtl"] .bao-shell-drawer > .drawer-side {
    inset-inline-start: auto;
    inset-inline-end: 0;
  }

  [dir="rtl"] .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay) {
    transform: translate3d(100%, 0, 0);
  }

  [dir="rtl"] .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > *:not(.drawer-overlay) {
    transform: translate3d(0, 0, 0);
  }
}

/* RTL tablet band: the in-flow icon rail never sits off-canvas — reset the
   RTL off-canvas shift above (equal specificity; later source order wins). */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  [dir="rtl"] .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay),
  [dir="rtl"] .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > *:not(.drawer-overlay) {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay),
  .bao-shell-drawer > .drawer-side > .drawer-overlay {
    transition-property: none; transition-duration: var(--duration-instant, 0ms);
  }
}

/* Product Settings → reduced motion must match OS prefers-reduced-motion. */
html[data-reduced-motion="true"] .bao-shell-drawer > .drawer-side > *:not(.drawer-overlay),
html[data-reduced-motion="true"] .bao-shell-drawer > .drawer-side > .drawer-overlay {
  transition-property: none; transition-duration: var(--duration-instant, 0ms);
}

/*
 * Sidebar capability-empty state. The nav list is a .menu, so the empty row has
 * to opt out of menu item geometry (hover/active plate, inline padding) or it
 * paints as a misleading, hoverable destination. The row also stops being a flex item
 * that stretches to the rail width so the centred copy stays readable while the
 * rail is collapsed.
 */
.bao-sidebar-empty-item {
  display: block;
  width: 100%;
  min-width: 0;
  /* Kill .menu li hover/active plate for every pointer — no :hover gate needed. */
  background: none;
}
.bao-sidebar-empty {
  min-width: 0;
  border-radius: var(--radius-field, 0.75rem);
  background: var(--bg-panel, var(--color-base-100));
  color: var(--text-secondary, var(--color-base-content));
}

/*
 * Tablet band (48–79.99rem): end the rail's SCROLLPORT above the sidebar peek
 * affordance. The trigger is fixed at the rail column's block-end (geometry
 * SSOT in tokens-glass-shell-drawer.ts).
 *
 * Trailing padding is NOT sufficient here: the rail scrolls, so padding only
 * reserves space at the end of the list while any mid-list item still scrolls
 * underneath the fixed control (measured: it covered the Billing and HL7 nav
 * items). Shortening the scrollport itself is what guarantees no nav item can
 * ever sit beneath it — the in-flow footer guarantee, without moving the
 * trigger in the DOM.
 */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  .bao-shell-drawer #app-sidebar {
    /* The rail ships h-full AND min-h-full; min-height wins over height, so
       shrinking the scrollport requires overriding all three box constraints. */
    height: calc(100% - var(--touch-target-min, 2.75rem));
    min-height: calc(100% - var(--touch-target-min, 2.75rem));
    max-height: calc(100% - var(--touch-target-min, 2.75rem));
  }
}



/* A phone More sheet is an expanded touch menu, independent of the persisted rail preference. */
@media (max-width: 47.99rem) {
  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > #app-sidebar {
    width: 100%;
    max-width: none;
    padding: var(--space-section, 0.75rem);
  }

  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > #app-sidebar [data-sidebar-expanded-only] {
    display: inline;
  }

  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > #app-sidebar .menu-title {
    display: list-item;
  }

  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > #app-sidebar .bao-nav-item {
    justify-content: flex-start;
    padding-inline: var(--space-inline, 0.5rem);
  }

  .bao-shell-drawer > .drawer-toggle:checked ~ .drawer-side > #app-sidebar [data-sidebar-rail-toggle] {
    display: none;
  }
}



/*
 * WebView 101 (BlueStacks Chromium ~101) does not support color-mix() or :has().
 * Solid token paints from tokens.bao (theme-palette-from-tokens); progressive
 * color-mix enhancements after. daisyUI theme :has() is stripped at emit.
 */
[data-theme="capture-dx"] {
  color-scheme: light;
  --color-accent: #F56E28;
  --color-accent-content: #18181b;
  --color-base-100: #ffffff;
  --color-base-content: #18181b;
  --color-base-200: #f0f1f3;
  --color-base-300: #dfe1e5;
  --color-error: #b91c1c;
  --color-error-content: #ffffff;
  --color-info: #0369a1;
  --color-info-content: #ffffff;
  --color-neutral: #18181b;
  --color-neutral-content: #ffffff;
  --color-primary: #1E73BE;
  --color-primary-content: #ffffff;
  --color-secondary: #f0f1f3;
  --color-secondary-content: #18181b;
  --color-success: #065f46;
  --color-success-content: #ffffff;
  --color-warning: #b45309;
  --color-warning-content: #ffffff;
  --bg-app: #F7F8F9;
  --bg-panel: #ffffff;
  --bg-surface: #f0f1f3;
  --bg-surface-hover: #e2e4e7;
  --border-base: #dfe1e5;
  --text-primary: #18181b;
  --text-secondary: #52525b;
  --text-tertiary: #67676f;
}
[data-theme="capture-dx-dark"] {
  color-scheme: dark;
  --color-accent: #F56E28;
  --color-accent-content: #18181b;
  --color-base-100: #151b24;
  --color-base-content: #f4f4f5;
  --color-base-200: #1e2633;
  --color-base-300: #2d3847;
  --color-error: #f87171;
  --color-error-content: #0c1017;
  --color-info: #41B6E6;
  --color-info-content: #0c1017;
  --color-neutral: #f4f4f5;
  --color-neutral-content: #151b24;
  --color-primary: #41B6E6;
  --color-primary-content: #0c1017;
  --color-secondary: #1e2633;
  --color-secondary-content: #f4f4f5;
  --color-success: #059669;
  --color-success-content: #0c1017;
  --color-warning: #d97706;
  --color-warning-content: #18181b;
  --bg-app: #0c1017;
  --bg-panel: #151b24;
  --bg-surface: #1e2633;
  --bg-surface-hover: #2d3847;
  --border-base: #2d3847;
  --text-primary: #f4f4f5;
  --text-secondary: #a1a1aa;
  --text-tertiary: #8b8b96;
}

/* Ambient field behind glass chrome so blur reads as liquid glass so material blur remains perceptible.
   Soft color fields only — never high-frequency texture under L1 chrome. */
.bao-shell-ambient {
  background: var(--bg-app, var(--color-base-100));
}
.bao-shell-ambient {
  background:
    radial-gradient(
      ellipse 120% 70% at 2% -20%,
      color-mix(in oklab, var(--color-primary) 26%, transparent),
      transparent 68%
    ),
    radial-gradient(
      ellipse 90% 58% at 106% 4%,
      color-mix(in oklab, var(--color-secondary) 20%, transparent),
      transparent 62%
    ),
    radial-gradient(
      ellipse 70% 50% at 48% 118%,
      color-mix(in oklab, var(--color-info) 14%, transparent),
      transparent 66%
    ),
    radial-gradient(
      ellipse 48% 34% at 74% 30%,
      color-mix(in oklab, var(--color-accent, var(--color-primary)) 10%, transparent),
      transparent 72%
    ),
    linear-gradient(
      165deg,
      color-mix(in oklab, var(--color-base-200) 70%, transparent) 0%,
      transparent 50%
    ),
    var(--bg-app, var(--color-base-100));
  background-attachment: fixed;
}

/* Elevation from the canonical token (theme-aware via --glass-shadow-*),
   plus the shared inset top-light line — no bespoke shadow recipes. */
.bao-surface-glass-regular.bao-glass-large,
[data-theme="capture-dx-dark"] .bao-surface-glass-regular.bao-glass-large {
  box-shadow:
    var(--shadow-glass-floating),
    0 1px 0 0 color-mix(in oklab, var(--glass-highlight-strong) 70%, transparent) inset;
}

details.dropdown:not([open]) > .dropdown-content {
  display: none;
}

/*
 * daisyUI 5 only paints .dropdown-content on .dropdown-open / :focus-within.
 * Android WebView opens <details> without focusing the summary, so the menu
 * stayed at opacity 0. Force open visibility from the native open attribute.
 *
 * align-content is load-bearing, not decoration. This rule replaces daisyUI's
 * .menu (display: flex; flex-direction: column) with a grid, and a grid whose
 * align-content is left at the initial 'normal' resolves to STRETCH: every
 * implicit row is sized by the panel box instead of by its own content. The
 * panel is content-sized today only by accident — nothing currently resolves
 * both of its block edges — so the defect stayed latent until a host pinned
 * the panel's block size, and then the rows split it evenly between them.
 *
 * Measured at 390x844 on /capture with the two-row page-toolbar overflow, the
 * panel forced to a definite 600px block size: rows landed at y=156 and y=453
 * (297px apart — 600/2 per track) with blank space between and after them,
 * while the controls stayed 44px tall. The reported production panel is the
 * same arithmetic one size up: 1340px tall, two rows, second row ~670px below
 * the first. With align-content: start the same 600px box lays the rows at
 * y=156 and y=204 — 44px plus the 4px gap, a uniform list — and the panel
 * collapses back to its content height wherever the block size is free.
 *
 * A menu's height is its content's height. Declare it; never infer it.
 */
details.dropdown[open] > .dropdown-content,
details.dropdown.dropdown-open > .dropdown-content {
  display: grid;
  align-content: start;
  opacity: 1;
  scale: 1;
  visibility: visible;
  pointer-events: auto;
  z-index: var(--z-dropdown, 1000);
}

/*
 * Compact scrim behind an open menu (§8.6: popovers become sheets on compact).
 *
 * Measured at 390x844 and 320x720: every open dropdown panel — page-toolbar
 * overflow, account menu, capture overflow, row overflow — floated over a live,
 * fully lit page with nothing separating the layers and nothing to dismiss
 * against. The panel's own backdrop-filter blurred what was directly behind it
 * and stopped there.
 *
 * The scrim is the panel's own ::backdrop, not a rendered element: the shell
 * dropdown controller already promotes open panels to the top layer via
 * popover, so the pseudo-element costs no markup and no class strings composed
 * in TypeScript. It is also not hit-testable — measured with
 * elementFromPoint(40, panelBottom + 200), which resolves to the page content
 * underneath with and without the scrim — so the controller's outside-click
 * dismissal keeps working unchanged (verified: the menu closes on a scrim tap).
 *
 * Recipe mirrors .bao-filter-sheet-backdrop (the canonical compact scrim) so
 * both sheets read as one material. Phone band only: a pointer-first dropdown
 * on tablet/desktop is an anchored popover, and HIG does not scrim those.
 */
@media (max-width: 47.99rem) {
  .bao-dropdown-panel:popover-open::backdrop {
    background-color: color-mix(in oklab, var(--color-base-100) 60%, transparent);
    backdrop-filter: blur(var(--liquid-blur-clear, 8px));
    -webkit-backdrop-filter: blur(var(--liquid-blur-clear, 8px));
  }
  @media (forced-colors: active) {
    .bao-dropdown-panel:popover-open::backdrop {
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
  }
}

/*
 * Host-relative end anchoring, for the ABSOLUTELY POSITIONED panel only.
 *
 * :not(.bao-dropdown-panel-viewport) is load-bearing. app-shell-dropdowns
 * promotes an open panel to position: fixed in the popover top layer and
 * publishes trigger-anchored geometry in --bao-dropdown-left /
 * --bao-dropdown-right. Once the panel is fixed, inset-inline-end: 0 resolves
 * against the VIEWPORT rather than the host, so this rule silently discarded
 * that geometry and pinned every end-aligned menu to the window edge —
 * measured at 1440x900 on a Cases row overflow, the trigger's right edge sat
 * at x=1353 while the panel's landed at x=1440, 87px adrift, with
 * --bao-dropdown-right: 252.27px computed and thrown away.
 *
 * Specificity could never have settled this: the rule is unlayered and the
 * anchoring rule it outranked sits in @layer utilities, which loses to
 * unlayered no matter how many classes it repeats. Scoping by mode gives each
 * positioning scheme exactly one owner instead.
 */
.dropdown.dropdown-end > .dropdown-content:not(.bao-dropdown-panel-viewport) {
  inset-inline-end: 0;
  inset-inline-start: auto;
}

/* overflow-x-auto on navbar-end clips absolute dropdown panels on phones. */
#app-header .navbar-end {
  overflow: visible;
}

#app-header.navbar,
#app-header[data-shell-topbar] {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-micro, 0.25rem);
  width: 100%;
  /* Density SSOT: chrome-min-height + safe-area only from the canonical chrome density tokens. */
  min-height: calc(var(--chrome-min-height, 2.25rem) + env(safe-area-inset-top, 0px));
  /*
   * Never use padding-block here — it overwrites safe-area inset-top and
   * pulls workspace chips under the status bar / front camera cutout.
   */
  padding-top: max(var(--safe-area-inset-top, 0px), env(safe-area-inset-top, 0px));
  padding-bottom: 0;
  padding-inline: max(var(--safe-area-inset-left, var(--space-inline, 0.75rem)), env(safe-area-inset-left, 0px))
    max(var(--safe-area-inset-right, var(--space-inline, 0.75rem)), env(safe-area-inset-right, 0px));
  /* Solid first — WebView 101 drops color-mix declarations entirely. */
  background-color: var(--bg-panel, var(--color-base-100));
  border-bottom: 1px solid var(--border-base);
  box-sizing: border-box;
  backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
  -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
}
/* WebView 101: color-mix declarations are dropped there, so the solid panel
 * fill above is the resting style. Capable engines re-enable the translucent
 * glass fill via feature detection through the canonical capability rule, and
 * strengthen the tint while content scrolls beneath when content reaches the scroll edge.
 * Translucency stays off for every reduced-transparency / increased-contrast
 * signal — OS preference and product toggle alike for every accessibility preference signal. */
#app-header.navbar,
#app-header[data-shell-topbar] {
  background-color: var(--bg-panel, var(--color-base-100));
  background-image: none;
}
@supports (background-color: color-mix(in oklab, red 50%, transparent)) {
  @media (prefers-reduced-transparency: no-preference) and (prefers-contrast: no-preference) {
    html:not([data-reduced-transparency="true"]):not([data-high-contrast="true"]) #app-header.navbar,
    html:not([data-reduced-transparency="true"]):not([data-high-contrast="true"]) #app-header[data-shell-topbar] {
      background-color: color-mix(in oklab, var(--bg-panel) calc(var(--liquid-fill-persistent) * 100%), transparent);
    }
    html:not([data-reduced-transparency="true"]):not([data-high-contrast="true"]) #app-header[data-shell-topbar][data-scroll-edge="true"] {
      background-color: color-mix(in oklab, var(--bg-panel) calc(var(--liquid-fill-transient) * 100%), transparent);
      backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
      -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-strong));
    }
  }
}

/* Accessibility preference modes must win over the topbar's ID-specificity
 * chrome above: drop the (now pointless) blur behind the solid fill and give
 * increased contrast a stronger bottom edge for every accessibility preference signal. */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  #app-header.navbar,
  #app-header[data-shell-topbar],
  .bao-glass-control,
  .dropdown-content {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
@media (prefers-reduced-transparency: reduce) {
  .bao-glass-control {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    background: var(--bg-panel, var(--color-base-100));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
html[data-reduced-transparency="true"] #app-header.navbar,
html[data-reduced-transparency="true"] #app-header[data-shell-topbar],
html[data-high-contrast="true"] #app-header.navbar,
html[data-high-contrast="true"] #app-header[data-shell-topbar] {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
@media (prefers-contrast: more) {
  #app-header.navbar,
  #app-header[data-shell-topbar] {
    border-bottom-width: 2px;
  }
  .dropdown-content,
  .bao-glass-control {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: var(--liquid-border-contrast);
  }
}
html[data-high-contrast="true"] #app-header.navbar,
html[data-high-contrast="true"] #app-header[data-shell-topbar] {
  border-bottom-width: 2px;
}


/*
 * Topbar zone/strip/search/status rules live in ONE owner:
 * tokens-glass-shell-context-density.ts. This module keeps only
 * WebView-specific hardening that is not topbar chrome.
 */
/*
 * Keyboard cheatsheet control only — .btn.hidden permanently beats sm:inline-flex.
 * Theme toggle + voice must NEVER use this class (always-visible chrome).
 */
#app-header .bao-shell-kbd-control {
  display: none;
}
@media (min-width: 40rem) {
  #app-header .bao-shell-kbd-control {
    display: inline-flex;
  }
}/*
 * Topbar icon actions: BAO touch target + perfect center (theme swap,
voice,
user).
 * Icons live under .bao-shell-topbar-actions (not direct navbar-end children).
 * Full geometry SSOT: tokens-glass-shell-topbar-icons.ts (later in cascade).
 */
#app-header .bao-shell-topbar-actions {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2xs, 0.125rem);
}#app-header .navbar-end .bao-shell-icon-action.btn,
#app-header .navbar-end .bao-shell-topbar-actions .btn.bao-shell-icon-action,
#app-header .navbar-end details.dropdown > summary.bao-shell-icon-action,
#app-header .navbar-start .bao-shell-icon-action.btn {
  display: inline-grid;
  place-content: center;
  place-items: center;
  min-width: var(--touch-target-min, 2.75rem);
  min-height: var(--touch-target-min, 2.75rem);
  width: var(--touch-target-min, 2.75rem);
  height: var(--touch-target-min, 2.75rem);
  padding: 0;
  line-height: 0;
  flex-shrink: 0;
}

/*
 * Topbar hamburger:
 * - Phone (<48rem): dock "More" opens the drawer — hide duplicate hamburger.
 * - Tablet (48–79.99rem): persistent icon rail owns nav — hamburger retired
 *   (tokens-glass-shell-drawer.ts tablet band renders the rail in-flow).
 * - Permanent sidebar (≥80rem): hide hamburger — rail collapse lives in
 *   #app-sidebar [data-sidebar-rail-toggle] (no dual collapse controls).
 */
#app-header [data-drawer-toggle],
#app-header .bao-drawer-toggle-control {
  display: none;
}
@media (min-width: 80rem) {
  #app-header [data-drawer-toggle],
  #app-header .bao-drawer-toggle-control {
    display: none;
  }
}


@media (max-width: 63.99rem) {
  /*
   * Dual-title ban on phone (design.md §4 / §5.1): when content PageHeader
   * owns naming, keep shell title demoted. Restore shell title only when
   * shell mode owns naming (immersive/media demoted H1).
   */
  body[data-page-header-mode="shell"] #shell-page-title,
  body[data-page-header-mode="shell"] [data-shell-page-title] {
    position: static;
    width: auto;
    height: auto;
    max-width: 9rem;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip: auto;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  /* Page body breadcrumbs still hide on phone — H1 owns in-page naming. */
  [data-page-breadcrumbs] {
    display: none;
  }
}


#app-header details.dropdown > summary {
  list-style: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  cursor: pointer;
}

#app-header details.dropdown > summary::-webkit-details-marker {
  display: none;
}

#app-header [data-user-avatar],
#app-header .avatar-placeholder > span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  min-width: 1.75rem;
  min-height: 1.75rem;
  max-width: 1.75rem;
  max-height: 1.75rem;
  overflow: hidden;
  border-radius: var(--radius-full);
  background: var(--color-neutral);
  color: var(--color-neutral-content);
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  line-height: 1;
  text-overflow: clip;
  white-space: nowrap;
}

/* Topbar account control — square circle only (never expand to label width). */
#app-header details.dropdown[data-topbar-user-menu] > summary {
  width: 2.75rem;
  height: 2.75rem;
  overflow: hidden;
}
#app-header details.dropdown[data-topbar-user-menu] > summary .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Shell scroll chain — nested daisyUI drawer flex is ignored on WebView 101. */
html.h-full,
html {
  height: 100%;
  max-height: 100%;
  /* tokens.bao typography SSOT — platform-native Apple stack on every surface. */
  font-family: var(--font-sans);
}

body.bao-shell-ambient,
body {
  display: flex;
  flex-direction: column;
  height: 100%;
  max-height: 100%;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.bao-shell-drawer {
  flex: 1 1 0%;
  min-height: 0;
  max-height: 100%;
}

/*
 * These two are layout columns, not scrollports: `clip`, never `hidden`.
 *
 * `hidden` clips visually but still creates a scroll container the browser may
 * scroll programmatically — a focus() or scrollIntoView() on anything wider
 * than the column shoves the entire content column sideways, with no scrollbar
 * to bring it back. Measured on /cases at 1440x900: .drawer-content sat at
 * scrollLeft 23.5 and its child at 10, so #app-main painted at x=23 while the
 * 56px rail painted over it — elementFromPoint at the breadcrumb's leading
 * edge returned NAV#app-sidebar, and the breadcrumb lost its leading glyphs.
 *
 * Both axes, not just the inline one. CSS Overflow 3: when one axis is `clip`
 * and the other is neither `clip` nor `visible`, the `clip` computes back to
 * `hidden` — so `overflow-x: clip; overflow-y: hidden` is a no-op that still
 * measures as `hidden` on both. Neither column ever needed to scroll: #app-main
 * is the scrollport, and it is a descendant, so sticky offsets inside it are
 * unaffected by removing the scroll container here.
 *
 * The child already carries the `overflow-x-clip` utility, which asked for
 * exactly this — but that utility lives in @layer utilities while these rules
 * are unlayered, so the `overflow` shorthand silently overrode it.
 */
.bao-shell-drawer > .drawer-content {
  flex: 1 1 0%;
  min-height: 0;
  max-height: 100%;
  overflow: clip;
}

.bao-shell-drawer > .drawer-content > .relative {
  flex: 1 1 0%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: clip;
}
/* Permanent-rail band (80rem SSOT): #app-main and #session-rail share the
   row — column direction here stacked the rail beneath main and painted it
   over the page content. */
@media (min-width: 80rem) {
  .bao-shell-drawer > .drawer-content > .relative {
    flex-direction: row;
  }
}

#app-main {
  
  
  
  overflow-x: hidden;
  
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  /* Happydumpling page gutter: section rhythm, not raw rem hardcodes.
   * Do not set padding-block shorthand — dock clearance owns block-end
   * (tokens-layout-dock) so collection pages keep dock+safe-area room. */
  padding-inline: var(--page-gutter-inline, var(--space-section, 1rem));
  padding-block-start: var(--page-gutter-block, var(--space-section, 0.75rem));
}

/*
 * Non-media pages under permanent-rail: reserve fixed-dock scroll room.
 * WebView 101 has no reliable :has(); data-main-page-type is the portable key.
 */
@media (max-width: 79.99rem) {
  #app-main:not([data-main-page-type="media"]):not([data-main-page-type="conversation"]) {
    padding-block-end: calc(
      var(--dock-height, 4rem) + env(safe-area-inset-bottom, 0px) + var(--space-section, 0.75rem) + var(--touch-target-min, 2.75rem)
    );
  }
}
@media (min-width: 80rem) {
  #app-main:not([data-main-page-type="media"]):not([data-main-page-type="conversation"]) {
    padding-block-end: var(--page-gutter-block, var(--space-section, 0.75rem));
  }
}

/*
 * Sign-in renders NO dock, so reserving dock clearance for it leaves a band of
 * bare #app-main below the auth shell. The shell paints the ambient gradient;
 * main does not — so the reserved strip read as a white cut-off under the
 * sign-in card (measured 84px at 375x812, of which 72px was this padding).
 *
 * data-page is the page's own key and is present in the SSR markup, so this
 * needs no JS mirror. Engines without :has() simply keep the old clearance —
 * extra space, never a broken layout.
 */
#app-main:has([data-page="login"]):not([data-main-page-type="media"]):not([data-main-page-type="conversation"]) {
  padding-block-end: 0;
}

/*
 * Immersive media/conversation: zero side/top gutters so workbench fills,
 * but keep dock clearance so shutter/composer never sits under fixed dock.
 * WebView 101 has no :has() — use data-main-page-type mirrored by app-shell JS.
 */
#app-main[data-main-page-type="media"],
#app-main[data-main-page-type="conversation"],
#app-main:has([data-page-type="media"]),
#app-main:has([data-page-type="conversation"]) {
  
  
  padding-block-end: calc(
    var(--dock-height, 4rem) + env(safe-area-inset-bottom, 0px)
  );
  /*
   * Stays hidden at every width. The stacked phone layout does NOT need
   * #app-main to scroll: the acquire panel's own section is already the
   * scrollport (measured on /capture at 320x720 — clientHeight 528,
   * scrollHeight 564), so opening a second one here would nest scrollers for
   * no reachability gain.
   */
  overflow: hidden;
}
@media (min-width: 80rem) {
  #app-main[data-main-page-type="media"],
  #app-main[data-main-page-type="conversation"],
  #app-main:has([data-page-type="media"]),
  #app-main:has([data-page-type="conversation"]) {
    
  }
}

/*
 * Mobile-dock material is NOT declared here. tokens-layout-dock-base is the
 * single owner (border/background/color + backdrop-filter). TOKENS_LAYOUT_DOCK_CSS
 * emits after TOKENS_GLASS_CSS (tokens.ts), so a dock surface rule in this
 * module would be dead weight that only looks authoritative.
 */

/* Solid cards/panels — theme tokens, never raw white (dark mode WebView SSOT). */
.card,
[data-dashboard-widget],
.bao-data-panel,
.fieldset.bao-settings-group {
  background: var(--bg-panel, var(--color-base-100));
  border-color: var(--border-base, var(--color-base-300));
  color: var(--text-primary, var(--color-base-content));
}

/* Form controls — solid theme vars (no color-mix/oklch required). */
.input,
.select,
.textarea,
.file-input {
  background-color: var(--bg-panel, var(--color-base-100));
  color: var(--text-primary, var(--color-base-content));
  border-color: var(--border-base, var(--color-base-300));
}
.input::placeholder,
.textarea::placeholder {
  color: var(--text-secondary, var(--color-base-content));
  opacity: var(--opacity-medium, 0.75);
}

/* Ghost/outline chrome that daisyUI paints with color-mix on modern Chromium. */
.btn-ghost,
.btn-outline {
  color: var(--text-primary, var(--color-base-content));
  border-color: var(--border-base, var(--color-base-300));
}
.btn-primary {
  background-color: var(--color-primary);
  color: var(--color-primary-content);
  border-color: var(--color-primary);
}

body,
body.bao-shell-ambient {
  background-color: var(--bg-app, var(--color-base-100));
  color: var(--text-primary, var(--color-base-content));
}

/* Shell sidebar surface — safe-area insets clear mobile notches and
   rounded desktop edges; smooth scroll honors reduced-motion. */
.bao-shell-sidebar {
  padding-top: max(var(--safe-area-inset-top, var(--space-micro, 0.25rem)), env(safe-area-inset-top));
  padding-bottom: max(var(--safe-area-inset-bottom, var(--space-micro, 0.25rem)), env(safe-area-inset-bottom));
  scroll-behavior: smooth;
}
/*
 * Baobeep Liquid Glass law: aside frame is glass; nav copy sits on solid plate
 * (never bare glass under menu labels). Nested fill keeps 4.5:1 after blur.
 */
#app-sidebar.bao-shell-sidebar > [data-widget="sidebar"],
#app-sidebar.bao-shell-sidebar > .menu,
#app-sidebar.bao-shell-sidebar > ul.menu {
  background: var(--bg-panel, var(--color-base-100));
  border-radius: var(--radius-field, 0.75rem);
  color: var(--text-primary, var(--color-base-content));
}
#app-sidebar.bao-shell-sidebar > [data-sidebar-brand],
#app-sidebar.bao-shell-sidebar > [data-sidebar-rail-header] {
  background: color-mix(in oklab, var(--bg-panel, var(--color-base-100)) 92%, transparent);
  border-radius: var(--radius-field, 0.75rem);
}
@media (prefers-reduced-motion: reduce) {
  .bao-shell-sidebar {
    scroll-behavior: auto;
  }
}


/*
 * Form action rows — full-width labeled verbs on phone (save is primary,
 * never a floating icon-only square under WebView 101 / no-hover).
 */
@media (max-width: 47.99rem) {
  [data-licensing-admin-form] form > .flex.flex-row,
  [data-licensing-serial-admin] form > .flex.flex-row,
  form .flex.flex-row.flex-wrap.items-center.justify-end {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: var(--space-inline, 0.5rem);
  }
  [data-licensing-admin-form] .btn,
  [data-licensing-serial-admin] .btn,
  form .flex.flex-row.flex-wrap.items-center.justify-end > .btn {
    width: 100%;
    min-width: 0;
    min-height: var(--touch-target-min, 2.75rem);
    justify-content: center;
  }
  form .bao-toolbar-action.btn-square {
    width: 100%;
    min-width: 0;
    padding-inline: 0.75rem;
  }
  form .bao-toolbar-action.btn-square .bao-toolbar-label,
  form .btn .bao-toolbar-label {
    display: inline;
    max-width: none;
    opacity: 1;
    margin-inline-start: 0.375rem;
  }
  /*
   * Collection/home bento grids on phone: generic reactive panels collapse to
   * one column. Compact KPI stat widgets on home pair two-across (Apple Health
   * summary rhythm — design.pdf §8.2). Rich/full/form workbench widgets always
   * stack full-bleed — never crush setup/training/models forms to half width.
   */
  [data-page] .bao-grid-reactive-panels,
  [data-page] [data-section-group] > .bao-grid-reactive-panels {
    grid-template-columns: minmax(0, 1fr);
  }
  /*
   * Default phone dashboard grids: single column (forms/setup/training).
   * Home KPI strip re-opens 2-up AFTER this rule so cascade wins.
   */
  [data-page] [data-dashboard-grid] {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-inline, 0.5rem);
  }
  /* Home KPI strip only: 2-up compact stats (higher cascade than base). */
  [data-page-type="home"] [data-dashboard-grid],
  [data-page="dashboard"] [data-dashboard-grid] {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-inline, 0.5rem);
  }
  /* Uniform full layout / detail workbenches never 2-up on phone. */
  [data-page] [data-dashboard-layout-root][data-dashboard-uniform-size="full"] [data-dashboard-grid],
  [data-page-type="detail"] [data-dashboard-grid],
  [data-page-type="ops"] [data-dashboard-grid],
  [data-page-type="form"] [data-dashboard-grid] {
    grid-template-columns: minmax(0, 1fr);
  }
  [data-page] .bao-grid-panel-quarter,
  [data-page] .bao-grid-panel-third,
  [data-page] .bao-grid-panel-half,
  [data-page] .bao-grid-panel-narrow,
  [data-page] .bao-grid-panel-wide,
  [data-page] .bao-grid-panel-full {
    grid-column: 1 / -1;
  }
  /*
   * Home only: auto placement restores 2-across for compact KPI cards.
   * Explicit full/rich/form widgets keep the full row (setup, models, etc.).
   */
  [data-page-type="home"] [data-dashboard-grid] > [data-dashboard-widget][data-widget-density="compact"],
  [data-page="dashboard"] [data-dashboard-grid] > [data-dashboard-widget][data-widget-density="compact"] {
    grid-column: auto;
  }
  /* Data-dense / rich / full widgets keep the full row on every page. */
  [data-page] [data-dashboard-grid] > [data-dashboard-widget][data-widget-density="rich"],
  [data-page] [data-dashboard-grid] > [data-dashboard-widget][data-dashboard-panel-size="full"],
  [data-page] [data-dashboard-grid] > [data-dashboard-widget].bao-grid-panel-full,
  [data-page] [data-dashboard-grid] > [data-dashboard-widget][data-widget="recent-cases"],
  [data-page] [data-dashboard-grid] > [data-dashboard-widget]:has(table),
  [data-page] [data-dashboard-grid] > [data-dashboard-widget]:has(form),
  [data-page] [data-dashboard-grid] > [data-dashboard-widget]:has([data-case-list]) {
    grid-column: 1 / -1;
  }
  /*
   * FilterBar phone densify SSOT lives in tokens-glass-shell-controls
   * (2-col grid + full-span search). Never force flex-direction:column here —
   * that undid the grid and burned 3+ viewport rows (cases/billing/models).
   */
  [data-filter-bar] .fieldset,
  [data-case-filters] .fieldset,
  [data-primitive="FilterBar"] .fieldset,
  [data-filter-bar] label.fieldset,
  [data-primitive="FilterBar"] label.fieldset,
  [data-filter-bar] fieldset,
  [data-case-filters] fieldset,
  [data-primitive="FilterBar"] fieldset {
    width: 100%;
    max-width: none;
    /* Content-height only — flex-grow blew empty voids between stacked filters. */
    flex: none;
    min-height: 0;
  }
  /* Page header actions: full-width primary CTAs on phone. */
  [data-page-header-actions] {
    width: 100%;
  }
  [data-page-header-actions] > .btn {
    flex: 1 1 auto;
    min-height: var(--touch-target-min, 2.75rem);
    justify-content: center;
  }
}


/*
 * Collection grids (models/cards) on WebView 101 — never 12 thin tracks with
 * span-1 children (crushes card titles into vertical glyphs). Phone = 1-col.
 */
@media (max-width: 47.99rem) {[data-page="models"] .bao-grid-reactive-panels,
[data-page="models"] .bao-stagger-children.bao-grid-reactive-panels {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-section, 0.75rem);
  }[data-page="models"] [data-model-download-card],
[data-page="models"] .bao-grid-panel-third,
[data-page="models"] .bao-grid-panel-quarter {
    grid-column: 1 / -1;
    width: 100%;
    min-width: 0;
  }/* Freeze infinite motion that reflows under HTMX load on Chromium 101. */
  .skeleton-shimmer,
.bao-stagger-children > *,
.htmx-added,
[data-page] [data-state-content] {
    animation: none;
  }
}



/*
 * WebKit hardening: daisyUI emits unprefixed `appearance: none` only, so iOS
 * Safari still paints the NATIVE checkbox/toggle/select control UNDER the
 * styled one (the doubled toggle: outline pill + filled native square). The
 * prefixed reset is required on every appearance-styled control — this is the
 * single SSOT owner (never a per-page override).
 */
/* Specificity above daisyUI .checkbox { appearance: none } alone so WebKit
 * never reverts to native double-paint under modal stacking (audit:structure
 * modal@phone native-control-paint). Input-typed selectors win UA+daisy. */
.toggle,
input.toggle[type="checkbox"],
.checkbox,
input.checkbox[type="checkbox"],
.radio,
input.radio[type="radio"],
.select,
select.select,
.range,
input.range[type="range"],
.file-input,
input.file-input[type="file"] {
  -webkit-appearance: none;
  appearance: none;
}
/*
 * Checkbox affordance: daisyUI couples checkbox+toggle radius on one
 * --radius-selector token; at the theme's capsule value a small checkbox
 * reads as a RADIO (multi-select sections looked single-choice). Toggles
 * keep the capsule; the checkbox mark caps at the canon 0.25rem step so
 * square = multi-select, circle = single-select stays legible. Single
 * owner — never per-page.
 */
.checkbox {
  border-radius: min(var(--radius-selector, 0.75rem), var(--radius-xs, 0.25rem));
}
/* WebView button metrics; variant selectors below retain equal specificity. */
.btn:not([hidden]):not(.hidden) {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  border-radius: var(--radius-field, 0.75rem);
  border-width: 1px;
  border-style: solid;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  box-sizing: border-box;
  white-space: nowrap;
  /* Neutral default fill — variants below re-declare with matching specificity. */
  border-color: var(--border-base, var(--color-base-300));
  background: var(--bg-panel, var(--color-base-100));
  color: var(--text-primary, var(--color-base-content));
}
/* Unlayered override — beats any residual .btn display force for steppers. */
.btn[hidden],
.btn.hidden,
button.btn[hidden],
button.btn.hidden,
[hidden].btn {
  display: none;
}

.btn svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

/* Match base :not() specificity so primary/error fills actually paint. */
.btn-primary:not([hidden]):not(.hidden),
.btn.btn-primary:not([hidden]):not(.hidden) {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-content);
}

.btn-error:not([hidden]):not(.hidden),
.btn.btn-error:not([hidden]):not(.hidden) {
  background: var(--color-error);
  border-color: var(--color-error);
  color: var(--color-error-content);
}

.btn-success:not([hidden]):not(.hidden),
.btn.btn-success:not([hidden]):not(.hidden) {
  background: var(--color-success);
  border-color: var(--color-success);
  color: var(--color-success-content);
}

.btn-warning:not([hidden]):not(.hidden),
.btn.btn-warning:not([hidden]):not(.hidden) {
  background: var(--color-warning);
  border-color: var(--color-warning);
  color: var(--color-warning-content);
}

.btn-ghost:not([hidden]):not(.hidden),
.btn-outline:not([hidden]):not(.hidden),
.btn.btn-ghost:not([hidden]):not(.hidden),
.btn.btn-outline:not([hidden]):not(.hidden) {
  background: transparent;
  border-color: var(--border-base, var(--color-base-300));
  color: var(--text-primary, var(--color-base-content));
}

.btn-soft:not([hidden]):not(.hidden),
.btn.btn-soft:not([hidden]):not(.hidden) {
  background: color-mix(in oklab, var(--color-primary) 12%, var(--bg-panel));
  border-color: color-mix(in oklab, var(--color-primary) 28%, transparent);
  color: var(--text-primary, var(--color-base-content));
}

/* Hover is a fine-pointer enhancement (design.md §6) — touch keeps the
   resting fill and acknowledges presses via the :active rule below. */
@media (hover: hover) and (pointer: fine) {
  .btn-soft:not([hidden]):not(.hidden):hover,
  .btn.btn-soft:not([hidden]):not(.hidden):hover {
    background: color-mix(in oklab, var(--color-primary) 18%, var(--bg-panel));
    border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
  }
}

.btn-soft:not([hidden]):not(.hidden):active,
.btn.btn-soft:not([hidden]):not(.hidden):active,
.btn-soft:not([hidden]):not(.hidden).btn-active,
.btn.btn-soft:not([hidden]):not(.hidden).btn-active {
  background: color-mix(in oklab, var(--color-primary) 24%, var(--bg-panel));
  border-color: color-mix(in oklab, var(--color-primary) 45%, transparent);
  color: var(--color-base-content);
}

/* Segmented/tab active must use theme primary fill (not muted soft wash).
   Session mode tabs are single-owned by tokens-glass-shell-capture (the
   reactive-state-owner gate) — only the generic canon arm may touch them. */
.tab.tab-active:not([hidden]):not(.hidden),
.bao-settings-tab.tab-active:not([hidden]):not(.hidden) {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-content);
}

.btn-primary:not([hidden]):not(.hidden):active,
.btn.btn-primary:not([hidden]):not(.hidden):active,
.btn-primary:not([hidden]):not(.hidden)[aria-pressed="true"],
.btn.btn-primary:not([hidden]):not(.hidden)[aria-pressed="true"] {
  background: color-mix(in oklab, var(--color-primary) 88%, var(--color-base-content));
  border-color: color-mix(in oklab, var(--color-primary) 88%, var(--color-base-content));
  color: var(--color-primary-content);
}

/* Ensure soft/ghost/outline remain hit-testable even under dense shell CSS. */
.btn:not([hidden]):not(.hidden):not(:disabled):not([aria-disabled="true"]) {
  pointer-events: auto;
  cursor: pointer;
}

/* Dense daisyUI sizes yield to the canonical touch-target utility. */
.btn:not([hidden]):not(.hidden):not(.bao-touch-target) {
  min-height: 2.5rem;
  padding: 0.5rem 0.875rem;
  font-size: var(--text-sm, 0.875rem);
  line-height: 1.2;
}

.btn-sm:not([hidden]):not(.hidden):not(.bao-touch-target),
.btn-xs:not([hidden]):not(.hidden):not(.bao-touch-target) {
  min-height: 2rem;
  padding: 0.25rem 0.625rem;
  font-size: var(--text-sm, 0.875rem);
}

.btn-square:not([hidden]):not(.hidden):not(.bao-touch-target) {
  width: 2.5rem;
  min-width: 2.5rem;
  padding-inline: 0;
}

/* Canonical 44x44dp touch target wins by source order. */
.bao-touch-target:not([hidden]):not(.hidden) {
  min-height: 2.75rem;
  min-width: 2.75rem;
}

/*
 * The square width applies only where the control is genuinely icon-only.
 *
 * This is a 44px touch FLOOR, not a cap, and it was acting as both. At
 * (0,4,0) it outranked the labeled-density contract at (0,3,0) — so a toolbar
 * that declared data-toolbar-density="labeled" revealed its caption and then
 * painted it inside a box pinned to 44px. Measured on the editor dock:
 * "Rectangle" natural width 59px, button 44px, and an inline width:auto probe
 * returned 97px, proving the layout was fine and only this rule was in the
 * way. Six modules clipped that label; this is the one that actually won.
 *
 * min-width keeps the WCAG 2.5.8 / HIG floor intact for labelled controls too.
 */
.btn-square.bao-touch-target:not([hidden]):not(.hidden):not([data-toolbar-density="labeled"] *) {
  width: 2.75rem;
  min-width: 2.75rem;
}
[data-toolbar-density="labeled"] .btn-square.bao-touch-target:not([hidden]):not(.hidden) {
  width: auto;
  min-width: 2.75rem;
}

/* Enforce the touch floor on coarse pointers without shrinking larger variants. */
@media (pointer: coarse), (hover: none) {
  .btn:not([hidden]):not(.hidden):not(.bao-touch-target):not(.btn-lg):not(.btn-xl),
  .btn.btn-sm:not([hidden]):not(.hidden):not(.bao-touch-target),
  .btn.btn-xs:not([hidden]):not(.hidden):not(.bao-touch-target) {
    min-height: var(--touch-target-min, 2.75rem);
  }
  .btn.btn-circle:not([hidden]):not(.hidden):not(.bao-touch-target),
  .btn.btn-square:not([hidden]):not(.hidden):not(.bao-touch-target) {
    min-width: var(--touch-target-min, 2.75rem);
  }
  /* Text controls join the floor — input-sm/select-sm computed 30-36px on
     phones (WCAG 2.5.8 / design.md §8.2 primary mobile controls 44-48). */
  .input:not([hidden]):not(.hidden),
  .select:not([hidden]):not(.hidden),
  .textarea:not([hidden]):not(.hidden),
  .range:not([hidden]):not(.hidden) {
    min-height: var(--touch-target-min, 2.75rem);
  }
}

/*
 * Switch/checkbox/radio target floor (ALL pointers — not coarse-only).
 * Playwright audits measure the INPUT border box; packing toggle-sm (≈20px)
 * next to another control failed WCAG 2.5.8 on phone-narrow. Keep daisyUI 5
 * pill morphology (≈2:1 track, thumb slides on grid) — never force a 44×44
 * square that collapses the thumb animation. Floor: mark ≥24px (WCAG 2.5.8);
 * label row ≥44px isolates packing so the spacing exception is not required.
 */
.toggle:not([hidden]):not(.hidden),
.checkbox:not([hidden]):not(.hidden),
.radio:not([hidden]):not(.hidden) {
  position: relative;
  flex-shrink: 0;
  box-sizing: border-box;
  min-height: 1.5rem;
  min-width: 1.5rem;
}
.toggle:not([hidden]):not(.hidden) {
  /* Horizontal pill track: 44×24 — width is the primary touch dimension. */
  width: var(--touch-target-min, 2.75rem);
  min-width: var(--touch-target-min, 2.75rem);
  height: 1.5rem;
  min-height: 1.5rem;
}
/* Label rows that host a switch grow to the touch floor so packing is isolated. */
.label:has(> .toggle),
.label:has(> .checkbox),
.label:has(> .radio) {
  min-height: var(--touch-target-min, 2.75rem);
  align-items: center;
  gap: var(--space-inline, 0.5rem);
}

/*
 * Touch: labeled square buttons may grow for visible verbs. Icon-only shell
 * chrome (bao-shell-icon-action) stays fixed — reasserted in topbar-icons CSS.
 */
@media (hover: none), (pointer: coarse) {
  .btn-square:not([hidden]):not(.hidden):not(.bao-shell-icon-action) {
    width: auto;
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding-inline: 0.625rem;
  }
  .btn-square:not(.bao-shell-icon-action) .bao-toolbar-label {
    display: inline-block;
  }
}

/* Sticky toolbar under shell chrome — tokenized offset with no arbitrary positioning utility. */
.bao-sticky-toolbar {
  position: sticky;
  top: calc(var(--chrome-min-height, 2.25rem) + env(safe-area-inset-top, 0px));
}

/* AI analysis overlay stage — reserved geometry (CLS), no min-h-48 raw utility. */
.bao-min-h-overlay-stage {
  min-height: var(--overlay-stage-min-height, 12rem);
}

/* Stage empty CTA only while state is empty (success paints canvas overlays). */
[data-ai-analysis-overlay-stage]:not([data-state="empty"]) [data-ai-analysis-overlay-empty] {
  display: none;
}

/* Textarea default height — tokenized (no min-h-32 raw utility). */
.bao-min-h-textarea {
  min-height: var(--textarea-min-height, 8rem);
}

/* PageHeader grid, H1, and phone collapse live in
 * tokens-glass-shell-page-header.ts (400-LOC monolith gate). */

/* background-color LONGHAND only: the background shorthand resets
 * background-image and erases the daisyUI select caret gradients app-wide
 * (unlayered rule beats the layered component). Selects also keep the
 * 1.75rem inline-end pad — that band IS the caret zone. */
.input,
.select,
.textarea {
  display: flex;
  width: 100%;
  min-height: 2.5rem;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border-base);
  border-radius: var(--radius-field, 0.75rem);
  background-color: var(--bg-panel);
  font-size: var(--text-sm, 0.875rem);
  box-sizing: border-box;
}
.select {
  padding-inline-end: 1.75rem;
}

.input-sm,
.select-sm {
  min-height: 2.25rem;
  padding: 0.375rem 0.625rem;
  font-size: var(--text-sm, 0.875rem);
}
.select-sm {
  padding-inline-end: 1.75rem;
}

/* Filter bar — soft panel, not a black wireframe. */
[data-case-filters],
[data-primitive="FilterBar"] {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  padding: var(--space-section);
  border: 1px solid var(--border-base);
  border-radius: var(--radius-field);
  background: color-mix(in oklab, var(--bg-panel) 96%, var(--color-base-200));
  box-shadow: 0 1px 2px color-mix(in oklab, var(--color-neutral, var(--color-base-content)) 8%, transparent);
}
/*
 * FilterBar host layout is CSS-only (FilterBar.bao no longer bakes flex/wrap).
 * Desktop/tablet: horizontal wrap row. Phone: 2-col grid (cascade owner late).
 */
form[data-case-filters],
form[data-primitive="FilterBar"],
form[data-filter-bar],
[data-case-filters],
[data-primitive="FilterBar"],
[data-filter-bar] {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  width: 100%;
  min-width: 0;
}

@media (max-width: 47.99rem) {
  form[data-case-filters],
  form[data-primitive="FilterBar"],
  form[data-filter-bar],
  [data-case-filters],
  [data-primitive="FilterBar"],
  [data-filter-bar] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: end;
    gap: var(--space-micro);
    flex-wrap: unset;
  }
  form[data-case-filters] fieldset:has(input[type="search"]),
  form[data-case-filters] fieldset:has([data-case-search]),
  [data-case-filters] fieldset:has(input[type="search"]),
  [data-case-filters] .fieldset:has(input[type="search"]),
  [data-case-filters] fieldset:has([data-case-search]),
  [data-primitive="FilterBar"] fieldset:has(input[type="search"]),
  [data-primitive="FilterBar"] .fieldset:has(input[type="search"]),
  [data-primitive="FilterBar"] fieldset:has([data-filter-search]),
  /* Actions row only — never bare .flex (fieldsets also carry class flex). */
  form[data-case-filters] > [data-filter-actions],
  [data-case-filters] > [data-filter-actions],
  [data-primitive="FilterBar"] > [data-filter-actions],
  form[data-case-filters] > div.flex.shrink-0,
  [data-case-filters] > div.flex.shrink-0,
  [data-primitive="FilterBar"] > div.flex.shrink-0,
  form[data-case-filters] > div.flex.items-center,
  [data-case-filters] > div.flex.items-center,
  [data-primitive="FilterBar"] > div.flex.items-center {
    grid-column: 1 / -1;
  }
  /* Legend is AT-only on phone — placeholder / selected value carries the cue. */
  form[data-case-filters] .fieldset-legend,
  form[data-case-filters] legend,
  [data-case-filters] .fieldset-legend,
  [data-case-filters] legend,
  [data-primitive="FilterBar"] .fieldset-legend,
  [data-primitive="FilterBar"] legend {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}


[data-case-filters] legend,
[data-case-filters] .fieldset-legend {
  padding: 0;
  font-size: var(--text-2xs, 0.6875rem);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow, 0.04em);
  text-transform: uppercase;
  color: var(--text-secondary);
}

[data-case-filters] fieldset,
[data-case-filters] .fieldset,
[data-primitive="FilterBar"] fieldset,
[data-primitive="FilterBar"] .fieldset {
  display: flex;
  /*
   * flex-basis must stay auto. A fixed rem basis becomes HEIGHT when the bar
   * is flex-direction:column on phone — ADB S26 measured ~192px voids under
   * Debug Search. Inline min uses tokens.bao --grid-context-min only.
   */
  flex: 0 1 auto;
  flex-direction: column;
  gap: var(--space-micro);
  min-width: min(100%, var(--grid-context-min));
  margin: 0;
  padding: 0;
  border: 0;
}

@media (min-width: 48rem) {
  [data-case-filters] fieldset,
  [data-case-filters] .fieldset,
  [data-primitive="FilterBar"] fieldset,
  [data-primitive="FilterBar"] .fieldset {
    flex: 1 1 var(--grid-context-min);
    min-width: 0;
  }
}

/* Phone column FilterBar: content-height only (beats row basis). */
@media (max-width: 47.99rem) {
  [data-case-filters] fieldset,
  [data-case-filters] .fieldset,
  [data-primitive="FilterBar"] fieldset,
  [data-primitive="FilterBar"] .fieldset,
  [data-filter-bar] fieldset,
  [data-filter-bar] .fieldset {
    flex: 0 0 auto;
    flex-basis: auto;
    min-width: 0;
    height: auto;
  }
  [data-case-filters] .bao-field-basis,
  [data-primitive="FilterBar"] .bao-field-basis,
  [data-filter-bar] .bao-field-basis {
    flex-basis: auto;
  }
}

/*
 * Case list, case card, case status, context card, context grid,
 * card/card-body/card-title, dashboard widget — all SSoT in
 * tokens-layout-page-shell-surfaces.ts (token-driven, no hardcoded hex).
 * Do NOT redefine them here.
 */

/* Conversation composer — unique WebView harden (no token source). */
[data-page="chat"] .bao-conversation-composer,
[data-page-type="conversation"] [data-composer] {
  border: 1px solid var(--border-base);
  border-radius: var(--radius-field, 0.75rem);
  background: var(--bg-panel);
  box-shadow: 0 4px 16px color-mix(in oklab, var(--color-neutral, var(--color-base-content)) 10%, transparent);
}


/* PageHeader follows the enterprise-page.bao title/action grid. */
.bao-page-header,
[data-primitive="PageHeader"] {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: end;
  justify-items: stretch;
  column-gap: var(--space-section, 0.75rem);
  row-gap: var(--space-inline, 0.5rem);
  margin-block-end: var(--space-section, 0.75rem);
  width: 100%;
  min-width: 0;
  text-align: start;
}

@media (min-width: 40rem) {
  .bao-page-header,
  [data-primitive="PageHeader"] {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}

/* Header action row — touch targets without arbitrary child selectors in tokens. */
.bao-page-header-actions > .btn {
  min-height: var(--touch-target-min, 2.75rem);
}

[data-primitive="PageHeader"] > .flex.min-w-0.flex-col,
[data-primitive="PageHeader"] > div:first-child {
  min-width: 0;
  justify-self: stretch;
  text-align: start;
}

[data-page-header-actions],
[data-primitive="PageHeader"] [data-page-header-actions] {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-micro, 0.25rem);
  min-width: 0;
}

@media (min-width: 40rem) {
  [data-page-header-actions],
  [data-primitive="PageHeader"] [data-page-header-actions] {
    justify-content: flex-end;
    justify-self: end;
  }
}

[data-page-header-actions] .btn {
  flex-direction: row;
}

[data-primitive="PageHeader"] h1 {
  margin: 0;
  font-weight: var(--font-weight-semibold, 600);
  letter-spacing: var(--tracking-tight, -0.025em);
  color: var(--text-primary);
  text-align: start;
}

/*
 * Phone page headers: H1 only (dock + shell orient). Subtitles/leads hide.
 * One primary action remains full-width touch target. Icon-only chrome
 * (btn-square / soft tool buttons) must stay compact — never stretched into
 * full-width bars that read as accidental blocks (design.md §5.4 hierarchy).
 */
@media (max-width: 47.99rem) {
  [data-primitive="PageHeader"] p.text-sm.leading-snug,
  [data-primitive="PageHeader"] .max-w-description {
    display: none;
  }
  /*
   * Phone: shell already paints page title in chrome — page H1 becomes a
   * second title band that wastes vertical density and can overflow at
   * 320px when actions wrap. Visually hide H1 (keep in a11y tree via
   * clip) so the action row is the sole page chrome under the topbar.
   */
  [data-primitive="PageHeader"] h1,
  [data-primitive="PageHeader"] [id^="page-header-title-"] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  /*
   * Phone: shell already paints page title — breadcrumbs are secondary
   * orientation. Keep them for AT (visually hide) so the action row owns
   * the full width without a second chrome band (design.md §5.1 / HIG).
   */
  [data-primitive="PageHeader"] [data-page-breadcrumbs] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  [data-page-header-actions] {
    justify-content: flex-end;
  }
  [data-page-header-actions] > .btn.btn-primary,
  [data-page-header-actions] > a.btn.btn-primary,
  [data-page-header-actions] > [data-disabled-tip-host]:has(.btn-primary) {
    flex: 1 1 auto;
    justify-content: center;
    min-height: var(--touch-target-min, 2.75rem);
  }
  [data-page-header-actions] > .btn:not(.btn-primary),
  [data-page-header-actions] > a.btn:not(.btn-primary),
  [data-page-header-actions] > [data-disabled-tip-host]:not(:has(.btn-primary)) {
    flex: 0 0 auto;
    min-height: var(--touch-target-min, 2.75rem);
  }
  /*
   * Icon-first secondary chrome on phone: ghost/outline toolbar labels hide
   * so 3–5 actions fit one row without wrapping into a cramped second band
   * (Apple HIG dense toolbar; labels stay in aria). Soft + primary clinical
   * CTAs (finish case, generate report) keep permanent verb labels — touch
   * has no hover, so an unlabeled icon square is undiscoverable
   * (design.md §6: essentials must not depend on hover).
   */
  [data-page-header-actions] .bao-toolbar-action:not(.btn-primary):not(.btn-soft) .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
  }
  [data-page-header-actions] .bao-toolbar-action:not(.btn-primary):not(.btn-soft):focus-visible .bao-toolbar-label,
  [data-page-header-actions] .bao-toolbar-action:not(.btn-primary):not(.btn-soft)[aria-pressed="true"] .bao-toolbar-label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    clip: auto;
    max-width: var(--toolbar-label-max-width, 8rem);
    opacity: 1;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Soft clinical secondaries: permanent compact verb next to the glyph. */
  [data-page-header-actions] .bao-toolbar-action.btn-soft .bao-toolbar-label {
    display: inline-block;
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    margin-inline-start: var(--space-micro, 0.25rem);
    clip: auto;
    max-width: var(--toolbar-label-max-width, 8rem);
    opacity: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /*
   * Layout customizer is chrome, not a clinical verb (Apple HIG dense toolbar).
   * Icon-only on phone; aria-label + tip retain the name. Overrides soft
   * permanent-label rule above so lone "Customize" does not own a full row.
   */
  [data-dashboard-edit-toggle].bao-toolbar-action .bao-toolbar-label,
  [data-page-header-actions] [data-dashboard-edit-toggle].bao-toolbar-action.btn-soft .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
    display: block;
  }
  /*
   * Media workbenches already own a thumb dock / overflow sheet — header
   * "More actions" stays discoverable via glyph + aria, not a second verb.
   */
  [data-page-type="media"] [data-header-action-overflow] > summary.bao-toolbar-action .bao-toolbar-label,
  [data-page-type="media"] [data-page-header-actions] [data-header-action-overflow] > summary.btn-soft .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
    display: block;
  }
  /*
   * Collection/ops sticky toolbar already paints primary + overflow on phone.
   * Header overflow densifies to icon so two labeled "More actions" never
   * stack (reports composition fail). AT keeps aria-label.
   */
  body:has([data-toolbar-responsive="primary-overflow"]) [data-header-action-overflow] > summary .bao-toolbar-label,
  [data-page]:has([data-toolbar-responsive="primary-overflow"]) [data-header-action-overflow] > summary .bao-toolbar-label,
  [data-toolbar-action-overflow] > summary .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
    display: block;
  }
}

/* Tablet: keep labels for soft secondaries — more horizontal room. */
@media (min-width: 48rem) and (max-width: 63.99rem) {
  [data-page-header-actions] {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
    scrollbar-width: none;
  }
  [data-page-header-actions]::-webkit-scrollbar {
    display: none;
  }
  /*
   * Tablet overflow "More actions" must keep a permanent verb (orphanOverflows
   * gate). Phone densify may icon-only; tablet has room and no hover alternate.
   */
  [data-page-header-actions] [data-header-action-overflow] > summary .bao-toolbar-label,
  [data-header-action-overflow] > summary.bao-toolbar-action .bao-toolbar-label {
    position: static;
    display: inline-block;
    width: auto;
    height: auto;
    min-width: 1ch;
    max-width: var(--toolbar-label-reveal-max-width, 12rem);
    margin: 0;
    margin-inline-start: var(--space-micro, 0.25rem);
    padding: 0;
    overflow: hidden;
    clip: auto;
    opacity: 1;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}


/*
 * Shell icon action — theme, voice, account, drawer toggle.
 * Fixed 44px square;absolute stack centers glyph + daisyUI swap layers.
 * Nested under .bao-shell-topbar-actions (not navbar-end > .btn direct child).
 */
#app-header .bao-shell-icon-action.btn,
#app-header .bao-shell-icon-action.btn.btn-square,
#app-header .bao-shell-icon-action.btn.tooltip,
#app-header .bao-shell-icon-action.btn.swap,
#app-header .bao-shell-icon-action.btn.swap-rotate,
#app-header .bao-shell-topbar-actions .bao-shell-icon-action.btn,
#app-header .bao-shell-topbar-actions summary.bao-shell-icon-action,
#app-header button.bao-shell-icon-action,
#app-header summary.bao-shell-icon-action {
  position: relative;
  display: inline-grid;
  place-content: center;
  place-items: center;
  align-content: center;
  justify-items: center;
  width: var(--touch-target-min, 2.75rem);
  min-width: var(--touch-target-min, 2.75rem);
  max-width: var(--touch-target-min, 2.75rem);
  height: var(--touch-target-min, 2.75rem);
  min-height: var(--touch-target-min, 2.75rem);
  max-height: var(--touch-target-min, 2.75rem);
  padding: 0;
  padding-inline: 0;
  margin: 0;
  gap: 0;
  line-height: 0;
  flex-shrink: 0;
  box-sizing: border-box;
  /* visible: tooltips; icon layers use absolute so overflow does not clip glyph */
  overflow: visible;
  vertical-align: middle;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "stack";
}/* Kill bao-toolbar-action overflow:hidden clip + label-expand geometry on shell. */
#app-header .bao-toolbar-action.bao-shell-icon-action,
#app-header .bao-shell-topbar-actions .bao-toolbar-action.bao-shell-icon-action {
  overflow: visible;
  gap: 0;
}#app-header .bao-shell-icon-action.btn:hover,
#app-header .bao-shell-icon-action.btn:focus-visible,
#app-header .bao-shell-icon-action.btn:focus-within,
#app-header .bao-shell-icon-action.btn[aria-pressed="true"],
#app-header .bao-shell-icon-action.btn[aria-expanded="true"],
#app-header .bao-shell-icon-action.btn.swap-active,
#app-header .bao-shell-icon-action.btn.swap-rotate,
#app-header details[open] > summary.bao-shell-icon-action,
#app-header .bao-shell-topbar-actions .bao-shell-icon-action.btn:hover,
#app-header .bao-shell-topbar-actions .bao-shell-icon-action.btn[aria-pressed="true"] {
  width: var(--touch-target-min, 2.75rem);
  min-width: var(--touch-target-min, 2.75rem);
  max-width: var(--touch-target-min, 2.75rem);
  height: var(--touch-target-min, 2.75rem);
  min-height: var(--touch-target-min, 2.75rem);
  padding: 0;
  padding-inline: 0;
  gap: 0;
}/*
 * Absolute stack: every glyph layer shares the same box center.
 * swap-rotate's 45deg must not invent a second layout box.
 */
#app-header .bao-shell-icon-action > svg,
#app-header .bao-shell-icon-action > .swap-on,
#app-header .bao-shell-icon-action > .swap-off,
#app-header .bao-shell-icon-action > .avatar,
#app-header .bao-shell-icon-action > span:not(.sr-only):not(.htmx-indicator):not(.loading) {
  grid-area: stack;
  place-self: center;
  position: absolute;
  inset: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  line-height: 0;
  pointer-events: none;
  /* kill daisyUI swap-rotate optical skew for shell chrome */
  rotate: none;
  transform: none;
}#app-header .bao-shell-icon-action.swap-rotate > .swap-on,
#app-header .bao-shell-icon-action.swap-rotate > .swap-off,
#app-header .bao-shell-icon-action.swap-active.swap-rotate > .swap-on,
#app-header .bao-shell-icon-action.swap-active.swap-rotate > .swap-off {
  rotate: none;
  transform: none;
}#app-header .bao-shell-icon-action > svg,
#app-header .bao-shell-icon-action .swap-on svg,
#app-header .bao-shell-icon-action .swap-off svg {
  display: block;
  width: 1rem;
  height: 1rem;
  margin: 0;
  flex-shrink: 0;
}/* Avatar glyph inside account summary — circle centered in square. */
#app-header .bao-shell-icon-action > .avatar,
#app-header .bao-shell-icon-action .avatar {
  width: 100%;
  height: 100%;
}#app-header .bao-shell-icon-action .avatar > span,
#app-header .bao-shell-icon-action [data-user-avatar] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  min-width: 1.75rem;
  min-height: 1.75rem;
  margin: 0;
  line-height: 1;
  border-radius: var(--radius-full);
}#app-header .bao-shell-icon-action > .sr-only,
#app-header .bao-shell-icon-action .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}/* Coarse pointer: re-assert fixed square after general btn-square grow rule. */
@media (hover: none),
(pointer: coarse) {#app-header .bao-shell-icon-action.btn,
#app-header .bao-shell-icon-action.btn.btn-square,
#app-header .bao-shell-topbar-actions .bao-shell-icon-action.btn,
#app-header .bao-shell-topbar-actions summary.bao-shell-icon-action,
#app-header button.bao-shell-icon-action,
#app-header summary.bao-shell-icon-action,
#app-header .navbar-end .btn.btn-square,
#app-header .navbar-start .btn.btn-square {
    width: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
    max-width: var(--touch-target-min, 2.75rem);
    height: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
    padding: 0;
    padding-inline: 0;
    gap: 0;
  }
}

/*
 * Topbar action cluster — single dense toolbar row (design.md §5.1 chrome).
 * Indicators absolute so HTMX spinners never reflow the icon row.
 */
#app-header .bao-shell-topbar-actions {
  display: inline-flex;
  position: relative;
  flex-shrink: 0;
  min-width: 0;
}#app-header .bao-shell-topbar-actions > .htmx-indicator,
#app-header .bao-shell-topbar-actions > .loading,
#app-header .bao-shell-topbar-actions > [id$="-indicator"],
#app-header .bao-shell-topbar-actions > [id$="-status"],
#app-header .navbar-end > .htmx-indicator,
#app-header .navbar-end > .loading {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  pointer-events: none;
}/* Voice status is optional text — never crush icon alignment. */
#app-header .navbar-end [data-voice-command-status],
#app-header .bao-shell-topbar-actions [data-voice-command-status] {
  max-width: 6rem;
  min-width: 0;
  flex-shrink: 1;
}#app-header .navbar-end,
#app-header .navbar-end[data-shell-topbar-actions],
#app-header [data-shell-topbar-actions] {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2xs, 0.125rem);
  max-width: none;
  flex-shrink: 0;
}

/*
 * Responsive drawer-toggle contract must survive the icon-geometry override
 * above (its display: inline-grid outranks the drawer modules' hide rules).
 * Phone (<48rem): dock "More" owns the drawer. Tablet (48–79.99rem): the
 * persistent icon rail owns nav. Permanent rail (≥80rem): #app-sidebar
 * [data-sidebar-rail-toggle] owns collapse — one affordance per function
 * (design.md §5.1).
 */
#app-header button.bao-shell-icon-action.bao-drawer-toggle-control[data-drawer-toggle] {
  display: none;
}/*
 * Forced colors (SH-09,
WCAG 1.4.11): the UA strips glass fills and the
 * quiet transparent rest borders,
leaving glyph-only controls with no
 * perceivable boundary. Restore system-color boundaries + glyphs for shell
 * icon actions,
the status chip,
and dock items.
 */
@media (forced-colors: active) {#app-header .bao-shell-icon-action,
#app-header summary.bao-shell-icon-action,
#app-header .bao-shell-status-badge,
.bao-mobile-dock .bao-mobile-dock-item {
    border: 1px solid ButtonBorder;
    background: Canvas;
    color: ButtonText;
  }
  #app-header .bao-shell-status-badge[data-registry-offline="true"] {
    border-color: ButtonBorder;
    color: CanvasText;
  }#app-header .bao-shell-icon-action:focus-visible,
.bao-mobile-dock .bao-mobile-dock-item:focus-visible {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
}



/*
 * Identifier readout disclosure. The short segment is the summary; the full
 * value sits inside the disclosure so it is selectable by pointer, touch and
 * keyboard and announced in full. Before this the complete identifier lived
 * only on a title tooltip, which no touch surface can reach.
 */
.bao-settings-value-disclosure {
  min-width: 0;
  max-width: 100%;
}

/*
 * display: list-item keeps the native disclosure triangle. Setting display to
 * flex here removed it, leaving a control with no affordance that it opens —
 * the value looked like plain truncated text and nothing invited the tap.
 */
.bao-settings-value-disclosure > summary {
  display: list-item;
  list-style-position: inside;
  min-height: var(--touch-target-min, 2.75rem);
  padding-block: var(--space-micro, 0.25rem);
  cursor: pointer;
  border-radius: var(--radius-field, 0.75rem);
}

.bao-settings-value-full {
  display: block;
  padding-block-start: var(--space-micro, 0.25rem);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-xs, 0.75rem);
  overflow-wrap: anywhere;
  word-break: break-all;
  user-select: text;
}

/* Settings state strip — flex host for HTMX-loaded preference/state fragments. */
.bao-settings-state-strip {
  display: flex;
  flex-direction: column;
  gap: var(--space-inline);
  min-width: 0;
}

/*
 * Settings destination visibility is single-owned by
 * tokens-glass-shell-settings-responsive, which bands on the container's own
 * inline size rather than the device viewport.
 *
 * A viewport `@media (min-width: 40rem) { select { display: none } }` lived here
 * and directly contradicted that module's `@media (max-width: 79.99rem)`
 * select-visible rule: both matched between 40rem and 79.99rem, so which control
 * shipped depended on source order rather than on any declared contract. The
 * comment this replaces asserted ">=40rem: the tablist owns navigation", which
 * was false at every width actually measured — the select painted and the
 * tablist did not.
 *
 * The unlayered-specificity note it carried still holds and now lives with the
 * owning module: tokens-glass-shell-controls emits an UNLAYERED display:flex for
 * .input/.select/.textarea, so a layered `sm:hidden` utility (0,1,0) can never
 * hide this select. Any visibility rule for it must be unlayered and outrank
 * (0,1,0).
 */

/*
 * Settings tablist chrome — solid segmented host (Liquid Glass: chrome only).
 * Phone-first: compact horizontally scrollable destination rail.
 * Capture session tabs stay in tokens-glass-shell-capture (row only).
 */
[data-page="settings"] .tabs.tabs-box.bao-tabs-scroll,
[data-page-type="form"] .tabs.tabs-box.bao-tabs-scroll {
  display: flex;
  box-sizing: border-box;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: stretch;
  justify-content: flex-start;
  gap: var(--space-micro, 0.25rem);
  /* Beat daisyUI .tabs width:max-content so the rail never expands #app-main. */
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: var(--touch-target-min, 2.75rem);
  padding: var(--space-micro, 0.25rem);
  margin: 0 0 var(--space-inline, 0.5rem);
  border: 1px solid var(--color-base-300, var(--border-base));
  border-radius: var(--radius-field, 0.75rem);
  /* Longhand only — a shorthand here wipes the shared scroll-edge gradient. */
  background-color: var(--color-base-200, var(--bg-panel));
  --scroll-edge-bg: var(--color-base-200, var(--bg-panel));
  box-shadow: none;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /*
   * daisyUI tabs-box paints active chips with --tab-bg (defaults base-100 white).
   * Point SSOT primary so active destination never white-on-white.
   */
  --tab-bg: var(--color-primary);
  --tab-color: var(--color-primary-content);
}

[data-page="settings"] .bao-settings-tab,
[data-page-type="form"] .bao-settings-tab {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-compact);
  width: auto;
  max-width: none;
  min-width: max-content;
  margin: 0;
  padding: var(--space-micro, 0.25rem) var(--space-inline, 0.5rem);
  border: 1px solid transparent;
  border-radius: var(--radius-field, 0.75rem);
  background: transparent;
  color: var(--color-base-content);
  font-size: var(--text-sm, 0.875rem);
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: none;
}

[data-page="settings"] .bao-settings-tab .bao-toolbar-label,
[data-page-type="form"] .bao-settings-tab .bao-toolbar-label {
  position: static;
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: start;
  opacity: 1;
}

[data-page="settings"] .bao-settings-tab:not(.tab-active):not([aria-selected="true"]) {
  color: color-mix(in oklab, var(--color-base-content) 78%, transparent);
  background: transparent;
}

/*
 * Active destination — beat daisyUI .tab focus/active washes so primary fill
 * always carries white content (never white-on-white after keyboard/click).
 */
[data-page="settings"] .tabs.tabs-box.bao-tabs-scroll > .bao-settings-tab.tab-active,
[data-page="settings"] .tabs.tabs-box.bao-tabs-scroll > .bao-settings-tab[aria-selected="true"],
[data-page="settings"] .bao-settings-tab.tab-active,
[data-page="settings"] .bao-settings-tab[aria-selected="true"],
[data-page="settings"] .tabs.tabs-box .bao-settings-tab.tab-active:focus-visible,
[data-page="settings"] .tabs.tabs-box .bao-settings-tab[aria-selected="true"]:focus-visible {
  background-color: var(--color-primary);
  background-image: none;
  color: var(--color-primary-content);
  border-color: var(--color-primary);
  box-shadow: none;
  outline: 2px solid color-mix(in oklab, var(--color-primary) 45%, transparent);
  outline-offset: 2px;
}

[data-page="settings"] .bao-settings-tab.tab-active .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab[aria-selected="true"] .bao-toolbar-label,
[data-page="settings"] .bao-settings-tab.tab-active svg,
[data-page="settings"] .bao-settings-tab[aria-selected="true"] svg {
  color: var(--color-primary-content);
  opacity: 1;
}

[data-page="settings"] .bao-settings-tab svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

/* Tablet+ : horizontal scroll rail (icon+label chips). */
@media (min-width: 40.01rem) {
  [data-page="settings"] .tabs.tabs-box.bao-tabs-scroll,
  [data-page-type="form"] .tabs.tabs-box.bao-tabs-scroll {
    align-items: stretch;
    gap: var(--space-micro, 0.25rem);
    width: 100%;
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    /* Contain chip rail so smoke/layout never treat the tablist as uncontained overflow. */
    contain: inline-size;
  }
  [data-page="settings"] .bao-settings-tab,
  [data-page-type="form"] .bao-settings-tab {
    width: auto;
    flex: 0 0 auto;
    max-width: none;
    justify-content: center;
    padding-inline: var(--space-section, 0.75rem);
    scroll-snap-align: start;
  }
  [data-page="settings"] .bao-settings-tab .bao-toolbar-label,
  [data-page-type="form"] .bao-settings-tab .bao-toolbar-label {
    flex: 0 1 auto;
    max-width: 10rem;
  }
}

@media (max-width: 40rem) {
  [data-page="settings"] .tabs.tabs-box.bao-tabs-scroll,
  [data-page-type="form"] .tabs.tabs-box.bao-tabs-scroll {
    position: sticky;
    top: 0;
    z-index: 5;
  }
  [data-page="settings"] .bao-settings-tab,
  [data-page-type="form"] .bao-settings-tab {
    width: auto;
    min-width: var(--touch-target-min, 2.75rem);
    max-width: none;
    justify-content: center;
    gap: 0;
    padding-inline: var(--space-micro, 0.25rem);
  }
  /*
   * Phone density: inactive destinations are icon-only; the selected tab
   * keeps its label so path orientation never relies on color alone (HIG).
   */
  [data-page="settings"] .bao-settings-tab:not(.tab-active):not([aria-selected="true"]) .bao-toolbar-label,
  [data-page-type="form"] .bao-settings-tab:not(.tab-active):not([aria-selected="true"]) .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  [data-page="settings"] .bao-settings-tab.tab-active .bao-toolbar-label,
  [data-page="settings"] .bao-settings-tab[aria-selected="true"] .bao-toolbar-label,
  [data-page-type="form"] .bao-settings-tab.tab-active .bao-toolbar-label,
  [data-page-type="form"] .bao-settings-tab[aria-selected="true"] .bao-toolbar-label {
    position: static;
    width: auto;
    max-width: 7rem;
    margin: 0;
    margin-inline-start: var(--space-micro, 0.25rem);
    clip: auto;
    opacity: 1;
  }
}

/*
 * Settings navigation follows its available surface, not the device viewport.
 *
 * The container is the WRAPPER, never [data-settings-tabs] itself: an element is
 * not styled by a container query against its own size, so declaring the
 * container on [data-settings-tabs] made every rule targeting that element a
 * silent no-op while its children still resolved. The source-list grid lives on
 * [data-settings-tabs], so the query must be able to reach it.
 */
[data-page="settings"] :has(> [data-settings-tabs]),
[data-page-type="form"] :has(> [data-settings-tabs]) {
  container-type: inline-size;
  container-name: settings-tabs;
}

/* A constrained Settings destination stays reachable while its panel scrolls. */
@container settings-tabs (max-width: 96rem) {
  [data-page="settings"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page-type="form"] [data-settings-tabs] > select[data-settings-tab-select] {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky, 1100);
  }
}

/*
 * Compact surface (< 56rem of available inline size): the labelled select is the
 * destination control. Twelve destinations cannot render as a horizontal rail
 * without clipping, and a scrolling rail hides destinations behind a gesture.
 */
@container settings-tabs (max-width: 56rem) {
  [data-page="settings"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page-type="form"] [data-settings-tabs] > select[data-settings-tab-select] {
    display: flex;
  }

  [data-page="settings"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll,
  [data-page-type="form"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll {
    display: none;
  }
}

/*
 * Phone surface (< 40rem): the canonical bottom sheet, opened by
 * #settings-tab-phone-trigger, is the single destination menu. The select must
 * yield or both paint at once — a duplicate twelve-destination navigation
 * costing roughly 96px of the first viewport. This band is declared after the
 * compact band above so it wins at equal specificity.
 *
 * Deliberately a VIEWPORT query, unlike every other band here: the sheet and its
 * trigger are gated by a markup `sm:hidden` utility, which is viewport-based.
 * Gating the select on container size instead desynchronised the pair — at a
 * 768px viewport the container measures ~473px, so a container band hid the
 * select while the viewport-gated sheet was already hidden, leaving Settings
 * with no destination control at all and eleven of twelve panels unreachable.
 * Whichever axis the sheet uses, this rule must use too.
 */
@media (max-width: 39.99rem) {
  [data-page="settings"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page-type="form"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page="settings"] [data-settings-tabs] > label.sr-only,
  [data-page-type="form"] [data-settings-tabs] > label.sr-only {
    display: none;
  }
}

/*
 * Regular surface (>= 56.01rem): HIG source-list layout. The destination list
 * becomes a vertical sidebar beside its panel, so all twelve destinations are
 * visible at once with no horizontal overflow and no hidden-behind-a-gesture
 * scroll. Replaces the previous behaviour, where a viewport media query and two
 * container bands each hid the tablist and revealed the compact select, leaving
 * a phone control on every desktop and the tablist unreachable at any width.
 */
@container settings-tabs (min-width: 56.01rem) {
  [data-page="settings"] [data-settings-tabs],
  [data-page-type="form"] [data-settings-tabs] {
    display: grid;
    grid-template-columns: minmax(12rem, 15rem) minmax(0, 1fr);
    align-items: start;
    column-gap: var(--space-section);
  }

  [data-page="settings"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page-type="form"] [data-settings-tabs] > select[data-settings-tab-select],
  [data-page="settings"] [data-settings-tabs] > label.sr-only,
  [data-page-type="form"] [data-settings-tabs] > label.sr-only {
    display: none;
  }

  /* Source list: vertical, every destination visible, never scrolls sideways. */
  [data-page="settings"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll,
  [data-page-type="form"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll {
    display: flex;
    position: static;
    grid-column: 1;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    overflow-x: hidden;
    overflow-y: visible;
    margin-block-end: 0;
  }

  [data-page="settings"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll > .tab,
  [data-page-type="form"] [data-settings-tabs] > .tabs.tabs-box.bao-tabs-scroll > .tab {
    justify-content: flex-start;
    width: 100%;
    min-width: 0;
    text-align: start;
  }

  [data-page="settings"] [data-settings-tabs] > #settings-panels,
  [data-page-type="form"] [data-settings-tabs] > #settings-panels {
    grid-column: 2;
    margin-block-start: 0;
    min-width: 0;
  }

  [data-page="settings"] [data-settings-tabs] > #settings-panels-indicator,
  [data-page-type="form"] [data-settings-tabs] > #settings-panels-indicator {
    grid-column: 2;
  }
}

/*
 * Settings destination visibility is single-owned by
 * tokens-glass-shell-settings-responsive.
 *
 * This module previously declared a 56.01rem-96rem container band that revealed
 * the compact select and hid the tablist, on the reasoning that "a full label
 * rail is only useful when every destination is visible". The premise was right
 * and the remedy was not: twelve destinations do not fit a horizontal rail, so
 * the fix is a vertical source list at regular width, not a phone control on
 * every desktop. Because that band spanned 896-1536px of container width, the
 * tablist was unreachable on every realistic display.
 */


/* Settings — solid group panels only (never every input fieldset — design.md §1.1). */
[data-page="settings"] .bao-settings-group.fieldset,
[data-page="settings"] fieldset.bao-settings-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-inline);
  min-width: 0;
  margin: 0;
  padding: var(--space-section);
  border: 1px solid var(--border-base, var(--color-base-300));
  border-radius: var(--radius-box, 1rem);
  background: var(--bg-panel, var(--color-base-100));
  box-shadow: 0 1px 2px color-mix(in oklab, var(--text-primary) 5%, transparent);
}

/*
 * Control / form fieldsets stay borderless rows inside the group surface.
 * Never promote daisyUI .fieldset into a second card (design.md glass-on-glass).
 */
[data-page="settings"] .bao-settings-control-row.fieldset,
[data-page="settings"] fieldset.bao-settings-control-row,
[data-page="settings"] .bao-settings-group fieldset.fieldset:not(.bao-settings-group),
[data-page="settings"] [data-settings-device-registry] fieldset.fieldset,
[data-page="settings"] [data-settings-storage-root] fieldset.fieldset {
  margin: 0;
  padding-block: var(--space-micro);
  padding-inline: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

/*
 * Settings legends — sentence case, not ALL CAPS. Uppercase tracking burned
 * vertical rhythm on phone (Active Context / Environment stacks) and fought
 * enterprise microcopy density (Apple HIG: avoid shouting labels).
 */
[data-page="settings"] .fieldset-legend {
  padding-inline: 0;
  margin-block-end: var(--space-micro, 0.25rem);
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-secondary, var(--color-base-content));
}
/* Group titles keep section weight without uppercase shout. */
[data-page="settings"] .bao-settings-group > .fieldset-legend,
[data-page="settings"] legend.fieldset-legend {
  font-size: var(--text-sm, 0.875rem);
  font-weight: 600;
  color: var(--text-primary, var(--color-base-content));
}

.bao-settings-group {
  container-type: inline-size;
  container-name: settings-group;
}

/*
 * Settings control rows — DataSection.controlRow (toggle flex/grid) and
 * controlField (fieldset column). Border rhythm only here; layout from tokens.
 */
.bao-settings-control-row {
  min-width: 0;
  margin: 0;
  padding-block: var(--space-micro);
  border-block-end: 1px solid color-mix(in oklab, var(--color-base-300) 55%, transparent);
}

.bao-settings-control-row:first-child {
  padding-block-start: 0;
}

.bao-settings-control-row:last-child {
  padding-block-end: 0;
  border-block-end: 0;
}

/* Toggle: label | switch | indicator */
.bao-settings-control-row[data-settings-control-kind="toggle"] {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-inline);
}

.bao-settings-control-row[data-settings-control-kind="toggle"] > .label {
  min-width: 0;
  overflow-wrap: anywhere;
  white-space: normal;
}

.bao-settings-control-row[data-settings-control-kind="toggle"] > .htmx-indicator {
  grid-column: 3;
  grid-row: 1;
}

/* Input/select fieldset SSOT — stacked legend + control */
.bao-settings-control-row[data-settings-control-kind="input"],
.bao-settings-control-row[data-settings-control-kind="select"] {
  display: flex;
  flex-direction: column;
  gap: var(--space-micro);
  min-width: 0;
}

.bao-settings-control-row[data-settings-control-kind="input"] > input:not([type="checkbox"]),
.bao-settings-control-row[data-settings-control-kind="input"] > select,
.bao-settings-control-row[data-settings-control-kind="select"] > select {
  min-width: 0;
  width: 100%;
  max-width: 100%;
}

/* Runtime UUIDs / mono values must ellipsize, never blow the settings column. */
.bao-settings-control-row input.font-mono,
.bao-settings-control-row .font-mono,
[data-page="settings"] input.font-mono,
[data-page="settings"] .input.font-mono {
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* HTMX indicators must not reserve vertical space when idle. */
.bao-settings-control-row .htmx-indicator,
[data-settings-control-kind="input"] > .htmx-indicator,
[data-settings-control-kind="select"] > .htmx-indicator,
[data-page="settings"] .htmx-indicator {
  display: none;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.bao-settings-control-row .htmx-indicator.htmx-request,
[data-settings-control-kind="input"] > .htmx-indicator.htmx-request,
[data-settings-control-kind="select"] > .htmx-indicator.htmx-request,
[data-page="settings"] .htmx-indicator.htmx-request {
  display: inline-block;
  width: 1rem;
  height: 1rem;
}

[data-page="settings"] .input,
[data-page="settings"] .select,
[data-page="settings"] input[type="text"],
[data-page="settings"] select {
  width: 100%;
  min-height: 2.25rem;
  max-height: 2.75rem;
}

[data-page="settings"] .bao-settings-control-row[data-settings-control-kind="toggle"] > input[type="checkbox"] {
  inline-size: 2.75rem;
  block-size: 1.5rem;
  min-inline-size: 2.75rem;
  max-inline-size: 2.75rem;
  justify-self: end;
  flex: 0 0 auto;
  accent-color: var(--color-primary);
}

/* Hardware capability groups are supplied as newline-delimited runtime state. */
[data-page="settings"] [data-settings-control="capture.hardware-summary"] {
  display: block;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.bao-settings-pref-field {
  margin: 0;
  padding: var(--space-inline);
  border: 1px solid var(--border-base);
  border-radius: var(--radius-field, 0.75rem);
  background: var(--bg-panel);
}


  .bao-settings-pair-actions {
    display: grid;
    min-width: 0;
    grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
    gap: var(--space-inline);
  }
  .bao-settings-pair-actions > button {
    min-width: 0;
    width: 100%;
    justify-content: center;
  }

.bao-settings-panels-host,
#settings-panels {
  min-width: 0;
}

/*
 * Settings value readout — read-only current-value display (rendered as
 * <output>). Semantic hook composed with the typography/width utilities in
 * READOUT_CLASS and READOUT_MONO_CLASS (packages/shared/settings-controls-classes.ts).
 * Owns the value baseline: a readout never overflows its settings cell.
 */
.bao-settings-value-readout {
  min-width: 0;
  width: 100%;
  max-width: 100%;
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-medium, 500);
  letter-spacing: var(--tracking-tight, -0.025em);
  color: var(--color-base-content, var(--text-primary));
  overflow-wrap: anywhere;
  user-select: text;
}

.bao-settings-value-readout-mono {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-xs, 0.75rem);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
  word-break: break-all;
}

/*
 * Long settings values: collapsed by default, revealed in full on demand.
 *
 * `DataSection.bao` declared `valueReadoutDisclosure` / `valueReadoutFull` and
 * shipped them with no selector at all, so both rendered unstyled — the same
 * dangling-class defect class as the glass surfaces before them. The collapsed
 * form clamps to two lines instead of truncating on one, because a single-line
 * ellipsis on a device path or an endpoint URL hides the segment that
 * distinguishes it; the expanded form drops the clamp and lets long tokens
 * break anywhere so nothing overflows the panel.
 */
.bao-settings-value-disclosure {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.bao-settings-value-full {
  display: block;
  overflow-wrap: anywhere;
  word-break: break-word;
  white-space: pre-wrap;
}

/*
 * Settings tabpanels — exclusive visibility (daisyUI tabs content pattern).
 * Inactive panels must leave layout completely so Appearance/theme cannot
 * paint under every tab when all panels were stacked.
 * Cover attribute, utility class, and aria-hidden for WebView 101 + HTMX.
 */
.bao-settings-tab-panel[hidden],
.bao-settings-tab-panel.hidden,
.bao-settings-tab-panel[aria-hidden="true"],
[data-page="settings"] [data-settings-panel][hidden],
[data-page="settings"] [data-settings-panel].hidden,
[data-page="settings"] [data-settings-panel][aria-hidden="true"] {
  display: none;
  visibility: hidden;
  pointer-events: none;
}

.bao-settings-tab-panel:not([hidden]):not(.hidden):not([aria-hidden="true"]),
[data-page="settings"] [data-settings-panel]:not([hidden]):not(.hidden):not([aria-hidden="true"]) {
  display: block;
  visibility: visible;
  pointer-events: auto;
  min-width: 0;
  width: 100%;
}

/* Appearance control layout is owned by the generated DataSection primitive. */

/*
 * Dual Settings sheet banned — theme/locale only on Settings → Accessibility.
 * No #settings-dialog chrome; residual class tokens are compile placeholders only.
 */


/* ---- Topbar zones: start flexes, center is content-sized, end is fixed. ---- */
#app-header.navbar {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-inline, 0.5rem);
  /* Apple HIG / notch: keep chrome clear of status bar + home indicator. */
  min-height: calc(var(--chrome-min-height, 2.25rem) + env(safe-area-inset-top, 0px));
  height: auto;
  padding-block: 0;
  padding-block-start: env(safe-area-inset-top, 0px);
  padding-inline: max(var(--space-inline, 0.5rem), env(safe-area-inset-left, 0px))
    max(var(--space-inline, 0.5rem), env(safe-area-inset-right, 0px));
  min-width: 0;
  width: 100%;
  max-width: 100%;
  overflow-x: clip;
}
#app-header .navbar-start,
#app-header .navbar-center,
#app-header .navbar-end {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  width: auto;
  max-width: none;
  min-width: 0;
  min-height: var(--chrome-min-height, 2.25rem);
  gap: var(--space-2xs, 0.125rem);
}
/*
 * Symmetric zones (design.md §8.4 top command bar): start and end both flex
 * so the search column sits at the TRUE center — edge-pinned clusters with a
 * dead void between them read as an unfinished bar on wide displays.
 */
#app-header .navbar-start {
  flex: 0 1 auto;
  justify-content: flex-start;
  overflow: hidden;
}
#app-header .navbar-center {
  flex: 0 1 auto;
  justify-content: center;
  margin-inline: auto;
}
#app-header .navbar-end {
  flex: 0 0 auto;
  justify-content: flex-end;
}
/* Brand title: fluid clamp, never crushes the context strip. */
#app-header .bao-shell-brand {
  display: flex;
  min-width: 0;
  max-width: clamp(4.5rem, 18vw, 12rem);
  flex-shrink: 0;
  align-items: center;
}
@media (max-width: 39.99rem) {
  #app-header [data-context-switcher],
  #app-header .navbar-center,
  #app-header .navbar-end .bao-shell-topbar-actions [data-theme-toggle].btn,
  #app-header .navbar-end .bao-shell-topbar-actions [data-voice-command].btn,
  #app-header .navbar-end .bao-shell-topbar-actions [data-session-rail-sheet-open].btn {
    display: none;
  }

  #app-header.navbar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 7rem) auto;
    grid-template-areas: "menu title context actions";
    align-items: center;
    column-gap: var(--space-inline, 0.5rem);
    padding-block-end: 0;
  }

  #app-header.navbar .navbar-start {
    display: contents;
  }

  #app-header .navbar-start > [data-drawer-toggle] {
    grid-area: menu;
  }

  #app-header .navbar-start > .bao-shell-brand {
    grid-area: title;
    min-width: 0;
    max-width: none;
    overflow: hidden;
  }

  #app-header .navbar-start > [data-context-capsule].btn {
    grid-area: context;
  }

  #app-header .navbar-end {
    grid-area: actions;
    gap: var(--space-2xs, 0.125rem);
  }

  #app-header .navbar-start > [data-context-capsule].btn.bao-shell-context-capsule {
    width: 100%;
    max-width: 100%;
    justify-content: space-between;
  }

  body[data-page-header-mode="content"] #app-header .bao-shell-brand {
    display: none;
  }

  body[data-page-header-mode="shell"] #app-header .bao-shell-brand #shell-page-title,
  body[data-page-header-mode="shell"] #app-header .bao-shell-brand [data-shell-page-title] {
    display: block;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    line-height: 1.25;
  }
}

@media (max-width: 22.5rem) {
  #app-header.navbar {
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 5rem) auto;
  }

  body[data-page-header-mode="shell"] #app-header .bao-shell-brand #shell-page-title,
  body[data-page-header-mode="shell"] #app-header .bao-shell-brand [data-shell-page-title] {
    font-size: var(--text-xs, 0.75rem);
  }
}

@media (min-width: 40rem) {
  /* Medium/expanded: persistent search field is the ONE search affordance. */
  #app-header .navbar-end .bao-shell-topbar-actions [data-command-palette-trigger].btn {
    display: none;
  }
}
/* Voice status text: compact shells never show it; wider shells only when
   the runtime wrote content (owner: this module — never markup utilities). */
#app-header [data-voice-command-status] {
  display: none;
}
@media (min-width: 40rem) {
  #app-header [data-voice-command-status]:not(:empty) {
    display: inline;
  }
}
/* Drawer-toggle visibility is owned by tokens-glass-shell-drawer.ts
   (dock "More" on phone · hamburger 48–80rem · rail collapse ≥80rem). */
/* ---- Compact context capsule (visibility owned above). ---- */
#app-header .bao-shell-context-capsule {
  min-height: var(--touch-target-min, 2.75rem);
  max-width: clamp(8rem, 55vw, 14rem);
  /* Squeeze, never clip: when the navbar-start track runs out (offline pill +
     search + avatar on a phone), the capsule shrinks and its value span
     ellipsizes — a hard-clipped chevron reads as a broken control.
     Floor, never vanish: min-width:0 let the phone shell collapse the capsule
     to ~37px ("Dev …"), which names no workspace at all. Tenancy selects which
     patients are visible, so an unreadable active-context control is a safety
     regression, not a cosmetic one. The floor keeps status dot + a legible
     name stem + chevron; the track still shrinks below it on ultra-narrow
     shells because flex-shrink stays enabled. */
  min-width: clamp(6rem, 28vw, 9rem);
  flex: 0 1 auto;
  justify-content: flex-start;
  gap: var(--space-micro, 0.25rem);
  padding-inline: var(--space-inline, 0.5rem);
  /* Glass fill tokens — never nested backdrop blur inside blurred chrome. */
  background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
  border: 1px solid var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
}
#app-header .bao-shell-context-capsule [data-context-capsule-value] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#app-header .bao-shell-context-capsule > svg {
  flex: none;
}
/* The capsule carries NO status dot.
   It used to paint a 6px ::before that turned success-green once a context was
   set. That was a second green dot in the same topbar as the registry status
   chip, and the two meant different things — context selected vs registry
   reachable — while looking identical. It also signalled by colour alone,
   which the accessibility canon forbids for status.

   The capsule already states its own status in text: the value span names the
   active workspace, and data-context-status drives the sr-only Active/Unset
   label. That is the readable, non-colour-dependent signal, so the dot was
   pure redundancy. Connection state is the registry chip's job, and that chip
   now tints a real glyph per state rather than painting a dot. */

/*
 * ---- Context strip: ONE composed glass group, not loose floating words. ----
 * The strip is a content-sized glass-fill capsule housing the chips as
 * segments (same material vocabulary as the compact capsule); it hugs its
 * chips instead of stretching an invisible flex void across the bar.
 */
.bao-shell-context-strip {
  display: flex;
  min-width: 0;
  max-width: 100%;
  align-items: stretch;
  flex: 0 1 auto;
  width: auto;
  padding: var(--space-2xs, 0.125rem);
  border-radius: var(--radius-field, 0.75rem);
  background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
  border: 1px solid var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
  /* Fade edge + swipe scroll — chips never hard-clip mid-glyph (§9.5). */
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  touch-action: pan-x;
  scrollbar-width: none;
}
/*
 * Scroll-edge fades are STATE-DRIVEN, never unconditional (design.pdf §7.5
 * "add a LOCAL scroll-edge fade"; §9.5 "prevent button text from
 * disappearing").
 *
 * The previous rule masked calc(100% - 1.25rem) on the right at all times
 * and never masked the left. Two consequences, both shipped:
 *   1. When the chips FIT, the trailing chip was still faded out — content was
 *      sheared with nothing to scroll to ("Dev Workspace" losing its edge).
 *   2. When the strip WAS scrolled, the leading chip hard-clipped mid-glyph
 *      ("ev Workspace") because no left fade existed — the exact failure the
 *      old comment claimed to prevent.
 *
 * This reuses the SAME data-overflow-start / data-overflow-end contract
 * the page-header action strip already consumes, stamped from live scroll
 * metrics by bindActionStripOverflowState in app-shell-glass.ts. Both are
 * ABSENT while content fits, so a strip that cannot scroll carries no mask.
 */
.bao-shell-context-strip {
  --strip-fade: var(--space-section, 0.75rem);
}
.bao-shell-context-strip[data-overflow-end="true"] {
  mask-image: linear-gradient(to right, var(--black) calc(100% - var(--strip-fade)), transparent);
  -webkit-mask-image: linear-gradient(to right, var(--black) calc(100% - var(--strip-fade)), transparent);
}
.bao-shell-context-strip[data-overflow-start="true"] {
  mask-image: linear-gradient(to left, var(--black) calc(100% - var(--strip-fade)), transparent);
  -webkit-mask-image: linear-gradient(to left, var(--black) calc(100% - var(--strip-fade)), transparent);
}
.bao-shell-context-strip[data-overflow-start="true"][data-overflow-end="true"] {
  mask-image: linear-gradient(to right, transparent, var(--black) var(--strip-fade), var(--black) calc(100% - var(--strip-fade)), transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, var(--black) var(--strip-fade), var(--black) calc(100% - var(--strip-fade)), transparent);
}
.bao-shell-context-strip::-webkit-scrollbar { display: none; }
/* Contrast: drop fade mask; thin scrollbar keeps clipped chips discoverable. */
@media (prefers-contrast: more), (forced-colors: active) {
  .bao-shell-context-strip {
    mask-image: none;
    -webkit-mask-image: none;
    scrollbar-width: thin;
    scrollbar-color: var(--glass-scrollbar-thumb, color-mix(in oklab, var(--color-base-content) 40%, transparent)) var(--glass-scrollbar-track, transparent);
  }
  .bao-shell-context-strip::-webkit-scrollbar {
    display: block;
    height: 0.375rem;
  }
}
.bao-shell-context-chip-item + .bao-shell-context-chip-item {
  border-inline-start: 1px solid color-mix(in oklab, var(--color-base-content) 10%, transparent);
}
/* List is intrinsic width; strip owns scroll (never shrink-clip children). */
.bao-shell-context-chip-list {
  width: max-content;
  min-width: max-content;
  max-width: none;
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-micro, 0.25rem);
  align-items: stretch;
}
.bao-shell-context-chip-item {
  flex: 0 0 auto;
  min-width: 0;
  max-width: 13rem;
  width: auto;
}
/* Status-based visibility: set chips paint; workspace+case stay as CTAs. */
.bao-shell-context-chip-item[data-context-status="unset"] {
  display: none;
}
.bao-shell-context-chip-item[data-context-chip-kind="workspace"],
.bao-shell-context-chip-item[data-context-chip-kind="case"],
.bao-shell-context-chip-item[data-context-chip-kind="workspace"][data-context-status="unset"],
.bao-shell-context-chip-item[data-context-chip-kind="case"][data-context-status="unset"] {
  display: list-item;
}
/* Single-line value-first chips; HIG 44px floor (not chrome-min 36px). */
.bao-shell-context-chip {
  max-width: 11rem;
  min-height: var(--touch-target-min, 2.75rem);
  height: var(--touch-target-min, 2.75rem);
  padding-block: 0;
  padding-inline: var(--space-inline, 0.5rem);
  gap: var(--space-micro, 0.25rem);
  border: 0;
  background: transparent;
}
.bao-shell-context-chip-item[data-context-chip-kind="case"] .bao-shell-context-chip,
.bao-shell-context-chip-item[data-context-chip-kind="specimen"] .bao-shell-context-chip {
  max-width: 13rem;
}
.bao-shell-context-chip::before {
  content: "";
  width: 0.375rem;
  height: 0.375rem;
  border-radius: var(--radius-full);
  flex: none;
  background: color-mix(in oklab, var(--color-base-content) 30%, transparent);
}
.bao-shell-context-chip[data-context-status="set"]::before {
  background: var(--color-success);
}
.bao-shell-context-chip[data-context-status="unset"] {
  opacity: var(--opacity-chrome, 0.92);
}
.bao-shell-context-chip [data-context-label] {
  font-size: var(--text-xs, 0.75rem);
  line-height: 1;
  opacity: var(--opacity-soft, 0.65);
}
.bao-shell-context-chip [data-context-value] {
  font-size: var(--text-xs, 0.75rem);
  line-height: 1;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bao-shell-context-chip [data-context-state] {
  font-size: var(--text-xs, 0.75rem);
  line-height: 1;
}
/*
 * Fine pointer: hide kind/state until hover/focus (hover-to-reveal).
 * Instant clip only — never animate max-width/opacity. Layout-property
 * transitions on HTMX remounts read as continuous chip vibration (WebView 101).
 */
@media (hover: hover) and (pointer: fine) {
  .bao-shell-context-chip [data-context-label],
  .bao-shell-context-chip [data-context-state] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    max-width: 0;
    opacity: 0;
    transition: none;
  }
  .bao-shell-context-chip:hover [data-context-label],
  .bao-shell-context-chip:focus-visible [data-context-label],
  .bao-shell-context-chip:focus-within [data-context-label],
  .bao-shell-context-chip:hover [data-context-state],
  .bao-shell-context-chip:focus-visible [data-context-state],
  .bao-shell-context-chip:focus-within [data-context-state] {
    position: static;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    overflow: hidden;
    clip: auto;
    max-width: 6rem;
    opacity: var(--opacity-medium, 0.75);
    transition: none;
  }
}
/* Coarse pointer: keep ≥44px hit targets without multi-line stacks. */
@media (pointer: coarse) {
  .bao-shell-context-chip {
    min-height: var(--touch-target-min, 2.75rem);
    height: var(--touch-target-min, 2.75rem);
  }
}

@media (max-width: 64rem) {
  /* Strip scroll + fade + contrast fallback are global on
     .bao-shell-context-strip above;this band only demotes labels. */
  /* Value-first chips in the constrained band: the kind label consumes the
     width the value needs (tablet "WORKSPACE Dev Wo…" crush — design.md §9.5
     reflow: labels wrap or demote,
values stay readable). Kind stays
     available to AT via the visually-hidden label span. */
  .bao-shell-context-chip [data-context-label] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .bao-shell-context-chip [data-context-value] {
    max-width: 100%;
    font-weight: 600;
  }
}
@media (max-width: 47.99rem) {
  .bao-shell-context-chip { max-width: 9rem; }
  .bao-shell-context-chip-item[data-context-chip-kind="case"] .bao-shell-context-chip { max-width: 11rem; }
  .bao-shell-context-chip [data-context-label] { display: none; }
  .bao-shell-context-chip [data-context-state] { display: none; }
  .bao-shell-context-chip-item[data-context-chip-kind="case"][data-context-status="unset"] {
    display: none;
  }
  /* Tablet medium: demote specimen/operator/device chips — workspace+case
   * are the enterprise CTAs;full set lives in the context sheet. */
  .bao-shell-context-chip-item[data-context-chip-kind="specimen"],
.bao-shell-context-chip-item[data-context-chip-kind="operator"],
.bao-shell-context-chip-item[data-context-chip-kind="device"] {
    display: none;
  }
  /* Compact right cluster: tight gap keeps ≥44px targets without crowding. */
  #app-header .bao-shell-topbar-actions {
    gap: var(--space-2xs, 0.125rem);
  }
}

/* ---- Context sheet (compact bottom sheet, §8.6). ---- */
/* Sheet plate caps at the visual viewport and scrolls internally so the row
   list never pushes the dismiss affordance off a small phone.
   Row-list grid SSOT lives in tokens-glass-shell-overlays.ts
   (.bao-shell-context-sheet-list) — never re-grid the nav wrapper here: a
   second grid on nav halves the list track and quarters every row. */
.bao-shell-context-sheet {
  max-height: min(70vh, var(--vv-height, 100dvh));
  overflow-y: auto;
  overscroll-behavior-y: contain;
}
.bao-shell-context-sheet-item {
  min-height: var(--touch-target-min, 2.75rem);
}
.bao-shell-context-sheet-item[data-context-status="set"] {
  border-inline-start: 2px solid var(--color-success);
  background: color-mix(in oklab, var(--color-success) 6%, transparent);
}
.bao-shell-context-sheet-item[data-context-status="unset"] [data-context-value] {
  opacity: var(--opacity-soft, 0.65);
}
/* Set rows: success text on the state badge — text is the signal, color decorates. */
.bao-shell-context-sheet-item[data-context-status="set"] [data-context-state] {
  color: var(--color-success);
}

/*
 * Account menu → compact bottom sheet (§8.6 canonical transformation:
 * popovers become sheets on compact, matching the context sheet idiom).
 * One SSoT: the same dropdown panel is re-anchored to the dock's top edge;
 * >=40rem keeps the anchored dropdown. Specificity must beat the
 * .dropdown.dropdown-end > .dropdown-content inline-end anchoring.
 *
 * The block-end offset clears the dock, and the dock band is the whole reason
 * this needs saying. Anchoring to the raw safe-area inset put the sheet's lower
 * edge at the viewport floor: measured at 390x844 the account sheet ran to
 * y=836 while nav#mobile-dock starts at y=780, so 56px of the sheet — a full
 * row — sat on top of the dock's first tab. The dock's own box is
 * --dock-height + --dock-safe-area-offset (tokens-layout-dock-base), which is
 * what the filter sheet and the dashboard customizer tray already clear, and
 * it collapses to the bare safe area on its own wherever the dock is retired
 * (--dock-height: 0rem at >=48rem).
 */
@media (max-width: 39.99rem) {
  [data-topbar-user-menu].dropdown.dropdown-end > .dropdown-content {
    position: fixed;
    inset-inline: var(--space-inline, 0.5rem);
    inset-block-start: auto;
    inset-block-end: calc(
      var(--dock-height, 4rem) +
      var(--dock-safe-area-offset, env(safe-area-inset-bottom, 0px)) +
      var(--space-inline, 0.5rem)
    );
    margin: 0;
    width: auto;
    max-width: none;
    max-height: min(70vh, var(--vv-height, 100dvh));
    overflow-y: auto;
    overscroll-behavior-y: contain;
    border-radius: var(--radius-box, 1rem);
    box-shadow: var(--shadow-glass-floating, 0 12px 36px rgb(0 0 0 / 16%));
    z-index: var(--z-modal, 1300);
  }
  /* Sheet rows read as destinations: full-width, 44px targets (coarse). */
  [data-topbar-user-menu].dropdown.dropdown-end > .dropdown-content [role="menuitem"] {
    width: 100%;
    min-height: var(--touch-target-min, 2.75rem);
  }
}

/*
 * Session rail sheet (SH-06): bottom sheet on compact → side sheet ≥40rem
 * (§8.6 inspector transformation: full sheet → side sheet → persistent pane).
 * daisyUI modal-end owns the side anchoring; these rules only cap the track
 * and give the sheet body an internal scroll so the header never scrolls off.
 */
.bao-session-rail-sheet {
  display: flex;
  flex-direction: column;
  max-height: min(80vh, var(--vv-height, 100dvh));
}
.bao-session-rail-sheet-body {
  min-height: 0;
  flex: 1 1 auto;
}
@media (min-width: 40rem) {
  .bao-session-rail-sheet.modal-box {
    width: min(26rem, 92vw);
    max-width: 92vw;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }
}

/* Tooltips must paint outside chrome boxes (daisyUI ::before / ::after). */
#app-header.navbar {
  overflow-x: visible;
  overflow-y: visible;
}/* Chip strip off: capsule + context-sheet own switching at every width. */
#app-header [data-context-switcher],
#app-header .bao-shell-context-strip {
  display: none;
}
/* Capsule is the sole topbar context control. */
#app-header .navbar-start [data-context-capsule].btn {
  display: inline-flex;
}/* Theme + voice live in the user menu — keep topbar sparse. */
#app-header .navbar-end .bao-shell-topbar-actions > [data-theme-toggle].btn,
#app-header .navbar-end .bao-shell-topbar-actions > button[data-theme-toggle],
#app-header .navbar-end .bao-shell-topbar-actions > [data-voice-command].btn,
#app-header .navbar-end .bao-shell-topbar-actions > button[data-voice-command] {
  display: none;
}
/* Registry status: icon glyph only (label is aria + title). */
#app-header .bao-shell-status-badge {
  position: relative;
  place-content: center;
  width: var(--touch-target-min, 2.75rem);
  height: var(--touch-target-min, 2.75rem);
  padding: 0;
  border-radius: var(--radius-field, 0.75rem);
}
#app-header .bao-shell-status-badge [data-status-label] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
/*
 * Tablet band (48–79.99rem): context strip expansion (imp-t07). Phone keeps
 * the capsule + context sheet;≥80rem keeps the capsule. In between,
the capsule owns context at every band. The chip
 * strip was tried here and measured 100px of client width against 444px of
 * chips at 768x1024 — 344px hidden, 4 of 5 chips (Case, Specimen, Operator,
 * Device) unreachable. It could not claim a free inline track because none
 * exists: a 256px sidebar leaves a 512px topbar, and search plus actions are
 * flex-shrink:0 at 176px and 136px. No allocation fits 444px of chips there (imp: "context strip same as mobile" → expand on
 * tablet). One context control per band — the capsule retires here. These
 * rules must stay LAST in this module: equal-specificity base policy above
 * (capsule inline-flex / strip display:none) yields by source order.
 */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  #app-header .bao-shell-context-strip.bao-shell-context-strip-tablet {
    display: none;
  }
  #app-header .navbar-start [data-context-capsule].btn {
    display: inline-flex;
  }
}

/*
 * ---- Search (§8.6 canonical): compact = palette overlay, medium/expanded =
 * the ONE persistent field, rest-compact so center chrome never steals
 * workbench width; it expands while focused. Field surface uses the glass
 * FILL tokens — never nested backdrop blur inside already-blurred chrome.
 */
@media (max-width: 39.99rem) {
  /* Compact: palette trigger owns search — inline field must not compete (§8.6). */
  #app-header [data-topbar-search] {
    display: none;
  }
}
@media (min-width: 40rem) and (max-width: 63.99rem) {
  #app-header .navbar-start {
    flex: 1 1 auto;
    min-width: 0;
  }
  #app-header .navbar-center {
    flex: 0 0 auto;
    max-width: min(12rem, 30vw);
  }
  #app-header [data-topbar-search] {
    width: 100%;
    max-width: 100%;
  }
}
/* Canonical label.input host (icon inline — no absolute positioning). */
#app-header [data-topbar-search] .input {
  /* background-color LONGHAND: the shorthand resets background-image and
   * erases daisyUI component chrome (form-control-bg-longhand gate). */
  background-color: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
  border-color: var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
}
#app-header [data-topbar-search] .input > svg {
  opacity: var(--opacity-soft, 0.65);
}
/*
 * Tertiary icon actions rest QUIET (design.md §6: rest = quiet border, low
 * emphasis) — boxed btn-soft squares made every tertiary action shout.
 * Hover/focus paint the control fill; pressed keeps immediate feedback.
 */
#app-header .navbar-end .bao-shell-topbar-actions .btn.bao-shell-icon-action,
#app-header .navbar-end details.dropdown > summary.bao-shell-icon-action,
#app-header .navbar-start [data-drawer-toggle-button].btn {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
#app-header .navbar-end .bao-shell-topbar-actions .btn.bao-shell-icon-action:focus-visible,
#app-header .navbar-end details.dropdown > summary.bao-shell-icon-action:focus-visible,
#app-header .navbar-start [data-drawer-toggle-button].btn:focus-visible {
  background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
  border-color: var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
}
/* Hybrid hover (imp-t04): fill paints only where hover is real — touch taps
   must not leave a sticky-hover fill on header chrome. */
@media (hover: hover) and (pointer: fine) {
  #app-header .navbar-end .bao-shell-topbar-actions .btn.bao-shell-icon-action:hover,
  #app-header .navbar-end details.dropdown > summary.bao-shell-icon-action:hover,
  #app-header .navbar-start [data-drawer-toggle-button].btn:hover {
    background: var(--glass-control-fill, color-mix(in oklab, var(--color-base-content) 5%, transparent));
    border-color: var(--glass-control-border, color-mix(in oklab, var(--color-base-content) 8%, transparent));
  }
}
@media (hover: hover) and (pointer: fine) {
  #app-header [data-topbar-search] {
    max-width: 11rem;
    transition: none;
  }
  #app-header [data-topbar-search]:focus-within {
    max-width: min(100%, 20rem);
  }
  #app-header [data-topbar-search] .input {
    min-height: var(--chrome-min-height, 2.25rem);
    height: var(--chrome-min-height, 2.25rem);
  }
}
/* Focus expansion breathes with motion tokens; reduced motion keeps it instant. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  #app-header [data-topbar-search] {
    transition: max-width var(--duration-fast, 120ms) var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
  }
}
@media (pointer: coarse) {
  #app-header [data-topbar-search] {
    max-width: min(100%, 14rem);
  }
  #app-header [data-topbar-search] .input {
    min-height: var(--touch-target-min, 2.75rem);
  }
}
/*
 * ---- Registry status (navbar-end): glyph-first. Offline keeps shape+tint
 * plus a short label below 40rem / full label at >=40rem (never color alone).
 * Fine-pointer hover reveals the full label for connected/checking too.
 */
#app-header .bao-shell-status-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  min-height: var(--chrome-min-height, 2.25rem);
  /* Content-sized cap — warning/error labels must never clip mid-word
     (design.md §2.5/§9.1: status text legible in every state, all locales). */
  max-width: 14rem;
  gap: var(--space-micro, 0.25rem);
  padding-inline: 0.25rem;
}
#app-header .bao-shell-status-badge [data-status-label] {
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  /* No max-width/opacity transition — remount thrash = vibration. */
  transition: none;
}
@media (hover: hover) and (pointer: fine) {
  #app-header .bao-shell-status-badge:hover [data-status-label],
  #app-header .bao-shell-status-badge:focus-visible [data-status-label],
  #app-header .bao-shell-status-badge:focus-within [data-status-label] {
    max-width: 13rem;
    opacity: 1;
    transition: none;
  }
}
/*
 * Status glyph trio (design.md §2.11): one simple silhouette per state —
 * network while connected/checking, monitor when only local surfaces serve
 * (a healthy workstation must not wear the brand-adjacent network mark),
 * wifi-off when the runtime stamps data-registry-offline. Shape + tone +
 * label/AT text; never color alone.
 */
#app-header .bao-shell-status-badge [data-status-glyph] {
  display: inline-flex;
  align-items: center;
}
#app-header .bao-shell-status-badge [data-status-glyph="local"],
#app-header .bao-shell-status-badge [data-status-glyph="offline"] {
  display: none;
}
#app-header .bao-shell-status-badge[data-registry-state="local"] [data-status-glyph="local"] {
  display: inline-flex;
}
#app-header .bao-shell-status-badge[data-registry-state="local"] [data-status-glyph="connected"] {
  display: none;
}
#app-header .bao-shell-status-badge[data-registry-offline="true"] [data-status-glyph="offline"],
#app-header .bao-shell-status-badge[data-registry-state="offline"] [data-status-glyph="offline"] {
  display: inline-flex;
}
#app-header .bao-shell-status-badge[data-registry-offline="true"] [data-status-glyph="connected"],
#app-header .bao-shell-status-badge[data-registry-offline="true"] [data-status-glyph="local"],
#app-header .bao-shell-status-badge[data-registry-state="offline"] [data-status-glyph="connected"] {
  display: none;
}
/* Checking: the network glyph breathes until the runtime resolves a state
   (stable dimensions — the pulse is opacity-only, never layout). */
#app-header .bao-shell-status-badge[data-registry-state="checking"] [data-status-glyph="connected"],
#app-header .bao-shell-status-badge:not([data-registry-state]) [data-status-glyph="connected"] {
  animation: bao-status-checking var(--duration-slow, 1.6s) var(--easing-standard, ease-in-out) infinite alternate;
}
@keyframes bao-status-checking {
  from { opacity: 1; }
  to { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  #app-header .bao-shell-status-badge[data-registry-state="checking"] [data-status-glyph="connected"],
  #app-header .bao-shell-status-badge:not([data-registry-state]) [data-status-glyph="connected"] {
    animation: none;
  }
}
/* OPERATOR-CONFIRMED DESIGN DECISION (supersedes plan P2 wording — do not
   resurrect the text pills): the chip is glyph-only at EVERY width. wifi-off
   is an unambiguous silhouette (§2.11); words live in title/aria-label and
   the fine-pointer hover/focus reveal above. */
/* Offline: the wifi-off glyph in error tone carries the signal at rest —
   never a permanent alarm-tinted slab in global chrome (SH-10, §4 tint
   selectively). The severity plate appears on hover/focus, where the chip's
   retry affordance is actually targeted; forced-colors keeps a system border. */
#app-header .bao-shell-status-badge[data-registry-offline="true"] {
  background: transparent;
  border-color: transparent;
  color: var(--color-error);
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #app-header .bao-shell-status-badge[data-registry-offline="true"]:hover,
  #app-header .bao-shell-status-badge[data-registry-offline="true"]:focus-visible,
  #app-header .bao-shell-status-badge[data-registry-offline="true"]:focus-within {
    background: color-mix(in oklab, var(--color-error) 10%, transparent);
    border-color: color-mix(in oklab, var(--color-error) 30%, transparent);
  }
}
/* Offline chip is actionable (retry registry) — match touch target + keyboard. */
#app-header button.bao-shell-status-badge[data-registry-offline="true"] {
  min-height: var(--touch-target-min, 2.75rem);
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
}
@media (pointer: coarse) {
  #app-header .bao-shell-status-badge {
    min-height: var(--touch-target-min, 2.75rem);
  }
}
/*
 * Hover-to-reveal: shell topbar densify only (design.md §6).
 * Page-header clinical CTAs (finish/generate/soft secondaries) keep permanent
 * labels — never hide essentials until hover on fine pointer either.
 */
@media (hover: hover) and (pointer: fine) {
  #app-header .bao-toolbar-action:not(.btn-primary) .bao-toolbar-label {
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    margin-inline-start: 0;
    transition: none;
  }
  #app-header .bao-toolbar-action:not(.btn-primary):hover .bao-toolbar-label,
  #app-header .bao-toolbar-action:not(.btn-primary):focus-visible .bao-toolbar-label,
  #app-header .bao-toolbar-action:not(.btn-primary):focus-within .bao-toolbar-label {
    max-width: 12rem;
    opacity: 1;
    margin-inline-start: var(--space-micro, 0.25rem);
    transition: none;
  }
}

/*
 * AI overlay findings: selectable assessment rows (provider/model/confidence/shape).
 * Solid content plane inside glass chrome; selected uses base-200 + primary ring.
 */
[data-ai-overlay-item] {
  cursor: pointer;
  border-radius: var(--radius-field, 0.75rem);
  border: 1px solid transparent;
  padding: var(--space-micro, 0.25rem);
  min-height: var(--touch-target-min, 2.75rem);
  transition: background-color var(--motion-duration-fast, 120ms) var(--motion-easing-standard, ease),
    border-color var(--motion-duration-fast, 120ms) var(--motion-easing-standard, ease);
}
[data-ai-overlay-item]:focus-visible {
  outline: 2px solid color-mix(in oklab, var(--color-primary) 55%, transparent);
  outline-offset: 2px;
}
[data-ai-overlay-item][data-selected="true"],
[data-ai-overlay-item][aria-selected="true"] {
  background: var(--color-base-200, var(--bg-panel-muted));
  border-color: color-mix(in oklab, var(--color-primary) 35%, var(--color-base-300));
}
@media (hover: hover) and (pointer: fine) {
  [data-ai-overlay-item]:hover:not([data-selected="true"]):not([aria-selected="true"]) {
    background: color-mix(in oklab, var(--color-base-200) 55%, transparent);
  }
}

/*
 * Tablet topbar: context strip scrolls in start; search stays a capped center
 * column (visibility SSOT lives in tokens-glass-shell-topbar-zones.ts).
 */
@media (min-width: 40rem) and (max-width: 63.99rem) {
  #app-header [data-context-switcher] {
    display: flex;
  }
}

/*
 * Context sheet (compact bottom sheet): single column on phone, two-up on
 * tablet so five context rows fit without a tall scroll wall.
 */
.bao-shell-context-sheet-list,
.bao-shell-context-sheet .bao-shell-context-sheet-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-micro, 0.25rem);
  width: 100%;
  min-width: 0;
  list-style: none;
  margin: 0;
  padding: var(--space-section, 0.75rem);
  padding-block-start: var(--space-micro, 0.25rem);
  /*
   * Bottom sheet pins to the screen edge on phone — the last rows must
   * clear the home-indicator safe area (Apple HIG). Same token/formula
   * family as .bao-dialog-footer-safe (tokens-layout-fabric-stage.ts),
   * additive here because the section pad stays.
   */
  padding-block-end: calc(var(--space-section, 0.75rem) + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 40rem) and (max-width: 63.99rem) {
  .bao-shell-context-sheet-list,
  .bao-shell-context-sheet .bao-shell-context-sheet-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.bao-shell-context-sheet-list > li,
.bao-shell-context-sheet .bao-shell-context-sheet-list > li {
  min-width: 0;
}

/*
 * Command palette — glass box chrome (CommandPalette.bao) + solid list rows.
 * Active row uses base-200 fill; hover is pointer-gated only.
 */
#command-palette [data-command-palette-results] [data-command-palette-item],
#command-palette [data-command-palette-results] a[role="option"],
#command-palette [data-command-palette-results] button[role="option"] {
  display: flex;
  align-items: center;
  min-height: var(--touch-target-min, 2.75rem);
  width: 100%;
  min-width: 0;
  padding-inline: var(--space-inline, 0.5rem);
  padding-block: var(--space-micro, 0.25rem);
  border-radius: var(--radius-field, 0.75rem);
  border: 1px solid transparent;
  background: var(--color-base-100, var(--bg-panel));
  color: var(--color-base-content);
}
/*
 * Filtered-out rows: the rank module hides non-matches by toggling .hidden
 * ([hidden] parity), but the display:flex rule above sits at (1,2,0) and
 * beats Tailwind's (0,1,0) utility — this scoped (1,3,0) override lets
 * hidden rows actually leave the painted tree.
 */
#command-palette [data-command-palette-results] [data-command-palette-item].hidden,
#command-palette [data-command-palette-results] [data-command-palette-item][hidden] {
  display: none;
}
#command-palette [data-command-palette-results] [data-command-palette-item][aria-selected="true"],
#command-palette [data-command-palette-results] a[role="option"][aria-selected="true"],
#command-palette [data-command-palette-results] button[role="option"][aria-selected="true"] {
  background: var(--color-base-200);
  border-color: color-mix(in oklab, var(--color-base-content) 8%, transparent);
  color: var(--color-base-content);
}
@media (hover: hover) and (pointer: fine) {
  #command-palette [data-command-palette-results] [data-command-palette-item]:hover:not([aria-selected="true"]),
  #command-palette [data-command-palette-results] a[role="option"]:hover:not([aria-selected="true"]),
  #command-palette [data-command-palette-results] button[role="option"]:hover:not([aria-selected="true"]) {
    background: var(--bg-surface-hover);
  }
}
#command-palette [data-command-palette-results] .menu {
  gap: var(--space-2xs, 0.125rem);
}


/*
 * Settings group reflow — container-scoped, not viewport-scoped.
 *
 * .bao-settings-group declares container-type: inline-size / container-name:
 * settings-group (tokens-glass-shell-settings.ts). That container had no
 * consumer: every reflow below hung off @media (max-width: 42rem), so the
 * groups paid for inline-size containment and answered to the window anyway.
 *
 * Groups tile in the panel grid — "Settings groups tile via panel grid" in
 * tokens-glass-chrome-part.ts, and the appearance group spans it with
 * grid-column: 1 / -1 — so a group is routinely ~20-24rem wide inside a
 * 1280px window. A viewport breakpoint cannot see that: the three-track
 * toggle row (label | switch | indicator) kept its wide layout while the
 * label track floored at 0 and crushed the setting name, and the label/control
 * pair in a plain row had no width left to share. The same group is wide at
 * 1-up on a phone-width sheet and narrow at 2-up on a desktop, which is
 * exactly the condition a container query expresses and a media query cannot.
 */
@container settings-group (max-width: 42rem) {
  [data-page="settings"] .bao-settings-control-row {
    padding-block: var(--space-inline);
  }

  /*
   * Drop the indicator out of the track list so the label keeps the inline
   * size instead of ellipsing behind a spinner slot that is empty at rest.
   */
  [data-page="settings"] .bao-settings-control-row[data-settings-control-kind="toggle"] {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-micro) var(--space-inline);
  }
}

/*
 * Narrow groups stack label above control. ROW_CLASS is a
 * justify-between flex row and INPUT_CLASS/SELECT_CLASS are w-full, so below
 * this width the control claims the row and the label is left with a few
 * characters. Stacking keeps the full setting name readable and gives the
 * control the whole group width — the same card-stack move the DataTable
 * makes at its own container threshold.
 */
@container settings-group (max-width: 28rem) {
  [data-page="settings"] .bao-settings-control-row:not([data-settings-control-kind="toggle"]) {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: var(--space-micro);
  }
}

/*
 * Touch sizing stays viewport-scoped: a coarse pointer is a property of the
 * device the window is on, not of how wide one settings group happens to be.
 * Group padding stays here too — a container query never styles its own
 * container, so .bao-settings-group cannot answer @container settings-group.
 */
@media (max-width: 42rem) {
  [data-page="settings"] .bao-settings-group.fieldset,
  [data-page="settings"] fieldset.bao-settings-group {
    padding: var(--space-inline);
  }

  [data-page="settings"] .input,
  [data-page="settings"] .select,
  [data-page="settings"] input[type="text"],
  [data-page="settings"] select {
    min-height: var(--touch-target-min, 2.75rem);
    max-height: none;
  }

  [data-page="settings"] .bao-settings-control-row[data-settings-control-kind="toggle"] > input[type="checkbox"] {
    inline-size: 3rem;
    block-size: 1.75rem;
    min-inline-size: 3rem;
    max-inline-size: 3rem;
  }
}

/* Uniform collection/detail section rhythm — same gutters as home. */
[data-page-type="collection"] .bao-data-panel,
[data-page-type="detail"] .bao-data-panel,
[data-page-type="ops"] .bao-data-panel {
  border-radius: var(--radius-box, 1rem);
}



/* Align collapse is global (all breakpoints) — not phone-media nested. */

/* Live + aligning: secondary acquire chrome collapses (all breakpoints).
   The overlay is ONE bottom bar (shutter row + action bar) — only the action
   bar collapses;the shutter stays put for framing. */
.bao-preview-region[data-preview-state="live"] [data-capture-action-bar],
.bao-preview-region[data-preview-state="live"] [data-capture-action-bar],
.bao-preview-region[data-preview-aligning="true"][data-camera-preview] [data-capture-action-bar],
.bao-preview-region[data-preview-aligning="true"] [data-capture-action-bar] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  max-width: 0;
  max-height: 0;
  overflow: hidden;
  margin: 0;
  padding: 0;
  border-width: 0;
  transform: translateY(0.5rem);
  transition-property: opacity, visibility, transform, max-width;
  transition-duration: var(--duration-fast, 120ms);
  transition-timing-function: var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
}.bao-preview-region[data-preview-state="live"][data-preview-controls="expanded"] [data-capture-action-bar] {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  max-width: none;
  max-height: none;
  overflow: visible;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {.bao-preview-region[data-preview-state="live"] [data-capture-action-bar],
.bao-preview-region[data-preview-aligning="true"] [data-capture-action-bar] {
    transition-property: none; transition-duration: var(--duration-instant, 0ms);
    transform: none;
  }
}

/*
 * Capture session mode tabs only — rectangular field chrome (not pill capsules).
 * Settings tab list SSOT lives in tokens-glass-shell-settings + layout-framework
 * (desktop horizontal rail / phone vertical Apple list). Never force settings
 * to row here or phone destinations overflow the viewport.
 * daisyUI tabs-box pill styling is dead on WebView 101; use field radius.
 */
[data-session-mode-tabs],
[data-page-type="media"] .tabs.tabs-box.bao-tabs-scroll {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: var(--space-micro, 0.25rem);
  /*
   * Compact segmented control that HUGS its tabs (design.md §5.4: one compact
   * affordance). The tabs already refuse to grow (flex: 0 1 auto below), but
   * the host was still a slab: measured on /capture at 375px the strip
   * computed to 359px around 191px of tabs, leaving ~160px of empty capsule
   * that reads as a third, dead segment. width:min() also silently overrode
   * the w-fit the markup asks for — the cap belongs on max-width so the box
   * shrink-wraps first and only clips/scrolls once the tabs exceed it.
   *
   * Start-aligned, not centred: this control shares a band with the header
   * overflow button and the case/specimen chip, both of which sit on the
   * content start edge. Centring it put three different start edges in one
   * band.
   *
   * The hug comes from align-self, NOT from an intrinsic width. Both
   * fit-content and max-content collapsed this box to 10px (padding + border
   * only) — measured twice on /capture, not assumed: the host is its own
   * scroll container (overflow-x: auto below) whose items are flex: 0 1 auto,
   * and intrinsic sizing resolved to their shrink floor. Leaving width auto
   * and asking the column parent not to stretch us is the sizing path that
   * actually shrink-wraps, and max-width still caps and scrolls the strip
   * once the tabs outgrow it.
   */
  width: auto;
  align-self: flex-start;
  max-width: min(100%, 24rem);
  margin-inline: 0;
  min-height: var(--touch-target-min, 2.75rem);
  padding: var(--space-micro, 0.25rem);
  margin-block: 0 var(--space-section, 0.75rem);
  border: 1px solid var(--color-base-300, var(--border-base));
  border-radius: var(--radius-field, 0.75rem);
  /* Solid segmented control host — not frosted data skin (Liquid Glass: chrome only). */
  background: var(--color-base-200, var(--bg-panel));
  box-shadow: none;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
[data-session-mode-tabs] .tab,
[data-session-mode-tabs] .bao-session-mode-tab {
  /* Segmented control hugs its content (HIG): stretching two tabs across
     the page column reads as wasted chrome, so no flex-grow. */
  flex: 0 1 auto;
  justify-content: center;
}

[data-session-mode-tabs] .tab,
[data-session-mode-tabs] .bao-session-mode-tab,
.tabs.tabs-box .bao-session-mode-tab,
[data-page-type="media"] .tabs.tabs-box .bao-session-mode-tab {
  display: inline-flex;
  flex: 0 1 auto;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-micro, 0.25rem);
  /* WCAG/HIG: never below 44px on phone/coarse. */
  min-height: var(--touch-target-min, 2.75rem);
  min-width: max-content;
  margin: 0;
  padding: var(--space-inline, 0.5rem) var(--space-section, 0.75rem);
  border: 1px solid transparent;
  border-radius: var(--radius-field, 0.75rem);
  background: transparent;
  color: var(--color-base-content);
  opacity: 1;
  font-size: var(--text-sm, 0.875rem);
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: none;
}

[data-session-mode-tabs] .tab:not(.tab-active) {
  color: color-mix(in oklab, var(--color-base-content) 72%, transparent);
}

[data-session-mode-tabs] .tab-active,
[data-session-mode-tabs] .bao-session-mode-tab.tab-active,
[data-session-mode-tabs] .bao-session-mode-tab[aria-selected="true"],
.tabs.tabs-box .bao-session-mode-tab.tab-active,
[data-page-type="media"] .tabs.tabs-box .bao-session-mode-tab.tab-active,
[data-page-type="media"] .tabs.tabs-box .bao-session-mode-tab[aria-selected="true"] {
  background: var(--color-primary);
  color: var(--color-primary-content);
  opacity: 1;
  border-color: var(--color-primary);
  /* One primary tint only — no elevation spectacle under dense workbench.
     Selector mirrors the base chrome's strongest arm so the active state
     always outranks the transparent base (one owner, no specificity war). */
  box-shadow: none;
}

[data-session-mode-tabs] .tab svg,
[data-session-mode-tabs] .bao-session-mode-tab svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

/*
 * Phone session tabs: icon-first segmented control (not full pill + long label).
 * aria-label on the tab keeps the mode name for assistive tech.
 */
@media (max-width: 47.99rem) {
  /* Two-segment mode switch keeps its labels on phones — icon-only camera/pen
     glyphs are ambiguous for a mode change and the strip has the width
     (HIG segmented controls stay labeled). Tabs just densify. */
  [data-session-mode-tabs] .tab,
  [data-session-mode-tabs] .bao-session-mode-tab {
    position: relative;
    flex: 1 1 0;
    min-width: var(--touch-target-min, 2.75rem);
    max-width: none;
    padding-inline: var(--space-inline, 0.5rem);
    gap: var(--space-micro, 0.25rem);
    justify-content: center;
  }
}

/*
 * Capture device fieldsets — flat, content-sized groups. The command chrome
 * band owns the surface; per-field card boxes multiplied five empty panels
 * across the capture fold, so fields carry no border/background of their own.
 */
[data-capture-command-chrome] fieldset,
fieldset[data-capture-control] {
  display: flex;
  flex-direction: column;
  gap: var(--space-micro, 0.25rem);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}

[data-capture-command-chrome] fieldset legend,
fieldset[data-capture-control] legend {
  padding-inline: var(--space-micro, 0.25rem);
  font-size: var(--text-2xs, 0.6875rem);
  font-weight: var(--font-weight-semibold, 600);
  letter-spacing: var(--tracking-wide, 0.025em);
  text-transform: uppercase;
  /* Label copy uses solid text-secondary — not translucent body (design.md §2.10). */
  color: var(--text-secondary, var(--color-base-content));
}

[data-capture-command-chrome] select,
[data-capture-command-chrome] .select,
fieldset[data-capture-control] select {
  width: 100%;
  min-height: var(--touch-target-min, 2.75rem);
  border: 1px solid var(--color-base-300, var(--border-base));
  border-radius: var(--radius-selector, 0.75rem);
  /* background-color LONGHAND: the shorthand resets background-image and
   * erased these selects' daisyUI caret (form-control-bg-longhand gate).
   * padding keeps the 1.75rem inline-end caret zone for the same reason. */
  background-color: var(--color-base-100, var(--bg-panel));
  padding: var(--space-micro, 0.25rem) var(--space-inline, 0.5rem);
  padding-inline-end: 1.75rem;
  font-size: var(--text-sm, 0.875rem);
}

/*
 * Capture workbench: preview + shutter above fold on phone.
 * Side panel (specimen) scrolls below; never starve the stage.
 */
/* Desktop + tablet: stage owns the viewport (design.md content plane first). */
[data-page="capture"] [data-capture-workbench],
[data-page-type="media"] [data-capture-workbench] {
  min-height: min(62vh, 36rem);
}
[data-page="capture"] .bao-preview-region,
[data-page-type="media"] .bao-preview-region {
  min-height: min(48vh, 28rem);
  flex: 1 1 auto;
}
/* Media pathologist workflow: one dense horizontal strip, never a second page. */
[data-page-type="media"] > [data-related-links][data-pathologist-workflow],
[data-page="capture"] > [data-related-links][data-pathologist-workflow],
[data-page="editor"] > [data-related-links][data-pathologist-workflow],
[data-page="ai-analysis"] [data-related-links][data-pathologist-workflow] {
  flex: 0 0 auto;
  max-height: 3.25rem;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  margin-block: 0;
  padding-block: var(--space-2xs, 0.125rem);
}
[data-page-type="media"] [data-related-links][data-pathologist-workflow] > h2,
[data-page="capture"] [data-related-links][data-pathologist-workflow] > h2,
[data-page="editor"] [data-related-links][data-pathologist-workflow] > h2,
[data-page="ai-analysis"] [data-related-links][data-pathologist-workflow] > h2 {
  display: none;
}
[data-page-type="media"] [data-related-links][data-pathologist-workflow] ol,
[data-page="capture"] [data-related-links][data-pathologist-workflow] ol,
[data-page="editor"] [data-related-links][data-pathologist-workflow] ol,
[data-page="ai-analysis"] [data-related-links][data-pathologist-workflow] ol {
  flex-wrap: nowrap;
  width: max-content;
  min-width: 100%;
}
[data-page-type="media"] [data-related-links][data-pathologist-workflow] ol > li,
[data-page="capture"] [data-related-links][data-pathologist-workflow] ol > li,
[data-page="editor"] [data-related-links][data-pathologist-workflow] ol > li,
[data-page="ai-analysis"] [data-related-links][data-pathologist-workflow] ol > li {
  flex: 0 0 auto;
  min-width: 0;
  max-width: none;
}

@media (max-width: 64rem) {
  [data-page-type="media"] [data-capture-workbench] {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  [data-capture-workbench-primary],
  .bao-media-workbench-primary {
    order: 0;
    flex: 1 1 auto;
    min-height: min(48vh, 22rem);
  }

  [data-page="capture"]
    .bao-preview-region:not([data-preview-state="live"]) {
    flex: 0 0 auto;
    height: auto;
  }

  [data-capture-workbench-secondary],
  .bao-media-workbench-secondary {
    order: 1;
    flex: 0 0 auto;
    max-height: none;
  }

  .bao-preview-region {
    min-height: min(44vh, 20rem);
    border: 1px solid var(--color-base-300, var(--border-base));
    border-radius: var(--radius-panel, 1rem);
    overflow: hidden;
    background: var(--color-base-200, var(--bg-panel));
  }
  /* Idle empty state: strong hierarchy so mobile WebView never shows a blank slab. */
  .bao-preview-region > [data-camera-preview-placeholder] {
    color: var(--color-base-content, var(--text-primary));
  }
  /*
   * HERO glyph only. This used to be a descendant selector, so it also
   * captured the SVGs inside the empty-state's own action buttons: measured
   * on /capture, the "Select camera" and "Gallery" icons rendered 48x48 in
   * --color-primary instead of 16x16 in their button's foreground. On the
   * primary-filled "Select camera" that is blue-on-blue — the icon was there
   * in the DOM and invisible on screen.
   *
   * The hero glyph is a direct child of the placeholder; the actions sit one
   * level down inside a fieldset. A child combinator therefore separates them
   * exactly, with no exclusion list to keep in sync.
   */
  .bao-preview-region > [data-camera-preview-placeholder] > svg {
    width: 3rem;
    height: 3rem;
    color: var(--color-primary, var(--accent-primary));
    /* Decorative icon mute via token — not body text opacity (design.md §2.10). */
    opacity: var(--pulse-alpha-floor, 0.55);
  }
  .bao-preview-region > [data-camera-preview-placeholder] p:first-of-type {
    font-size: var(--text-sm, 0.875rem);
    font-weight: var(--font-weight-semibold, 600);
    /* High-opacity semantic text tokens — never semitransparent body copy. */
    color: var(--color-base-content, var(--text-primary));
  }
  .bao-preview-region > [data-camera-preview-placeholder] p:last-of-type {
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-secondary, var(--color-base-content));
    max-width: 18rem;
  }
  @media (max-width: 47.99rem) {
    .bao-preview-region {
      min-height: min(50vh, 18rem);
    }
  }

  [data-capture-preview-overlay] .btn,
  [data-capture-action-bar] .btn {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
  }
}


  /*
   * Capture preview overlay bar — glass-standard media control layer
   * (design.pdf §5.11: controls attached to media are a canonical glass
   * layer; L1 chrome floating over the L0 stream). Unlayered token CSS
   * outranks the element's bg-base-100 utility. Solid-first fallback +
   * reduced-transparency / forced-colors variants per the a11y matrix.
   */
  [data-capture-preview-overlay] {
    background: color-mix(
      in oklab,
      var(--bg-panel) calc(var(--liquid-fill-persistent) * 100%),
      transparent
    );
    backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
    -webkit-backdrop-filter: blur(var(--liquid-blur-regular)) saturate(var(--liquid-saturate-regular));
    border-color: var(--liquid-border-regular);
    box-shadow:
      var(--shadow-glass-elevated),
      var(--shadow-glass-inner);
  }
  @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    [data-capture-preview-overlay] {
      background: var(--bg-panel);
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
  }

  /*
   * Overflow affordance panel — L2 transient glass-strong (menus/sheets).
   * daisyUI .dropdown-content owns positioning; tokens own the material.
   */
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
    background: color-mix(
      in oklab,
      var(--bg-panel) calc(var(--liquid-fill-transient) * 100%),
      transparent
    );
    backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
    -webkit-backdrop-filter: blur(var(--liquid-blur-nav)) saturate(var(--liquid-saturate-strong));
    border: 1px solid var(--liquid-border-regular);
    border-radius: var(--radius-box, 1rem);
    box-shadow:
      var(--shadow-glass-floating),
      var(--shadow-glass-inner);
    padding: var(--space-inline, 0.5rem);
  }
  @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
      background: var(--bg-panel);
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
  }
  /* Overflow panel scroll containment (long action lists on small hosts). */
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
    max-height: min(60dvh, 24rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /*
   * §8.6 canonical transformation: the overflow panel is a bottom sheet on
   * compact — same idiom as the account-menu sheet in
   * tokens-glass-shell-topbar-context-sheet.ts. The panel is popover-promoted
   * (top layer) because the page track's container-type traps fixed panels
   * under the toolbar clip; these rules must out-rank the controller's
   * .bao-dropdown-panel-viewport geometry and the popover UA inset defaults.
   *
   * The block-end offset clears the dock band, not just the safe area:
   * measured at 390x844 the sheet's lower edge landed at y=836 against a
   * nav#mobile-dock whose top edge is y=780, burying the "Capture" tab under
   * the last two rows. --dock-height + --dock-safe-area-offset is the dock's
   * own box (tokens-layout-dock-base) and it zeroes itself at >=48rem, so the
   * same expression is correct in every band.
   */
  @media (max-width: 39.99rem) {
    [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
      position: fixed;
      inset-inline: var(--space-inline, 0.5rem);
      inset-block-start: auto;
      inset-block-end: calc(
        var(--dock-height, 4rem) +
        var(--dock-safe-area-offset, env(safe-area-inset-bottom, 0px)) +
        var(--space-inline, 0.5rem)
      );
      margin: 0;
      width: auto;
      max-width: none;
      z-index: var(--z-modal, 1300);
    }
  }
  /* ≥40rem keeps the anchored dropdown, sized to its rows — never the 44px
     trigger column daisyUI infers from the icon-button host. */
  @media (min-width: 40rem) {
    [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
      width: max-content;
      min-width: 14rem;
      max-width: min(92vw, 24rem);
    }
  }
  /*
   * Tier discipline (Apple camera grammar): the persistent bar is gallery ·
   * shutter · overflow only. Any tiered action rendered directly into the
   * bar is a contract violation — it belongs in the grouped overflow sheet.
   * ONE rule replaces the retired per-action hide chains (geometry nuke
   * idiom: the shell-wide .btn display chains tie a lone display:none).
   */
  [data-capture-action-bar] > [data-capture-tier],
  [data-capture-action-bar] > [data-disabled-tip-host] > [data-capture-tier] {
    display: none;
    width: 0;
    min-width: 0;
    max-width: 0;
    height: 0;
    min-height: 0;
    overflow: hidden;
    padding: 0;
    margin: 0;
    border: 0;
    pointer-events: none;
    visibility: hidden;
  }
  /* Grouped sheet sections read as one list with hairline separators. */
  [data-capture-overflow-group] + [data-capture-overflow-group] {
    border-block-start: 1px solid
      color-mix(in oklab, var(--color-base-300, var(--border-base)) 60%, transparent);
    padding-block-start: var(--space-micro, 0.25rem);
  }
  /* 44px touch floor on plain menu rows (route items carry no .btn). */
  @media (pointer: coarse) {
    [data-capture-overflow-panel] [role="menuitem"] {
      min-height: var(--touch-target-min, 2.75rem);
    }
  }
  /*
   * Overflow menu rows are labeled list items rendered with the menuItemRow
   * token (route-row parity) — never btn-square toolbar chrome. The retired
   * .bao-toolbar-action/.bao-toolbar-label restore blocks fought the
   * icon-first hover-reveal cascade property-by-property and lost to a
   * higher-specificity fine-pointer clipper; fixing the row markup at the
   * emitter ends that war (pages-renderers-capture-actions.ts).
   */
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
    flex-direction: column;
    align-items: stretch;
  }
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] [data-disabled-tip-host] {
    width: 100%;
  }

  /*
   * Overlay controls — theme tokens only (no raw hex paints).
   * Idle/light preview: solid vibrancy (glass-control parity) — no second
   * backdrop-filter (glass-on-glass ban; blur SSOT stays on floating glass shells).
   * Live dark frames: tinted chip contrast over media, still no re-blur.
   */
  [data-capture-preview-overlay] .btn:not(.btn-primary):not(.bao-capture-shutter),
  [data-capture-action-bar] .btn:not(.btn-primary) {
    color: var(--color-base-content);
    background: color-mix(in oklab, var(--color-base-100, var(--bg-panel)) 92%, transparent);
    border: 1px solid color-mix(in oklab, var(--color-base-300, var(--border-base)) 80%, transparent);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: 0 2px 10px color-mix(in oklab, var(--color-base-content) 12%, transparent);
  }
  .bao-preview-region[data-preview-state="live"] [data-capture-preview-overlay] .btn:not(.btn-primary):not(.bao-capture-shutter),
  .bao-preview-region[data-preview-state="live"] [data-capture-action-bar] .btn:not(.btn-primary){
    color: var(--overlay-text-primary, var(--color-base-100));
    background: color-mix(in oklab, var(--color-base-content) 42%, transparent);
    border: 1px solid color-mix(in oklab, var(--color-base-100) 28%, transparent);
    box-shadow: 0 2px 10px color-mix(in oklab, var(--color-base-content) 28%, transparent);
  }
  /* Capture chrome is touch-primary — hover accents are fine-pointer only
     (design.md §6); a stuck post-tap hover would recolor overlay controls. */
  @media (hover: hover) and (pointer: fine) {
    [data-capture-preview-overlay] .btn:not(.btn-primary):not(.bao-capture-shutter):hover,
    [data-capture-action-bar] .btn:not(.btn-primary):hover {
      background: color-mix(in oklab, var(--color-primary) 55%, transparent);
      border-color: color-mix(in oklab, var(--color-primary) 50%, transparent);
      color: var(--color-primary-content);
    }
  }
  [data-capture-preview-overlay] .btn.btn-primary,
  [data-capture-preview-overlay] .bao-capture-shutter {
    color: var(--color-primary-content);
    background: var(--color-primary);
    border-color: transparent;
    min-width: var(--touch-target-min, 2.75rem);
    min-height: var(--touch-target-min, 2.75rem);
  }

/* Capture floating glass nests only — session mode tabs are chromeSurface solid
 * (sticky page chrome law). Blur SSOT: tokens-glass-base; a11y: tokens-glass-a11y. */
[data-capture-chrome] .bao-surface-glass-regular {
  background: color-mix(in oklab, var(--color-base-100, var(--bg-panel)) 86%, transparent);
}

@media (prefers-reduced-transparency: reduce) {
  /* .bao-glass-control listed mid-group so S77 does not treat this as the base block. */
  [data-capture-chrome] .bao-glass-control,
  [data-capture-chrome] .bao-surface-glass-regular,
  [data-capture-preview-overlay],
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
    background: var(--color-base-200);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

@media (forced-colors: active) {
  [data-capture-preview-overlay],
  [data-page="capture"] [data-capture-action-disclosure] > [data-capture-overflow-panel] {
    background: Canvas;
    color: CanvasText;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 1px solid ButtonText;
    box-shadow: none;
  }
}

/* Inspector disclosures: keep long AI/spatial panels inside the rail. */
[data-editor-inspector-disclosure] .collapse-content {
  max-height: min(24rem, 50vh);
  overflow-y: auto;
}

/*
 * Primary shutter — Apple capsule affordance for the sole primary capture action.
 * Larger than secondary toolbar icons; stays >=44dp touch target.
 */
.bao-capture-shutter,
[data-capture-action="capture"].btn-circle {
  width: 3.5rem;
  height: 3.5rem;
  min-width: 3.5rem;
  min-height: 3.5rem;
  box-shadow: 0 4px 16px color-mix(in oklab, var(--color-primary) 35%, transparent);
}
.bao-capture-shutter svg,
[data-capture-action="capture"].btn-circle svg {
  width: 1.25rem;
  height: 1.25rem;
}
/*
 * Touch shutter — iOS ring idiom: 72px disc with a light outer ring so the
 * primary capture action reads as THE camera control, thumb-reachable over
 * the viewfinder. Ring is box-shadow only (no layout shift, no extra DOM).
 */
@media (pointer: coarse), (max-width: 47.99rem) {
  /* Repeated-class specificity (0,4,0): the ring must outrank every btn
     variant chain without resorting to importance. */
  [data-page="capture"] .bao-capture-shutter.bao-capture-shutter.bao-capture-shutter,
  [data-capture-preview-overlay] .bao-capture-shutter.bao-capture-shutter.bao-capture-shutter,
  [data-capture-preview-overlay] .bao-capture-shutter,
  .bao-capture-shutter,
  [data-capture-action="capture"].btn-circle {
    width: 4.5rem;
    height: 4.5rem;
    min-width: 4.5rem;
    min-height: 4.5rem;
    /* Logical corner longhands too — btn chrome sets corners via longhands,
       which beat a shorthand from any earlier rule. */
    border-radius: var(--radius-full, 9999px);
    border-start-start-radius: var(--radius-full, 9999px);
    border-start-end-radius: var(--radius-full, 9999px);
    border-end-start-radius: var(--radius-full, 9999px);
    border-end-end-radius: var(--radius-full, 9999px);
    box-shadow:
      0 0 0 3px color-mix(in oklab, var(--color-primary-content) 85%, transparent),
      0 0 0 6px color-mix(in oklab, var(--color-primary) 45%, transparent),
      0 6px 20px color-mix(in oklab, var(--color-primary) 35%, transparent);
  }
  .bao-capture-shutter svg,
  [data-capture-action="capture"].btn-circle svg {
    width: 1.75rem;
    height: 1.75rem;
  }
}
.bao-capture-shutter:disabled,
[data-capture-action="capture"].btn-circle:disabled {
  box-shadow: none;
}


/* ============================================================
   Capture pipeline phase + recording overlay (Bao-Translate UX)
   SINGLE definition — the class-based twin in
   tokens-glass-shell-capture-recording.ts was removed (SSOT: one
   scrim treatment, one data-phase state driver).
   Phase truth on [data-capture-chrome][data-phase]; CSS reflects only.
   ============================================================ */

[data-capture-chrome][data-phase="idle"] [data-capture-recording-overlay],
[data-capture-chrome][data-phase="error"] [data-capture-recording-overlay],
[data-capture-chrome][data-phase] [data-capture-recording-overlay][hidden] {
  display: none;
}

[data-capture-chrome][data-phase="starting"] [data-capture-recording-overlay],
[data-capture-chrome][data-phase="recording"] [data-capture-recording-overlay],
[data-capture-chrome][data-phase="processing"] [data-capture-recording-overlay] {
  display: flex;
}

[data-capture-recording-overlay] {
  position: absolute;
  inset: 0;
  z-index: var(--z-modal, 1300);
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-inline, 0.5rem);
  padding: var(--space-section, 0.75rem);
  padding-block-end: calc(var(--space-section, 0.75rem) + env(safe-area-inset-bottom, 0px));
  background: color-mix(in oklab, var(--color-base-content) 55%, transparent);
}

[data-capture-recording-dock] {
  width: min(100%, var(--max-w-form, 28rem));
}

[data-capture-recording-halo] {
  display: grid;
  place-items: center;
  inline-size: 4.5rem;
  block-size: 4.5rem;
  border-radius: var(--radius-full);
  background: color-mix(in oklab, var(--color-error) 88%, transparent);
  color: var(--color-error-content, var(--color-base-100));
}

[data-capture-chrome][data-phase="recording"] [data-capture-action="record-audio"],
[data-capture-chrome][data-phase="recording"] [data-capture-action="record-video"],
[data-capture-chrome][data-phase="starting"] [data-capture-action="record-audio"],
[data-capture-chrome][data-phase="starting"] [data-capture-action="record-video"] {
  color: var(--color-error-content, var(--color-base-100));
  background: var(--color-error);
  border-color: transparent;
}

[data-capture-live-source] {
  font-style: italic;
  opacity: var(--opacity-strong, 0.85);
  max-inline-size: min(36rem, 100%);
  text-align: center;
}

[data-capture-live-final] {
  font-style: normal;
  font-weight: 600;
  max-inline-size: min(36rem, 100%);
  text-align: center;
}

[data-capture-waveform] {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 0.125rem;
  inline-size: min(16rem, 80%);
  block-size: 2.5rem;
}

[data-capture-waveform] span {
  flex: 1 1 0;
  min-inline-size: 0.125rem;
  block-size: 30%;
  border-radius: var(--radius-full);
  background: color-mix(in oklab, var(--color-primary) 70%, var(--color-base-content));
  transform-origin: bottom center;
  /* Amplitude via data-waveform-level 0..10 — never inline CSS custom props. */
  transform: scaleY(0.22);
}

[data-capture-waveform] span[data-waveform-level="0"] { transform: scaleY(0.22); }
[data-capture-waveform] span[data-waveform-level="1"] { transform: scaleY(0.3); }
[data-capture-waveform] span[data-waveform-level="2"] { transform: scaleY(0.38); }
[data-capture-waveform] span[data-waveform-level="3"] { transform: scaleY(0.46); }
[data-capture-waveform] span[data-waveform-level="4"] { transform: scaleY(0.54); }
[data-capture-waveform] span[data-waveform-level="5"] { transform: scaleY(0.62); }
[data-capture-waveform] span[data-waveform-level="6"] { transform: scaleY(0.7); }
[data-capture-waveform] span[data-waveform-level="7"] { transform: scaleY(0.78); }
[data-capture-waveform] span[data-waveform-level="8"] { transform: scaleY(0.86); }
[data-capture-waveform] span[data-waveform-level="9"] { transform: scaleY(0.94); }
[data-capture-waveform] span[data-waveform-level="10"] { transform: scaleY(1); }

[data-capture-recording-status] {
  font-variant-numeric: tabular-nums;
}

[data-capture-recording-actions] {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-inline, 0.5rem);
}

/* Tuning scrubbers: 44px touch floor on the primary touch surface. */
@media (pointer: coarse) {
  [data-uvc-tuning-panel] input,
  [data-uvc-tuning-panel] .btn {
    min-height: var(--touch-target-min, 2.75rem);
  }
}



/*
 * Capture side panel recent-captures slot fetches the section-param rail
 * variant (?sections=captures) — the server emits only the captures section,
 * so no consumer-side CSS hiding of annotation/AI rail sections exists here.
 */

/*
 * Permanent-rail band (≥80rem CSS media — not Tailwind xl: utilities; parity
 * with tokens-glass-shell-drawer session-rail show). Right rail already lists
 * captures; hide the side-panel twin so one HTMX consumer stays visible.
 */
@media (min-width: 80rem) {
  [data-page="capture"] [data-capture-recent-captures] {
    display: none;
  }
}

/* Context strip: muted skeleton pulse while HTMX hydrates live context. */
[data-capture-context-strip][aria-busy="true"] [data-capture-context],
[data-capture-context-strip][data-state="loading"] [data-capture-context] {
  opacity: var(--pulse-alpha-floor, 0.55);
  pointer-events: none;
}

/* Live preview: hide placeholder when stream is attached (WebView 101). */
[data-camera-preview][data-preview-state="live"] [data-camera-preview-placeholder] {
  display: none;
}

/* Pathology preview: contain (not cover) so anatomy is not cropped. */
[data-camera-preview-video] {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: inherit;
  background: var(--color-base-200);
}


/*
 * Stacked workbench: never shrink below the stage's own height.
 *
 * Once the column stacks, .bao-media-workbench is a flex:1 1 auto item in a
 * scrollport whose free space is already spent, so flex-shrink pulled it 22px
 * BELOW its content while its only visible child (the primary column) is
 * flex:0 0 auto and refuses to give the space back. With overflow visible the
 * surplus painted straight over the disclosure beneath it — measured on
 * /capture at 320x720: workbench 446px around 468px of content, #camera-preview
 * ending at 648 while the "Device Controls" details started at 634, a 14px
 * collision that the structure audit reports as an occluded control.
 *
 * Refusing to shrink is the fix, not a min-height: the scrollport absorbs the
 * extra (scrollHeight 564 -> 586), the details lands 8px clear of the stage,
 * and the stage keeps the height its own content asked for.
 */
@media (max-width: 64rem) {
  [data-page-type="media"] .bao-media-workbench,
  [data-page-type="media"] [data-capture-workbench] {
    flex-shrink: 0;
  }
}

/* Status readout chips (not fieldsets) share the same solid control shell. */
[data-capture-command-chrome] [data-capture-control][role="status"] {
  display: flex;
  flex-direction: column;
  gap: var(--space-micro, 0.25rem);
  min-width: 0;
  margin: 0;
  padding: var(--space-micro, 0.25rem) var(--space-inline, 0.5rem);
  border: 1px solid var(--color-base-300, var(--border-base));
  border-radius: var(--radius-field, 0.75rem);
  background: var(--color-base-100, var(--bg-panel));
}

[data-capture-command-chrome] [data-capture-control][role="status"] .badge,
[data-capture-command-chrome] [data-capture-control][role="status"] span:last-child {
  font-size: var(--text-sm, 0.875rem);
  font-weight: 500;
  color: var(--color-base-content);
}

/*
 * AI analysis context collapse is global (all breakpoints).
 * Shell context chips already surface case/specimen/patient/accession.
 * Keep readonly fields in the DOM (sr-clip) for form submit + app-shell
 * prefill — never paint raw UUIDs or duplicate chrome on desktop/tablet.
 */
[data-ai-analysis-form] fieldset:has([data-ai-analysis-context-field]),
[data-ai-analysis-form] [data-ai-analysis-context-hint] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Phone: status is informational only — reclaim stage for shutter. */
@media (max-width: 47.99rem) {
  /*
   * Dock the media page-header actions beside the mode strip instead of
   * giving them a band of their own.
   *
   * The media page header's title block is sr-only (the shell topbar owns
   * naming) and both desktop actions are max-md:hidden, so below 48rem the
   * only thing left in the header is the "More actions" overflow menu —
   * measured on /capture at 375px: 57px of bordered chrome around one 44px
   * control, stacked above the mode strip behind its own divider line. It
   * read as an empty section.
   *
   * The header is the immediate previous sibling of the strip inside
   * .bao-workbench, so taking it out of flow promotes the strip to the first
   * row and the actions dock at that row's end. Padding plus a transparent
   * border mirror the control row height, so the button lands on its centre
   * line from tokens rather than from a measured offset.
   *
   * Declared here, not in tokens-layout-capture-preview-part-b: that module
   * emits inside @layer utilities and the canonical
   * .bao-page-header width:100% is unlayered, which wins over any layered
   * width no matter how specific.
   */
  [data-page-type="media"] > [data-state-content] {
    position: relative;
  }
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"] {
    position: absolute;
    inset-block-start: 0;
    inset-inline-end: var(--page-gutter-inline, var(--space-section, 0.75rem));
    z-index: var(--z-content-overlay, 10);
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    width: max-content;
    padding-block: var(--space-micro, 0.25rem);
    padding-inline: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-field, 0.75rem);
  }
  /*
   * The header's leading slot is a flex-1 spacer that separates title from
   * actions on desktop. Out of flow with the title sr-only it has nothing to
   * separate, and it grew the docked box back to the full column width
   * (measured 341px), dragging the menu to the start edge.
   */
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"] > div:first-child:not(.sr-only):not(.bao-page-header-actions):empty {
    display: none;
  }
  /*
   * Keep the strip clear of the docked actions at every phone width: the cap
   * is the control's own touch target plus one gutter, so the two can never
   * meet however long the localized tab labels run.
   */
  [data-session-mode-tabs][data-session-mode-tabs],
  [data-page-type="media"] .tabs.bao-tabs-scroll[data-session-mode-tabs] {
    max-width: min(
      100% - var(--touch-target-min, 2.75rem) -
        (2 * var(--page-gutter-inline, var(--space-section, 0.75rem))),
      24rem
    );
  }
  /*
   * The editor's stage-action strip needs the same clearance, and did not have
   * it. The docked header is position:absolute at inset-inline-end, so on a
   * 390px phone its overflow control sits at x 329-373 while the last stage
   * action sits at x 333-377: browser-structure-audit measured the strip's
   * button winning elementFromPoint at the control's own centre, so the tap
   * that should open "More actions" fired a different command. Same cap as the
   * mode tabs above — one touch target plus a gutter each side.
   */
  [data-page-type="media"] [data-editor-stage-header] {
    max-inline-size: calc(
      100% - var(--touch-target-min, 2.75rem) -
        (2 * var(--page-gutter-inline, var(--space-section, 0.75rem)))
    );
  }
  [data-page="capture"] [data-capture-command-chrome] [data-capture-control][role="status"],
  [data-page="capture"] [data-capture-device-status] {
    display: none;
  }
  /* HIG/WCAG: enforce ≥44px touch targets on command chrome controls. */
  [data-page="capture"] [data-capture-command-chrome] .select,
  [data-page="capture"] [data-capture-command-chrome] .btn {
    min-height: var(--touch-target-min, 2.75rem);
  }
  [data-page="capture"] [data-capture-command-chrome] .bao-capture-device-grid {
    gap: var(--space-micro);
  }
  /* Model select in Analyze is SSOT on phone; catalog search is secondary. */
  [data-page="ai-analysis"] [data-model-search-bar] {
    display: none;
  }
}
/*
 * Permanent-rail band (≥80rem): session modes, recent captures, and the
 * capture side panel (case/specimen controls) move to #session-rail. The AI
 * review form remains in the editor inspector because it owns the real canvas
 * snapshot, model selection, candidate staging, and explicit adjudication.
 * Compact/sheet keep in-page controls when the permanent rail is inert. This
 * block must emit after display:flex on [data-session-mode-tabs] or dual
 * Acquire/Annotate chrome returns.
 */
@media (min-width: 80rem) {
  body:has(#session-rail:not([inert]))
    [data-page="capture"]
    [data-session-mode-tabs]:not([data-session-rail-mode-tabs]) {
    display: none;
  }
  body:has(#session-rail:not([inert]))
    [data-page="capture"]
    [data-capture-recent-captures] {
    display: none;
  }
  /*
   * Full-bleed canvas: case/specimen pickers move to the session rail
   * (data-session-rail-session-controls). Do not leave a second workbench
   * column that duplicates rail context (design.md §5.3 / permanent-rail SSOT).
   */
  body:has(#session-rail:not([inert]))
    [data-page="capture"]
    [data-capture-workbench-secondary] {
    display: none;
  }
  body:has(#session-rail:not([inert]))
    [data-page="capture"]
    [data-capture-workbench] {
    grid-template-columns: minmax(0, 1fr);
  }
  body:has(#session-rail:not([inert]))
    [data-page="capture"]
    .bao-media-workbench {
    grid-template-columns: minmax(0, 1fr);
  }
}


[data-capture-chrome][data-capture-case-ready="false"] [data-camera-preview-placeholder] {
  display: none;
}
[data-capture-chrome][data-capture-case-ready="false"] [data-capture-case-required] {
  display: flex;
}
[data-capture-chrome][data-capture-case-ready="true"] [data-capture-case-required] {
  display: none;
}

/*
 * Context readiness owns the empty plane, in both directions. Unlayered and
 * terminal: it must follow the generic empty-state geometry.
 */
/* Keep the selected workbench mode legible across the canonical tab variants. */
[data-session-mode-tabs] > .bao-session-mode-tab[aria-selected="true"] {
  --tab-bg: var(--color-primary);
  --tab-color: var(--color-primary-content);
  background: var(--color-primary);
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-content);
}

/* Selected capture medium remains visible over both idle and live preview materials. */
[data-page="capture"] .bao-preview-region[data-preview-state] [data-capture-preview-overlay] .bao-capture-media-mode-option.bao-capture-media-mode-option:has(input:checked) {
  color: var(--color-primary-content);
  background: var(--color-primary);
  border-color: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-primary-content) 35%, transparent);
}

[data-capture-preview-overlay] .bao-capture-media-mode-option:has(input:disabled):not(:has(input:checked)) {
  color: var(--text-secondary, var(--color-base-content));
  background: var(--color-base-200);
  border-color: var(--color-base-300);
  box-shadow: none;
  cursor: not-allowed;
  opacity: var(--opacity-muted, 0.55);
}

[data-capture-preview-overlay] .bao-capture-media-mode-option:has(input:focus-visible) {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}


@media (max-width: 64rem) {
  [data-page="capture"][data-session-mode="acquire"] [data-capture-workbench-primary],
  [data-page="capture"]:not([data-session-mode="annotate"]) [data-capture-workbench-primary] {
    flex: 0 0 auto;
    height: auto;
    overflow: visible;
  }
}


/* ============================================================
   Focus visibility — 2px outline per WCAG 2.2 SC 2.4.11
   ============================================================ */

/* Inputs wrapped by a daisyUI label control (.input/.select) announce focus
   on the WRAPPER via :focus-within — outlining the inner element too paints
   a double ring (one owner per reactive state; focus stays visible). */
.btn:focus-visible,
a:focus-visible,
button:focus-visible,
input:focus-visible:not(.input input, .select select),
textarea:focus-visible:not(.textarea textarea),
select:focus-visible:not(.select select),
.bao-touch-target:focus-visible,
.bao-toolbar-action:focus-visible,
.bao-settings-tab:focus-visible,
.tab:focus-visible,
.bao-nav-item:focus-visible,
[tabindex]:focus-visible {
  outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, var(--accent-primary));
  outline-offset: var(--focus-ring-offset, 2px);
}
.input input:focus-visible,
.select select:focus-visible,
.textarea textarea:focus-visible {
  outline: none;
}

:focus:not(:focus-visible) {
  outline: none;
}

/* Canon §11 / GLASS-15: the focus ring is re-themed for dark — the light
   #1E73BE measures only ≈3.4:1 against dark panels. The dark accent
   (#41B6E6) contrasts against BOTH the component and its surroundings. */
[data-theme="capture-dx-dark"] {
  --focus-ring-color: var(--accent-primary);
}

/* ============================================================
   HTMX indicator + swap motion (CSP-safe; no runtime style inject)
   Aligned with happydumpling app-shell contract (htmx 2.x).
   ============================================================ */

.htmx-indicator {
  opacity: 0;
  visibility: hidden;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
  visibility: visible;
  transition-property: opacity;
  transition-timing-function: var(--easing-entrance);
  transition-duration: var(--duration-normal);
}

/* Form controls inherit readable base-content (theme-safe). */


/* Card border polish — data panels lift without nested glass.
   design.md §7.2: hover/focus color transitions run in the 120–180ms band
   (duration-fast = 120ms), never the 200ms menu band. */
.card-border.bao-data-panel,
.bao-data-panel.card-border {
  transition-property: border-color, box-shadow;
  transition-timing-function: var(--easing-standard);
  transition-duration: var(--duration-fast);
}
@media (hover: hover) and (pointer: fine) {
  .card-border.bao-data-panel:hover,
  .bao-data-panel.card-border:hover {
    border-color: color-mix(in oklab, var(--text-primary) 18%, var(--border-base));
    box-shadow: 0 1px 0 0 color-mix(in oklab, var(--border-base) 40%, transparent);
  }
}

/* Named main content for view transitions (SPA-like HTMX swaps). */
#app-main {
  view-transition-name: main-content;
}

/* ============================================================
   Skeleton shimmer a11y overrides (phone densify + reduced motion)
   Base declaration + bao-skeleton-sweep keyframes live in
   tokens-glass-runtime-interactions.ts (single owner — DRY).
   ============================================================ */

/* Phone densify: freeze shimmer — infinite anim + HTMX load loops = vibration. */
@media (max-width: 47.99rem) {
  .skeleton-shimmer {
    background-image: none;
  }
}

/*
 * Entrance motion is transform-only. Opacity-from-0 stuck on WebView 101
 * (BlueStacks Chromium, tile_manager pressure) painted pure-white voids
 * over real SSR case rows while the header still painted. Never gate
 * readability on an animation that may not complete.
 */
@keyframes slide-up-fade {
  from {
    transform: translateY(var(--entrance-offset));
  }
  to {
    transform: translateY(0);
  }
}

/* Standardized entrance motion.
   Desktop main-nav uses View Transitions (hx-swap with VT modifier) —
   do NOT also run page-content entrances on those swaps (double refresh
   flash). Toasts and in-page .htmx-added fragments still animate. */
@media (prefers-reduced-motion: no-preference) {
  #toast-region > * {
    animation: slide-up-fade var(--duration-normal) var(--easing-entrance);
  }
  /* Desktop only — on phone HTMX re-adds nodes every poll and re-animates = vibrate.
   * Context strips remount on every context-changed — never entrance-animate them. */
  @media (min-width: 48rem) {
    .htmx-added:not([data-chat-context]):not([data-capture-context-strip]):not(.bao-context-card):not(.bao-shell-context-chip) {
      animation: slide-up-fade var(--duration-normal) var(--easing-entrance) both;
    }
  }
  [data-chat-context].htmx-added,
  [data-chat-context] .htmx-added,
  [data-capture-context-strip].htmx-added,
  [data-capture-context-strip] .htmx-added,
  .bao-context-card.htmx-added,
  .bao-shell-context-chip.htmx-added {
    animation: none;
    transform: none;
  }
  /* design.md §7.2: button color/edge feedback is hover+press — 120ms
     (duration-fast) sits at the press ceiling and hover floor. */
  .btn {
    transition-property: color, background-color, border-color, box-shadow;
    transition-timing-function: var(--easing-standard);
    transition-duration: var(--duration-fast);
  }
  /* View transitions thrash WebView 101 compositing; desktop only. */
  @media (min-width: 48rem) {
    ::view-transition-old(main-content) {
      animation: fade-out-content var(--duration-normal) var(--easing-exit) both;
    }
    ::view-transition-new(main-content) {
      animation: slide-up-fade var(--duration-normal) var(--easing-entrance) var(--view-transition-new-delay) both;
    }
  }
}
@media (max-width: 47.99rem) {
  #app-main {
    view-transition-name: none;
  }
  
}
.bao-toast-exit {
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: var(--easing-exit);
  transition-duration: var(--duration-slow);
}

@keyframes fade-out-content {
  to { opacity: 0; }
}

/* Empty / error surfaces: solid panel, generous padding, no sparse float.
 *
 * The descendant selectors deliberately exclude two classes of element that
 * carry [role="status"]/[role="alert"] without being empty-state *content*:
 *
 *   :not([data-state])    — an element that declares its own state contract
 *                           owns its own presentation. Without this guard the
 *                           slim inline case-journey strip
 *                           ([data-case-journey][data-state="empty"]
 *                           [role="status"]) matched as a descendant of its own
 *                           empty wrapper and was inflated from a one-line flex
 *                           row into a 192px dashed column, overriding
 *                           [data-case-journey] purely on source order.
 *   :not(.htmx-indicator) — request indicators are transient chrome, not
 *                           content. loading-xs spinners were being given
 *                           min-block-size:12rem, so every indicator reserved
 *                           192px of layout even while at opacity 0.
 *   :not([data-editor-canvas-empty])
 *                         — the annotation stage's empty overlay is full-bleed
 *                           (absolute inset-0 over the canvas), not a card. It
 *                           was taking the panel's rounded corner while the
 *                           stage beneath stayed square, so each corner showed
 *                           the square edge through the curve as a notch.
 *
 * The two guards below stop that list growing an entry per indicator. Both
 * state the same invariant: a COSMETIC rule must never resurrect an element
 * something else deliberately hid.
 *
 *   :not([hidden])        — the runtime hid it by attribute. This selector is
 *                           ancestor-qualified, so validate-hidden-attribute-
 *                           authority's "conditional rules may outrank
 *                           [hidden]" carve-out let it win: the tablet case
 *                           detail pane's [data-case-detail-loading] span ships
 *                           `hidden`, and was still painted as a 192px dashed
 *                           panel with a lone spinner in it.
 *   :not([data-transient-chrome])
 *                         — chrome its own component contract hides in CSS
 *                           rather than by attribute, where an attribute would
 *                           break the states that must show it. The cases
 *                           pull-to-refresh indicator is display:none off
 *                           touch, but this rule outranked that (0,6,0 beats
 *                           0,1,0) and reserved 192px above every desktop case
 *                           list.
 *
 * Genuine empty panels (a [role="status"] child inside an empty host, or an
 * explicit .bao-empty-panel) still receive the full treatment.
 */
[data-page] [data-state="empty"] [role="status"]:not([data-state]):not([hidden]):not(.htmx-indicator):not([data-editor-canvas-empty]):not([data-transient-chrome]),
[data-page] [data-state="error"] [role="alert"]:not([data-state]):not([hidden]):not(.htmx-indicator):not([data-editor-canvas-empty]):not([data-transient-chrome]),
.bao-empty-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-inline);
  text-align: center;
  min-block-size: 12rem;
  padding-block: var(--space-page);
  padding-inline: var(--space-section);
  border: 1px dashed color-mix(in oklab, var(--border-base) 90%, transparent);
  border-radius: var(--radius-box, 1rem);
  background: color-mix(in oklab, var(--bg-panel) 88%, var(--bg-surface));
}


/* ============================================================
   Apple HIG accessibility preference modes
   ============================================================
   These media queries are emitted unconditionally so the system
   respects accessibility settings automatically. They are NOT
   opt-in. */

/* Reduced Transparency: solid chrome surface, no backdrop blur.
   WCAG/governance: glass loses to reduced-transparency prefs.
   Also honors product override data-reduced-transparency on html. */
@media (prefers-reduced-transparency: reduce) {
  .bao-surface-glass-regular,
  .bao-surface-glass-clear,
  .bao-surface-glass-clear-dim,
  .bao-surface-glass-strong,
  .bao-surface-glass-modal {
    background: var(--bg-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-surface-glass-regular.bao-glass-large,
  .bao-surface-glass-regular.bao-glass-small,
  .bao-surface-glass-strong.bao-glass-large,
  .bao-surface-glass-strong.bao-glass-small {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-surface-glass-lens,
  .bao-surface-glass-strong {
    background: var(--bg-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-glass-control,
  .bao-surface-glass-regular .btn-soft.bao-toolbar-action,
  .bao-surface-glass-regular .btn-ghost.bao-toolbar-action,
  .bao-surface-glass-regular .btn-soft:not(.btn-primary):not(.btn-error),
  .bao-surface-glass-clear .btn-soft.bao-toolbar-action,
  .bao-surface-glass-clear .btn-ghost.bao-toolbar-action {
    background-color: var(--color-base-100, var(--bg-panel));
    border-color: var(--color-base-300);
    box-shadow: none;
  }
  .bao-surface-glass-regular::before,
  .bao-surface-glass-regular::after,
  .bao-surface-glass-clear::before,
  .bao-surface-glass-clear::after,
  .bao-surface-glass-strong::before,
  .bao-surface-glass-strong::after,
  .bao-surface-glass-modal::before,
  .bao-surface-glass-modal::after {
    display: none;
  }
  /* Raw-blur surfaces outside the variant classes (GLASS-06): the sticky
     journey band and the filter-sheet scrim must solidify too. */
  .bao-filter-sheet-backdrop,
  [data-case-journey-host] {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Reactive hover/focus is (0,2,0) and the suppression selectors above are
     (0,1,0); a media query adds no specificity, so an OS-level
     reduce-transparency user got the translucent material back on hover and
     on keyboard focus — transparency without blur, over live content, on the
     exact surface they asked to be made solid. */
  .bao-surface-glass-reactive:hover,
  .bao-surface-glass-reactive:focus-within {
    background: var(--bg-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
html[data-reduced-transparency="true"] .bao-surface-glass-regular,
html[data-reduced-transparency="true"] .bao-surface-glass-clear,
html[data-reduced-transparency="true"] .bao-surface-glass-clear-dim,
html[data-reduced-transparency="true"] .bao-surface-glass-strong,
html[data-reduced-transparency="true"] .bao-surface-glass-modal,
html[data-reduced-transparency="true"] .bao-surface-glass-lens,
html[data-reduced-transparency="true"] .bao-surface-glass-strong {
  background: var(--bg-surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html[data-reduced-transparency="true"] .bao-surface-glass-regular::before,
html[data-reduced-transparency="true"] .bao-surface-glass-regular::after,
html[data-reduced-transparency="true"] .bao-surface-glass-clear::before,
html[data-reduced-transparency="true"] .bao-surface-glass-clear::after,
html[data-reduced-transparency="true"] .bao-surface-glass-strong::before,
html[data-reduced-transparency="true"] .bao-surface-glass-strong::after,
html[data-reduced-transparency="true"] .bao-surface-glass-modal::before,
html[data-reduced-transparency="true"] .bao-surface-glass-modal::after {
  display: none;
}
html[data-reduced-transparency="true"] .bao-filter-sheet-backdrop,
html[data-reduced-transparency="true"] [data-case-journey-host] {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* Shell density: compact clinical chrome (HIG 44px min chrome). */
html[data-density="compact"] {
  --space-page: 0.75rem;
  --space-section: 0.75rem;
  --space-inline: 0.5rem;
  --chrome-min-height: 2.5rem;
}
html[data-density="comfortable"] {
  --space-page: 1.25rem;
  --space-section: 1rem;
  --space-inline: 0.75rem;
  --chrome-min-height: 2.75rem;
}
html[data-density="spacious"] {
  --space-page: 1.5rem;
  --space-section: 1.25rem;
  --space-inline: 1rem;
  --chrome-min-height: 3rem;
}

/* Increased Contrast: drop the blur and tint entirely; rely on a solid
   border for the panel edge so the pane reads against any background.
   Apple HIG: "Increased contrast makes elements predominantly black or
   white and highlights them with a contrasting border".
   Also honors product override data-high-contrast on html so the
   Settings → Accessibility toggle can force the same behavior. */
@media (prefers-contrast: more) {
  .bao-surface-glass-regular,
  .bao-surface-glass-clear,
  .bao-surface-glass-strong,
  .bao-surface-glass-modal,
  .bao-glass-control,
  .dropdown-content,
  /* Mobile dock needs ID-level reach: tokens-layout-dock-base is emitted
     after this module, so a plain .bao-mobile-dock.dock ties on specificity
     and loses — the dock kept its blur under Increased Contrast.
     Conversation dock (form shell, already solid) gets the contrast border. */
  #mobile-dock.dock.bao-mobile-dock,
  nav#mobile-dock[data-mobile-dock],
  .bao-conversation-dock,
  footer.bao-conversation-dock {
    background: var(--bg-panel);
    border-width: 2px;
    /* The comment above promises the blur is dropped "entirely". Only the
       lens/strong follow-up rule used to do that, so regular, clear and modal
       kept a live 20-36px backdrop-filter behind an opaque fill — invisible
       output at full GPU cost. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Reactive hover/focus is (0,2,0); the suppression selectors above are
     (0,1,0) and a media query adds no specificity, so without these the
     material came back translucent the moment a pointer or focus entered. */
  .bao-surface-glass-reactive:hover,
  .bao-surface-glass-reactive:focus-within {
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-surface-glass-lens,
  .bao-surface-glass-strong {
    background: var(--bg-panel);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .bao-surface-glass-regular::before,
  .bao-surface-glass-regular::after,
  .bao-surface-glass-clear::before,
  .bao-surface-glass-clear::after,
  .bao-surface-glass-strong::before,
  .bao-surface-glass-strong::after,
  .bao-surface-glass-modal::before,
  .bao-surface-glass-modal::after {
    display: none;
  }
  /* GLASS-06: raw-blur surfaces outside the variant classes lose blur. */
  .bao-filter-sheet-backdrop,
  [data-case-journey-host] {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
html[data-high-contrast="true"] .bao-surface-glass-regular,
html[data-high-contrast="true"] .bao-surface-glass-clear,
html[data-high-contrast="true"] .bao-surface-glass-strong,
html[data-high-contrast="true"] .bao-surface-glass-modal {
  background: var(--bg-panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--liquid-border-contrast);
  border-width: 2px;
}
html[data-high-contrast="true"] .bao-surface-glass-lens,
html[data-high-contrast="true"] .bao-surface-glass-strong {
  background: var(--bg-panel);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html[data-high-contrast="true"] .bao-surface-glass-regular::before,
html[data-high-contrast="true"] .bao-surface-glass-regular::after,
html[data-high-contrast="true"] .bao-surface-glass-clear::before,
html[data-high-contrast="true"] .bao-surface-glass-clear::after,
html[data-high-contrast="true"] .bao-surface-glass-strong::before,
html[data-high-contrast="true"] .bao-surface-glass-strong::after,
html[data-high-contrast="true"] .bao-surface-glass-modal::before,
html[data-high-contrast="true"] .bao-surface-glass-modal::after {
  display: none;
}
html[data-high-contrast="true"] .bao-filter-sheet-backdrop,
html[data-high-contrast="true"] [data-case-journey-host] {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-page] [data-state-content]:not([hidden]),
  #toast-region > *,
  .htmx-added {
    animation: none;
  }
  .skeleton-shimmer {
    animation-duration: var(--duration-reduced-motion-calm);
  }
  .bao-pulse,
  .bao-pulse-scale,
  .bao-pulse-alpha {
    transform: none;
  }
  ::view-transition-old(main-content),
  ::view-transition-new(main-content) {
    animation: none;
  }
  .htmx-request .htmx-indicator,
  .htmx-request.htmx-indicator {
    transition-duration: var(--duration-instant, 0ms);
    transition-property: none;
  }
}

/* Reduced Motion: keep the global 1ms clamp that the existing media query
   already applies; nothing additional is needed here because the glass
   material itself has no continuous animation. The pointer-tracking
   script also disables itself when this media query matches. */
@media (prefers-reduced-motion: reduce) {
  *,
  ::before,
  ::after {
    animation-duration: var(--reduced-motion-min-duration, 0.001ms);
    animation-iteration-count: 1;
    transition-duration: var(--reduced-motion-min-duration, 0.001ms);
    scroll-behavior: auto;
  }
}


/* ============================================================
   Forced colors — respect user-selected system colors
   ============================================================ */

@media (forced-colors: active) {.bao-surface-glass-regular,
.bao-surface-glass-clear,
.bao-surface-glass-clear-dim,
.bao-surface-glass-strong,
.bao-surface-glass-strong,
.bao-surface-glass-modal,
.bao-surface-glass-solid,
.bao-surface-glass-lens,
/* Dock needs ID-level reach: tokens-layout-dock-base is emitted after this
     module,
so a plain .bao-mobile-dock.dock ties on specificity and loses.
     backdrop-filter is not force-adjusted by the UA,
so the blur survived. */
  #mobile-dock.dock.bao-mobile-dock,
nav#mobile-dock[data-mobile-dock],
.bao-conversation-dock,
footer.bao-conversation-dock,
.bao-dropdown-panel {
    color: CanvasText;
    background: Canvas;
    border: 1px solid ButtonText;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }/* Specular ring + inner illumination are gradient-only layers — never
     a state indicator,
so they are removed entirely. */
  .bao-surface-glass-regular::before,
.bao-surface-glass-regular::after,
.bao-surface-glass-clear::before,
.bao-surface-glass-clear::after,
.bao-surface-glass-strong::before,
.bao-surface-glass-strong::after,
.bao-surface-glass-modal::before,
.bao-surface-glass-modal::after {
    display: none;
  }/* GLASS-06: raw-blur surfaces outside the variant classes — the UA
     does not force-adjust backdrop-filter,
so strip it explicitly. */
  .bao-filter-sheet-backdrop,
[data-case-journey-host] {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .skeleton-shimmer {
    animation: none;
    background-image: none;
  }.btn:focus-visible,
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }/* Selected destination survives forced colors — the inset side-bar
     shadow cue is dropped by the UA,
so map to system selection colors. */
  .bao-nav-item.menu-active,
.bao-nav-item[aria-current="page"] {
    background: SelectedItem;
    color: SelectedItemText;
    box-shadow: none;
    border: 1px solid SelectedItemText;
  }
}



/* ============================================================
   Pointer-gated hover utilities (design.md §6: hover is an
   enhancement for fine pointers only — touch never traps a
   stuck hover state). Replace raw `hover:` utilities with these
   canonical classes so every hover effect is media-gated.
   ============================================================ */

@media (hover: hover) and (pointer: fine) {
  .bao-hover-row:hover {
    background-color: color-mix(in oklab, var(--color-base-content) 5%, transparent);
  }
  .bao-hover-soft:hover {
    background-color: var(--bg-surface-hover);
  }
  .bao-hover-soft-veil:hover {
    background-color: color-mix(in oklab, var(--color-base-200) 80%, transparent);
  }
  .bao-hover-strong:hover {
    background-color: var(--color-base-300);
  }
  .bao-hover-panel-border:hover {
    border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
  }
  .bao-hover-text-primary:hover {
    color: var(--color-primary);
  }
  .bao-hover-underline:hover {
    text-decoration-line: underline;
  }
  .bao-hover-panel:hover {
    background-color: var(--color-base-100);
  }
  .bao-hover-border-s-primary:hover {
    border-inline-start-color: var(--color-primary);
  }
  .bao-hover-overlay-btn:hover {
    background-color: var(--overlay-btn-secondary-bg);
  }
}

/* ============================================================
   Scrim SSOT — one overlay-backdrop token owns every blocking
   scrim. daisyUI .modal-backdrop defaults to a raw black tint;
   align it with the canonical overlay token (design.md §5.8).
   ============================================================ */

.modal .modal-backdrop,
.modal-backdrop {
  background: var(--overlay-backdrop);
}

/* design.md §6: disabled keeps the label readable — drop elevation/chroma
   instead of crushing text via daisyUI opacity (design.md §2.10). */
.btn:disabled,
.btn-disabled {
  opacity: 1;
  background-color: var(--color-base-200, var(--bg-panel));
  border-color: var(--color-base-300);
  /* Readable chroma drop via solid mix against panel — never element opacity
   * (design.md §2.10 / §6; gate S113 requires ≥45% content in the mix). */
  color: color-mix(in oklab, var(--color-base-content) 55%, var(--color-base-200, var(--bg-panel)));
  pointer-events: none;
  box-shadow: none;
}



/* ═══════════════════════════════════════════════════════════════════
   FLUID TRANSITIONS — Apple Liquid Glass depth-aware animations
   ═══════════════════════════════════════════════════════════════════ */

/*
 * Spring-physics entrance: overshoot then settle.
 * Uses transform + opacity (compositor-only, no layout thrash).
 */
.bao-fluid-enter {
  animation: bao-fluid-enter-in var(--duration-slow, 320ms) var(--easing-entrance, cubic-bezier(0.05, 0.7, 0.1, 1)) both;
}
@keyframes bao-fluid-enter-in {
  0% {
    opacity: 0;
    transform: translateY(var(--entrance-offset, 0.375rem)) scale(0.97);
  }
  60% {
    opacity: 1;
  }
  80% {
    transform: translateY(-1px) scale(1.005);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/*
 * Fluid exit: fade + shrink with accelerated easing.
 */
.bao-fluid-exit {
  animation: bao-fluid-exit-out var(--duration-normal, 200ms) var(--easing-exit, cubic-bezier(0.3, 0, 0.8, 0.15)) both;
}
@keyframes bao-fluid-exit-out {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  100% {
    opacity: 0;
    transform: translateY(calc(var(--entrance-offset, 0.375rem) * -1)) scale(0.97);
  }
}

/*
 * Depth-tiered surfaces: deeper surfaces carry a larger blur TOKEN at
 * rest (consumed by their material class), and share one compositor-only
 * transition channel. Canon §7.4 — blur radius itself never transitions.
 */
.bao-fluid-depth-1 { --bao-depth-blur: var(--liquid-blur-small); }
.bao-fluid-depth-2 { --bao-depth-blur: var(--liquid-blur-regular); }
.bao-fluid-depth-3 { --bao-depth-blur: var(--liquid-blur-nav); }

.bao-fluid-depth-1,
.bao-fluid-depth-2,
.bao-fluid-depth-3 {
  transition:
    opacity var(--duration-normal, 200ms) var(--easing-standard),
    transform var(--duration-normal, 200ms) var(--easing-standard);
}

/*
 * Elevated hover shadow is owned solely by tokens-glass-base-variants.ts
 * (.bao-surface-glass-strong — single owner, S42). The dead
 * --glass-highlight-intensity hover block was deleted: it was never
 * consumed by any gradient and cannot transition without @property.
 */

/*
 * Touch feedback: tap states compress the glass surface.
 * Breakpoint-tiered scale lives in tokens-glass-fluid-responsive.ts
 * (single owner); this block owns only the press transition timing.
 */
.bao-surface-glass-regular:active,
.bao-surface-glass-strong:active,
.bao-surface-glass-clear:active {
  transition: transform var(--duration-press) var(--easing-standard);
}

/*
 * Stagger orchestration: child elements enter sequentially.
 * Each child delays by --stagger-step (30ms) * its index.
 */
.bao-fluid-stagger > *:nth-child(1) { animation-delay: calc(var(--stagger-step, 30ms) * 0); }
.bao-fluid-stagger > *:nth-child(2) { animation-delay: calc(var(--stagger-step, 30ms) * 1); }
.bao-fluid-stagger > *:nth-child(3) { animation-delay: calc(var(--stagger-step, 30ms) * 2); }
.bao-fluid-stagger > *:nth-child(4) { animation-delay: calc(var(--stagger-step, 30ms) * 3); }
.bao-fluid-stagger > *:nth-child(5) { animation-delay: calc(var(--stagger-step, 30ms) * 4); }
.bao-fluid-stagger > *:nth-child(6) { animation-delay: calc(var(--stagger-step, 30ms) * 5); }
.bao-fluid-stagger > *:nth-child(7) { animation-delay: calc(var(--stagger-step, 30ms) * 6); }
.bao-fluid-stagger > *:nth-child(8) { animation-delay: calc(var(--stagger-step, 30ms) * 7); }

.bao-fluid-stagger > * {
  animation: bao-fluid-enter-in var(--duration-slow, 320ms) var(--easing-entrance) both;
}

/*
 * Touch ripple keyframe (used by app-shell-fluid-interactions.ts).
 */
@keyframes bao-ripple-expand {
  0% {
    transform: scale(0);
    opacity: 0.15;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}
.bao-touch-ripple {
  position: absolute;
  border-radius: var(--radius-full, 9999px);
  pointer-events: none;
  background: currentColor;
  animation: bao-ripple-expand var(--duration-normal, 200ms) var(--easing-standard) forwards;
}

/*
 * Modal/dialog entrance: spring-physics with backdrop fade.
 */
dialog[open].bao-fluid-dialog {
  animation: bao-dialog-enter var(--duration-slow, 320ms) var(--easing-entrance) both;
}
dialog[open].bao-fluid-dialog::backdrop {
  background: var(--glass-scrim);
  animation: bao-backdrop-enter var(--duration-normal, 200ms) var(--easing-standard) both;
}
@keyframes bao-dialog-enter {
  0% {
    opacity: 0;
    transform: translateY(1rem) scale(0.95);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
@keyframes bao-backdrop-enter {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

/*
 * Sheet/drawer slide: slides from edge with spring easing.
 */
.bao-fluid-sheet-enter {
  animation: bao-sheet-slide-in var(--duration-slow, 320ms) var(--easing-entrance) both;
}
@keyframes bao-sheet-slide-in {
  0% {
    transform: translateX(100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}
.bao-fluid-sheet-exit {
  animation: bao-sheet-slide-out var(--duration-normal, 200ms) var(--easing-exit) both;
}
@keyframes bao-sheet-slide-out {
  0% {
    transform: translateX(0);
    opacity: 1;
  }
  100% {
    transform: translateX(100%);
    opacity: 0;
  }
}

/*
 * Sidebar collapse/expand: smooth width transition.
 */
.bao-shell-sidebar {
  transition: width var(--duration-slow, 320ms) var(--easing-glass, cubic-bezier(0.32, 0.72, 0, 1));
}

/*
 * Card hover lift: subtle elevation increase on hover.
 * Gated to fine pointers only (S41/S75/S76) — touch devices use :active.
 */
@media (hover: hover) and (pointer: fine) {
  .card.bao-motion-transition-colors:hover {
    box-shadow: var(--shadow-md);
    transition: box-shadow var(--duration-normal, 200ms) var(--easing-standard);
  }

  /*
   * Table row hover: background highlight with smooth transition.
   */
  .table tbody tr {
    transition: background-color var(--duration-fast, 120ms) var(--easing-standard);
  }
  .table tbody tr:hover {
    background-color: var(--bg-surface-hover);
  }
}

/*
 * Dock item active indicator: smooth scale + color transition.
 */
.bao-mobile-dock-item {
  transition:
    transform var(--duration-fast, 120ms) var(--easing-standard),
    color var(--duration-fast, 120ms) var(--easing-standard);
}

/* Product override uses scale(1); media block owns the sole none (S62). */



/*
 * Capture page fixes: prevent tooltip wrapper occlusion and shutter/session collision.
 */
/* Fix tooltip wrapper occlusion on capture action buttons (Evidence/Reports) */
[data-capture-action-bar] .tooltip {
  pointer-events: none;
}
[data-capture-action-bar] .tooltip > .btn,
[data-capture-action-bar] .tooltip > a.btn {
  pointer-events: auto;
}
/* Ensure tooltip wrapper doesn't extend beyond button bounds on tablet */
@media (min-width: 48rem) and (max-width: 64rem) {
  [data-capture-action-bar] .tooltip.inline-flex {
    display: inline-block;
    width: auto;
    height: auto;
  }
  /* Fix: toolbar labels in btn-square must not extend button bounds */
  [data-capture-action-bar] .btn-square .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   RESPONSIVE BREAKPOINTS — mobile-first fluid layout
   ═══════════════════════════════════════════════════════════════════ */

/* Mobile: stack layouts, full-width components */
@media (max-width: 47.99rem) {
  .bao-fluid-stagger > * {
    animation-delay: var(--duration-instant);
  }
  .bao-dashboard-widget-dragging {
    transform: scale(0.95);
  }
}

/* Desktop: enhanced animations, wider layouts */
@media (min-width: 64rem) {
  .bao-fluid-enter {
    animation-duration: var(--duration-slow, 400ms);
  }
  .bao-surface-glass-strong:hover {
    transform: translateY(-2px);
    transition: transform var(--duration-normal, 200ms) var(--easing-standard);
  }
}

/* ═══════════════════════════════════════════════════════════════════
   ACCESSIBILITY — reduced motion support
   ═══════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .bao-fluid-enter,
  .bao-fluid-exit,
  .bao-fluid-stagger > *,
  .bao-dashboard-widget-dragging,
  .bao-surface-glass-strong:hover,
  .bao-surface-glass-strong:active {
    animation: none;
    transition: none;
    /* scale(1) — S62: avoid a second none while dock-item selector stays sticky. */
    transform: scale(1);
  }
  .bao-touch-ripple {
    display: none;
  }
}


/* ═══════════════════════════════════════════════════════════════════
   RESPONSIVE — breakpoint-tiered glass :active spring scale
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 47.99rem) {/* Phone: reduce spring scale to avoid layout shift on small screens */
  .bao-surface-glass-regular:active,
.bao-surface-glass-strong:active {
    transform: scale(0.97);
  }
  /* Phone: dock items get larger touch targets */
  .bao-mobile-dock-item {
    min-height: var(--touch-target-min, 2.75rem);
    min-width: var(--touch-target-min, 2.75rem);
  }
}

@media (min-width: 48rem) and (max-width: 64rem) {/* Tablet: moderate spring scale */
  .bao-surface-glass-regular:active,
.bao-surface-glass-strong:active {
    transform: scale(0.98);
  }
}

@media (min-width: 64rem) {/* Desktop: subtle spring scale */
  .bao-surface-glass-regular:active,
.bao-surface-glass-strong:active {
    transform: scale(0.985);
  }
}


/* ═══════════════════════════════════════════════════════════════════
   REDUCED MOTION — disable all fluid animations
   Specificity: scoped to each class + media query (no cascade force).
   ═══════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .bao-fluid-enter,
  .bao-fluid-exit,
  .bao-fluid-stagger > * {
    animation: none;
    transition: none;
    /* scale(1) — S62: avoid a second none while dock-item selector stays sticky. */
    transform: scale(1);
  }
  dialog[open].bao-fluid-dialog {
    animation: none;
  }
  .bao-fluid-sheet-enter,
  .bao-fluid-sheet-exit {
    animation: none;
    transition: none;
  }
  .bao-surface-glass-regular:active,
  .bao-surface-glass-strong:active,
  .bao-surface-glass-clear:active {
    transform: none;
  }
}


/* Touch target minimum sizing (44x44px per Apple HIG) */
.min-h-touch-target {
  min-height: var(--touch-target-min, 2.75rem);
}

/*
 * Drag handles only when customize mode is on. Default browse must not
 * expose grab affordances on every metric card (Apple HIG: chrome vs content).
 */
[data-dashboard-layout-root]:not([data-dashboard-editing="true"]) [data-dashboard-drag-handle] {
  cursor: default;
  pointer-events: none;
}
/*
 * touch-action:none is required for the coarse-pointer drag path: without it
 * the browser claims the gesture for scrolling and pointermove never reaches
 * handleDragStart. Scoped to customize mode so browse-mode scrolling over a
 * widget is untouched.
 */
[data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-drag-handle] {
  cursor: grab;
  touch-action: none;
  transition: background-color var(--duration-fast, 120ms) var(--easing-standard);
}

@media (hover: hover) and (pointer: fine) {
  [data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-drag-handle]:hover {
    background-color: var(--bg-surface-hover);
    border-radius: var(--radius-sm, 0.375rem);
  }
}

/* Cursor-grabbing on :active comes from the Tailwind 'active:cursor-grabbing'
   utility applied in app-shell-dashboard-drag.ts — no duplicated rule here. */
[data-dashboard-layout-root][data-dashboard-editing="true"] [data-dashboard-drag-handle]:active {
  background-color: var(--bg-surface);
}/* Widget dragging state — geometry from setHostCssVars (--bao-drag-*). */
.bao-dashboard-widget-dragging,
[data-dashboard-widget][data-bao-dragging="true"] {
  position: fixed;
  left: var(--bao-drag-left, 0);
  top: var(--bao-drag-top, 0);
  width: var(--bao-drag-width, auto);
  transition: none;
  pointer-events: none;
}/* Drag placeholder — size from setHostCssVars (--bao-placeholder-*).
   The dashed primary outline lives here,
not on the element: the browser bundle
   used to append rounded-box,
border-2,
border-dashed,
border-primary/50 and
   bg-primary/5 to this class in app-shell-dashboard-drag-geometry.ts,
so half the
   placeholder's appearance was decided in TypeScript where no canon gate reads
   it. */
.bao-dashboard-widget-placeholder {
  width: var(--bao-placeholder-width, 100%);
  height: var(--bao-placeholder-height, 8rem);
  border-radius: var(--radius-box, 1rem);
  border: 2px dashed color-mix(in oklab, var(--color-primary) 50%, transparent);
  background-color: color-mix(in oklab, var(--color-primary) 5%, transparent);
  transition: box-shadow var(--duration-normal, 200ms) var(--easing-standard),
    opacity var(--duration-normal, 200ms) var(--easing-standard),
    transform var(--duration-normal, 200ms) var(--easing-standard);
}

/* ═══════════════════════════════════════════════════════════════════
   CAPTURE PAGE LAYOUT FIXES
   ═══════════════════════════════════════════════════════════════════ */

/* Fix shutter chrome z-index on tablet to avoid occluding session controls */
@media (min-width: 48rem) and (max-width: 64rem) {
  [data-capture-shutter-chrome] {
    z-index: var(--z-canvas-controls, 30);
  }

  [data-session-controls] {
    z-index: var(--z-canvas-controls, 30);
    position: relative;
  }
}

/* Ensure Evidence/Reports buttons are not occluded */
[data-capture-actions] .btn {
  position: relative;
  z-index: var(--z-canvas-actions, 40);
}


/* ═══════════════════════════════════════════════════════════════════
   VIEW TRANSITIONS — Apple HIG page navigation
   ═══════════════════════════════════════════════════════════════════ */

/*
 * Same-document transitions only (htmx transition:true swaps).
 * No @view-transition navigation opt-in: cross-document transitions
 * permanently freeze the post-login document in Chromium (see module doc).
 */

::view-transition-old(root) {
  animation: bao-view-transition-fade-out var(--duration-normal, 200ms) var(--easing-exit);
}

::view-transition-new(root) {
  animation: bao-view-transition-fade-in var(--duration-slow, 320ms) var(--easing-entrance);
}

@keyframes bao-view-transition-fade-out {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.98);
  }
}

@keyframes bao-view-transition-fade-in {
  from {
    opacity: 0;
    transform: scale(1.02);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/*
 * Shared element transitions for navigation items
 */
.bao-nav-item {
  view-transition-name: var(--vt-nav-item, none);
}

[data-route-id="dashboard"] {
  --vt-nav-item: nav-dashboard;
}

[data-route-id="cases"] {
  --vt-nav-item: nav-cases;
}

[data-route-id="capture"] {
  --vt-nav-item: nav-capture;
}

[data-route-id="editor"] {
  --vt-nav-item: nav-editor;
}

[data-route-id="models"] {
  --vt-nav-item: nav-models;
}::view-transition-group(nav-dashboard),
::view-transition-group(nav-cases),
::view-transition-group(nav-capture),
::view-transition-group(nav-editor),
::view-transition-group(nav-models) {
  animation-duration: var(--duration-normal, 200ms);
  animation-timing-function: var(--easing-standard);
}

/*
 * Page header transitions
 */
[data-primitive="PageHeader"] {
  view-transition-name: page-header;
}

::view-transition-group(page-header) {
  animation-duration: var(--duration-slow, 320ms);
  animation-timing-function: var(--easing-entrance);
}

/*
 * Dashboard widget transitions
 */
[data-dashboard-widget] {
  view-transition-name: var(--vt-widget, none);
}

[data-widget="cases"] {
  --vt-widget: widget-cases;
}

[data-widget="capture"] {
  --vt-widget: widget-capture;
}

[data-widget="models"] {
  --vt-widget: widget-models;
}

[data-widget="ai-analysis"] {
  --vt-widget: widget-ai;
}::view-transition-group(widget-cases),
::view-transition-group(widget-capture),
::view-transition-group(widget-models),
::view-transition-group(widget-ai) {
  animation-duration: var(--duration-slow, 320ms);
  animation-timing-function: var(--easing-entrance);
}

/*
 * Reduced motion: disable all view transitions
 */
@media (prefers-reduced-motion: reduce) {::view-transition-old(root),
::view-transition-new(root),
::view-transition-group(*) {
    animation-duration: var(--reduced-motion-min-duration);
  }
}

/*
 * Fallback for browsers without View Transitions API
 */
@supports not (view-transition-name: none) {
  [data-page] {
    animation: bao-page-fallback-enter var(--duration-slow, 320ms) var(--easing-entrance);
  }

  @keyframes bao-page-fallback-enter {
    from {
      opacity: 0;
      transform: translateY(var(--entrance-offset, 0.375rem));
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
}



/* ═══════════════════════════════════════════════════════════════════
   RUNTIME INTERACTIONS — JS-driven CSS custom properties + data attrs
   ═══════════════════════════════════════════════════════════════════ */

/* ─── Card 3D Tilt (driven by app-shell-card-interactions.ts) ───
   JS sets --card-tilt-x/--card-tilt-y via setHostCssVars and gates
   with data-bao-tilt (never style=). CSS owns the transform. */

.card.card-border,
[data-dashboard-widget] {
  --card-tilt-x: 0deg;
  --card-tilt-y: 0deg;
  perspective: 800px;
  transform-style: preserve-3d;
}

@media (hover: hover) and (pointer: fine) {
  .card.card-border[data-bao-tilt="true"],
  [data-dashboard-widget][data-bao-tilt="true"] {
    transform: perspective(800px) rotateX(var(--card-tilt-x)) rotateY(var(--card-tilt-y)) translateY(-2px);
    transition: transform var(--duration-fast, 120ms) var(--easing-standard);
  }
}

@media (prefers-reduced-motion: reduce) {
  .card.card-border,
  [data-dashboard-widget] {
    perspective: none;
    transform-style: flat;
  }
}

/* ─── Touch Press Feedback (driven by app-shell-touch-feedback.ts) ───
   JS sets data-bao-press="true" on touchstart, removes on touchend.
   CSS applies instant visual feedback — zero-latency feel. */

.bao-touch-target[data-bao-press="true"],
.btn[data-bao-press="true"],
a[href][data-bao-press="true"],
[role="button"][data-bao-press="true"],
[data-action][data-bao-press="true"],
summary[data-bao-press="true"] {
  transform: scale(0.97);
  opacity: var(--opacity-strong, 0.85);
  transition: none;
}

/* OS + product reduced-motion: no press spring scale (dock/topbar haptic). */
@media (prefers-reduced-motion: reduce) {
  .bao-touch-target[data-bao-press="true"],
  .btn[data-bao-press="true"],
  a[href][data-bao-press="true"],
  [role="button"][data-bao-press="true"],
  [data-action][data-bao-press="true"],
  summary[data-bao-press="true"] {
    transform: none;
    opacity: 1;
  }
}
html[data-reduced-motion="true"] .bao-touch-target[data-bao-press="true"],
html[data-reduced-motion="true"] .btn[data-bao-press="true"],
html[data-reduced-motion="true"] a[href][data-bao-press="true"],
html[data-reduced-motion="true"] [role="button"][data-bao-press="true"],
html[data-reduced-motion="true"] [data-action][data-bao-press="true"],
html[data-reduced-motion="true"] summary[data-bao-press="true"] {
  transform: none;
  opacity: 1;
}

/* ─── Toast Swipe Tracking (driven by app-shell-toast-swipe.ts) ───
   JS writes --bao-swipe-dx and --bao-swipe-alpha via setProperty
   during touchmove. CSS reads them for live transform + opacity. */

#toast-region > [data-bao-swiping="true"] {
  transform: translateX(var(--bao-swipe-dx, 0px));
  opacity: var(--bao-swipe-alpha, 1);
  transition: none;
}

#toast-region > [data-toast-dismissing] {
  transform: translateX(100%);
  opacity: 0;
  transition: transform var(--duration-normal, 200ms) var(--easing-standard),
              opacity var(--duration-normal, 200ms) var(--easing-standard);
}

@media (min-width: 48rem) {
  #toast-region > [data-toast-dismissing] {
    transform: translateX(120%);
  }
}

/* ─── Toast Entrance Animation ─── */

@keyframes bao-toast-enter {
  from {
    opacity: 0;
    transform: translateY(var(--space-section, 0.75rem)) scale(0.96);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

#toast-region > *:not([data-toast-dismissing]):not([data-bao-swiping]) {
  animation: bao-toast-enter var(--duration-slow, 320ms) var(--easing-entrance) both;
}

/* ─── Toast Region Top-Layer Promotion (driven by app-shell-toast.ts) ───
   JS promotes #toast-region via popover=manual (shown persistently) so
   error toasts paint — and stay interactive — above open native dialogs
   and the mobile drawer. The popover UA box (centered inset, border,
   padding, canvas background) must not fight the daisyUI .toast geometry,
   which already owns placement; neutralize it here. */
#toast-region[popover] {
  inset: auto;
  inset-inline-start: 50%;
  inset-inline-end: auto;
  --toast-x: -50%;
  inset-block-start: calc(var(--chrome-min-height) + var(--space-section) + var(--space-inline));
  inset-block-end: auto;
  inline-size: min(var(--toast-max-inline-size), calc(100vw - (2 * var(--space-inline))));
  max-inline-size: min(var(--toast-max-inline-size), calc(100vw - (2 * var(--space-inline))));
  margin: 0;
  padding: 0;
  border: none;
  overflow: visible;
  background: transparent;
  color: inherit;
}

#toast-region[popover] > * {
  min-inline-size: 0;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

/* ─── Selection Highlight ─── */

::selection {
  background-color: color-mix(in oklab, var(--accent-primary, var(--color-primary)) 20%, transparent);
  color: var(--text-primary, var(--color-base-content));
}

/* ─── Spring Hover Scale (driven by app-shell-fluid-interactions.ts) ───
   JS sets data-bao-hover="true" on pointerenter, removes on pointerleave.
   Spring easing overshoots then settles — Apple HIG material feel. */

/* Cards own transform via tilt/lift — spring scale excludes them. */
@media (hover: hover) and (pointer: fine) {
  [data-primitive="Button"][data-bao-hover="true"],
  .bao-data-panel[data-bao-hover="true"] {
    transform: scale(1.02);
    transition: transform var(--duration-slow) var(--easing-glass);
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-bao-hover="true"] {
    transform: none;
    transition: none;
  }
}
html[data-reduced-motion="true"] [data-bao-hover="true"] {
  transform: none;
  transition: none;
}

/* ─── Mobile Dock Snap (driven by app-shell-fluid-interactions.ts) ─── */

#mobile-dock[data-bao-dock-snap="true"] {
  scroll-snap-type: x mandatory;
}

#mobile-dock[data-bao-dock-snap="true"] .bao-mobile-dock-item {
  scroll-snap-align: center;
}

/* ─── Pull-to-Refresh Indicator (driven by app-shell-swipe-navigation.ts) ───
   JS publishes --pull-distance via setHostCssVars (data-bao-var-host),
   never inline style attrs. CSS pseudo-element renders the indicator. */

#app-main[data-bao-var-host]::before {
  content: "";
  position: fixed;
  top: 0;
  left: 50%;
  width: 2rem;
  height: 2rem;
  transform: translateX(-50%) translateY(var(--pull-distance, 0px));
  border-radius: var(--radius-full, 9999px);
  background: var(--bg-surface, var(--color-base-200));
  border: 1px solid var(--border-base, var(--color-base-300));
  box-shadow: var(--shadow-md);
  z-index: var(--z-toast, 1400);
  pointer-events: none;
}

/* ─── Skeleton Shimmer Enhancement ─── */

@keyframes bao-skeleton-sweep {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.skeleton-shimmer {
  background-color: var(--bg-surface-hover, var(--color-base-200));
  background-image: linear-gradient(
    90deg,
    var(--bg-surface, var(--color-base-200)) 25%,
    color-mix(in oklab, var(--bg-surface-hover, var(--color-base-300)) 60%, transparent) 50%,
    var(--bg-surface, var(--color-base-200)) 75%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: bao-skeleton-sweep var(--duration-skeleton) var(--easing-standard) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .skeleton-shimmer {
    animation: none;
    background-image: none;
    background-color: var(--bg-surface, var(--color-base-200));
  }
}

/* ─── Modal Glass Enhancement + Entrance ─── */

@keyframes bao-modal-enter {
  from {
    opacity: 0;
    transform: translateY(var(--space-section, 0.75rem)) scale(0.97);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

dialog.modal[open] .bao-modal-box-shell,
dialog.modal.modal-open .bao-modal-box-shell {
  animation: bao-modal-enter var(--duration-slow, 320ms) var(--easing-entrance) both;
  background: color-mix(in oklab, var(--bg-panel, var(--color-base-100)) calc(var(--liquid-fill-blocking, 0.88) * 100%), transparent);
  backdrop-filter: blur(var(--liquid-blur-nav, 32px)) saturate(var(--liquid-saturate-strong, 1.8));
  -webkit-backdrop-filter: blur(var(--liquid-blur-nav, 32px)) saturate(var(--liquid-saturate-strong, 1.8));
  box-shadow: var(--shadow-glass-floating);
  border-color: var(--liquid-border-contrast);
}

@media (prefers-reduced-motion: reduce) {
  dialog.modal[open] .bao-modal-box-shell,
  dialog.modal.modal-open .bao-modal-box-shell {
    animation: none;
  }
}

@media (prefers-reduced-transparency: reduce) {
  dialog.modal[open] .bao-modal-box-shell,
  dialog.modal.modal-open .bao-modal-box-shell {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--bg-panel, var(--color-base-100));
    box-shadow: var(--shadow-lg);
  }
}


/* ═══════════════════════════════════════════════════════════════════
   UX ENHANCEMENTS — hover, focus, mobile interactions
   ═══════════════════════════════════════════════════════════════════ */

/* ─── Glass Hover Illumination (design.pdf §2.7) ───
   A subtle radial glow near the pointer on glass surfaces. JS sets
   --glass-pointer-x/y via pointermove; CSS composes the glow as an
   additional background layer on the existing ::after inner illumination.
   Falls back gracefully when JS is unavailable (no glow, no break). */

@media (hover: hover) and (pointer: fine) {
  :is(.bao-surface-glass-regular, .bao-surface-glass-clear) {
    --glass-pointer-x: 50%;
    --glass-pointer-y: 50%;
  }
  .bao-surface-glass-regular:hover::after,
  .bao-surface-glass-clear:hover::after {
    background:
      radial-gradient(
        600px circle at var(--glass-pointer-x) var(--glass-pointer-y),
        var(--glass-illumination-strong),
        transparent 40%
      ),
      radial-gradient(
        ellipse 120% 80% at 20% 0%,
        var(--glass-illumination-strong) 0%,
        var(--glass-illumination-soft) 50%,
        var(--glass-highlight-clear) 80%
      );
  }
}

@media (prefers-reduced-motion: reduce) {
  .bao-surface-glass-regular:hover::after,
  .bao-surface-glass-clear:hover::after {
    background: radial-gradient(
      ellipse 120% 80% at 20% 0%,
      var(--glass-illumination-strong) 0%,
      var(--glass-illumination-soft) 50%,
      var(--glass-highlight-clear) 80%
    );
  }
}

/* ─── Focus Ring Pulse (design.pdf §6) ───
   Subtle pulse on focus-visible to draw attention without being distracting.
   Only triggers on keyboard focus (focus-visible), not mouse clicks. */

@keyframes bao-focus-pulse {
  0%, 100% { outline-offset: var(--focus-ring-offset, 2px); }
  50% { outline-offset: calc(var(--focus-ring-offset, 2px) + 1px); }
}

:focus-visible {
  outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, var(--color-primary));
  outline-offset: var(--focus-ring-offset, 2px);
  animation: bao-focus-pulse var(--duration-focus-pulse) var(--easing-standard) 1;
}

@media (prefers-reduced-motion: reduce) {
  :focus-visible {
    animation: none;
  }
}

/* ─── Sidebar Hover-Expand — DELETED (second width owner) ───
   `.bao-sidebar-collapsed:hover { width: 16rem }` widened the NAV while the
   grid track on .bao-shell-drawer stayed at --sidebar-collapsed, and
   .drawer-side clips (overflow:hidden). Measured at 1440px: nav 256px inside
   a 56px track, elementFromPoint(150, item) landed on page content, and the
   label spans stayed display:none because the drawer kept
   .bao-shell-rail-collapsed — so the hover revealed no labels at all. It did
   produce two artefacts: a 200px dead hit area, and .bao-nav-item.tooltip
   bubbles laid out against a 238px-wide item whose body fell outside the clip,
   leaving a stray arrow at the rail edge. The drawer grid track is the ONE
   owner of rail width (tokens-glass-shell-drawer-part-b) and the peek
   controller is the one owner of hover expansion — it moves the panel out of
   the track first, so nothing clips. The [data-sidebar-*-only] display swaps
   keep their real owners: app-shell-rails.ts (hidden class, per state) plus
   the peek/pinned/checked rules in tokens-glass-shell-drawer-part-1. */

/* ─── Smooth Theme Transition (light↔dark crossfade) ───
   Crossfade between themes when data-theme changes. Only applies to
   color properties — not layout/transform (would cause reflow). */

html {
  transition: background-color var(--duration-slow, 320ms) var(--easing-standard),
              color var(--duration-slow, 320ms) var(--easing-standard);
}

@media (prefers-reduced-motion: reduce) {
  html {
    transition: none;
  }
}

/* ─── Empty State Entrance Animation (transform-only stagger) ───
   Empty states enter with a subtle upward motion for perceived performance.
   Children stagger by --stagger-step (30ms default from tokens.bao).
   WebView 101 law (tokens-glass-a11y.ts): entrance motion is
   transform-only — opacity-from-0 can stick and paint voids over real
   content, so readability never depends on an animation completing. */

@keyframes bao-empty-enter {
  from {
    transform: translateY(var(--entrance-offset, 0.375rem));
  }
  to {
    transform: none;
  }
}

[data-state="empty"] [data-state-content],
[data-primitive="EmptyState"] {
  animation: bao-empty-enter var(--duration-slow, 320ms) var(--easing-entrance) both;
}

@media (prefers-reduced-motion: reduce) {
  [data-state="empty"] [data-state-content],
  [data-primitive="EmptyState"] {
    animation: none;
  }
}

/* ─── Context Strip Momentum Scroll + Snap ───
   Horizontal scroll with momentum and snap-to-chip alignment.
   Only on devices that can scroll horizontally (overflow-x-auto host). */

.bao-shell-context-strip {
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}

.bao-shell-context-chip {
  scroll-snap-align: start;
}

/* ─── Card Hover Lift (design.pdf §5.3) ───
   Clickable cards lift on hover with increased shadow and slight translateY.
   Only on fine pointers — touch devices use press feedback instead.
   Dashboard KPI tiles with a cover action share the same affordance so the
   whole card reads as the control (design.md §5.4). */

@media (hover: hover) and (pointer: fine) {
  [data-primitive="Card"][data-clickable="true"],
  .card.card-border[data-clickable="true"],
  [data-dashboard-widget]:has(.bao-dashboard-widget-action),
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action) {
    transition: transform var(--duration-fast, 120ms) var(--easing-standard),
                box-shadow var(--duration-fast, 120ms) var(--easing-standard),
                border-color var(--duration-fast, 120ms) var(--easing-standard);
  }
  [data-primitive="Card"][data-clickable="true"]:hover,
  .card.card-border[data-clickable="true"]:hover,
  [data-dashboard-widget]:has(.bao-dashboard-widget-action):hover,
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action):hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-glass-elevated, var(--shadow-lg));
    border-color: color-mix(in oklab, var(--text-primary) 18%, var(--border-base, var(--color-base-300)));
  }
  [data-primitive="Card"][data-clickable="true"]:active,
  .card.card-border[data-clickable="true"]:active,
  [data-dashboard-widget]:has(.bao-dashboard-widget-action):active,
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action):active {
    transform: translateY(0) scale(0.99);
    box-shadow: var(--shadow-sm);
  }
}

/* Coarse pointer: press scale on whole-card KPI tiles (Apple HIG feedback). */
@media (hover: none), (pointer: coarse) {
  [data-dashboard-widget]:has(.bao-dashboard-widget-action):active,
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action):active,
  [data-primitive="Card"][data-clickable="true"]:active,
  .card.card-border[data-clickable="true"]:active {
    transform: scale(0.985);
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-primitive="Card"][data-clickable="true"],
  .card.card-border[data-clickable="true"],
  [data-dashboard-widget]:has(.bao-dashboard-widget-action),
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action) {
    transition: none;
  }
  [data-primitive="Card"][data-clickable="true"]:hover,
  .card.card-border[data-clickable="true"]:hover,
  [data-dashboard-widget]:has(.bao-dashboard-widget-action):hover,
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action):hover,
  [data-dashboard-widget]:has(.bao-dashboard-widget-action):active,
  [data-primitive="DashboardWidget"]:has(.bao-dashboard-widget-action):active {
    transform: none;
  }
}

/* ─── Dialog Drag Handle (mobile bottom sheets) ───
   Visual indicator on bottom-sheet dialogs for drag-to-dismiss affordance.
   Only visible on mobile (below 48rem) where bottom sheets are used. */

@media (max-width: 47.99rem) {
  dialog.modal.modal-bottom .modal-box::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 0.25rem;
    border-radius: var(--radius-full, 9999px);
    background: color-mix(in oklab, var(--text-tertiary) 30%, transparent);
    margin: var(--space-micro) auto var(--space-inline);
  }
}

/* Filter sheet, row-swipe, dashboard drag-reorder, fuzzy-search, and the
   bao-sheet-enter keyframes are owned solely by
   tokens-glass-runtime-interactions-mobile.ts (single owner — DRY, S42).
   The scroll-to-top button was deleted: zero markup/JS consumers exist. */


/* ─── Scrollbar Refinement (Webkit) ─── */

::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: color-mix(in oklab, var(--text-tertiary, var(--color-base-content)) 30%, transparent);
  border-radius: var(--radius-full, 9999px);
}

[data-theme="capture-dx-dark"] ::-webkit-scrollbar-thumb {
  background: color-mix(in oklab, var(--text-tertiary, var(--color-base-content)) 25%, transparent);
}

/* Hybrid hover (imp-t04): thumb emphasis only where a pointer can hover —
   overlay scrollbars on touch never receive a hoverable thumb. */
@media (hover: hover) and (pointer: fine) {
  ::-webkit-scrollbar-thumb:hover {
    background: color-mix(in oklab, var(--text-secondary, var(--color-base-content)) 50%, transparent);
  }

  [data-theme="capture-dx-dark"] ::-webkit-scrollbar-thumb:hover {
    background: color-mix(in oklab, var(--text-secondary, var(--color-base-content)) 40%, transparent);
  }
}



/* ─── Filter Bar Mobile Bottom Sheet ───
   On mobile (<48rem), filter overflow transforms into a bottom sheet
   pattern. The overflow summary button triggers a details/summary
   disclosure that slides up from the bottom. */

@media (max-width: 47.99rem) {
  [data-primitive="FilterBar"] details.bao-filter-overflow {
    position: relative;
  }
  [data-primitive="FilterBar"] details.bao-filter-overflow[open] > summary ~ * {
    animation: bao-sheet-enter var(--duration-slow, 320ms) var(--easing-entrance) both;
  }
  [data-primitive="FilterBar"] details.bao-filter-overflow > summary ~ * {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: var(--z-overlay, 1200);
    max-height: 70dvh;
    overflow-y: auto;
    background: var(--bg-panel, var(--color-base-100));
    border-top: 1px solid var(--border-base, var(--color-base-300));
    border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
    padding: var(--space-section);
    box-shadow: var(--shadow-glass-floating);
  }
}

@keyframes bao-sheet-enter {
  from {
    opacity: 0;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-primitive="FilterBar"] details.bao-filter-overflow[open] > summary ~ * {
    animation: none;
  }
}

/* ─── Data Table Row Swipe Actions (mobile) ───
   Swipe left on a table row to reveal action buttons.
   JS (app-shell-table-row-swipe) sets --row-swipe-dx via setHostCssVars.
   Only on coarse pointers (touch devices). Card-stack rows keep actions
   visible via opacity:1 rules; swipe still works for denser table mode. */

@media (hover: none) and (pointer: coarse) {
  [data-primitive="DataTable"] tbody tr.bao-table-responsive-row,
  [data-primitive="DataTable"] tbody tr.bao-hover-row {
    position: relative;
    touch-action: pan-y;
  }
  [data-primitive="DataTable"] tbody tr[data-bao-swiping="true"] {
    transform: translateX(var(--row-swipe-dx, 0px));
    transition: none;
    z-index: 1;
  }
  [data-primitive="DataTable"] tbody tr[data-bao-swipe-open="true"]:not([data-bao-swiping]) {
    transform: translateX(var(--row-swipe-dx, -88px));
    transition: transform var(--duration-fast, 120ms) var(--easing-standard);
  }
  [data-primitive="DataTable"] tbody tr:not([data-bao-swiping]):not([data-bao-swipe-open]) {
    transition: transform var(--duration-fast, 120ms) var(--easing-standard);
  }
  /* Keep action cluster tappable while the row is translated. */
  [data-primitive="DataTable"] tbody tr[data-bao-swipe-open="true"] .bao-table-pinned-action,
  [data-primitive="DataTable"] tbody tr[data-bao-swiping="true"] .bao-table-pinned-action {
    position: relative;
    z-index: 2;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-primitive="DataTable"] tbody tr {
    transition: none;
  }
}

/* ─── Sheet Back Gesture (mobile) ───
   iOS-style swipe right from the left edge dismisses the topmost sheet.
   JS (app-shell-sheet-gesture) publishes --sheet-gesture-dx on the dialog
   via setHostCssVars and flips data-bao-sheet-gesture: "drag" follows the
   finger with no transition, "settle" transitions back to 0 on cancel.
   Sheet wrappers stay fixed (backdrop must not move); the panel translates.
   Reduced-motion never receives the attribute — dismiss is instant. */

@media (hover: none) and (pointer: coarse) and (max-width: 47.99rem) {
  dialog[data-bao-sheet-gesture] {
    transform: translateX(var(--sheet-gesture-dx, 0px));
  }
  dialog[data-bao-sheet-gesture="drag"] {
    transition: none;
  }
  dialog[data-bao-sheet-gesture="settle"] {
    transition: transform var(--duration-fast, 120ms) var(--easing-standard);
  }
  dialog[data-primitive="Sheet"][data-bao-sheet-gesture] {
    transform: none;
  }
  dialog[data-primitive="Sheet"][data-bao-sheet-gesture] .bao-sheet-panel {
    transform: translateX(var(--sheet-gesture-dx, 0px));
  }
  dialog[data-primitive="Sheet"][data-bao-sheet-gesture="drag"] .bao-sheet-panel {
    transition: none;
  }
  dialog[data-primitive="Sheet"][data-bao-sheet-gesture="settle"] .bao-sheet-panel {
    transition: transform var(--duration-fast, 120ms) var(--easing-standard);
  }
}

/* ─── Dashboard Widget Drag-to-Reorder ───
   Widgets get a drag handle on hover (desktop) and long-press (mobile).
   JS sets data-bao-dragging="true" during drag; CSS handles visual state. */

[data-dashboard-widget][data-bao-dragging="true"] {
  opacity: var(--opacity-strong, 0.85);
  box-shadow: var(--shadow-glass-floating);
  transform: scale(1.02);
  z-index: var(--z-overlay, 1200);
  cursor: grabbing;
}

[data-dashboard-widget][data-bao-drag-over="true"] {
  outline: 2px dashed var(--accent-primary, var(--color-primary));
  outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
  [data-dashboard-widget][data-bao-dragging="true"] {
    transform: none;
    transition: none;
  }
}

/* ─── Command Palette Fuzzy Search ───
   Enhanced search results with highlighted matching characters.
   JS sets data-bao-match="true" on matching characters; CSS styles them. */

[data-bao-match="true"] {
  font-weight: var(--font-weight-semibold, 600);
  color: var(--accent-primary, var(--color-primary));
}

/* ─── Case Card Swipe-Right Quick Claim (imp-m06, mobile) ───
   Swipe right on a worklist case row to slide the claim affordance in from
   the inline-start edge. JS (app-shell-card-swipe) sets --case-swipe-dx via
   setHostCssVars and data-case-swiping / data-case-swipe-armed on the row.
   The row itself never transforms — the table-row swipe-left contract owns
   row translation, so the two gestures compose without a transform race. */

[data-case-swipe-affordance] {
  display: none;
}

@media (hover: none) and (pointer: coarse) {
  [data-case-swipe-affordance] {
    position: absolute;
    inset-inline-start: 0;
    inset-block: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    padding-inline: var(--space-inline);
    background: color-mix(in oklab, var(--color-primary) 16%, var(--bg-panel));
    color: var(--color-primary);
    transform: translateX(-100%);
    opacity: 0;
    visibility: hidden;
    transition:
      transform var(--duration-fast, 120ms) var(--easing-standard),
      opacity var(--duration-fast, 120ms) var(--easing-standard);
  }
  tr[data-case-swiping] [data-case-swipe-affordance] {
    transform: translateX(calc(-100% + var(--case-swipe-dx, 0px)));
    opacity: 1;
    visibility: visible;
    transition: none;
  }
  tr[data-case-swipe-armed] [data-case-swipe-affordance] {
    background: color-mix(in oklab, var(--color-primary) 30%, var(--bg-panel));
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-case-swipe-affordance] {
    transition: none;
  }
}

/* ─── Cases List Pull-to-Refresh (imp-m07, mobile) ───
   Pull down at scrollTop 0 to re-issue the list's canonical hx-get. JS
   (app-shell-pull-refresh) sets --pull-refresh-dy and data-pull-refresh-state
   on the list host; the spinner markup is generated from
   partials/case-list-table.bao. Reduced-motion removes the rubber-band
   transform entirely (mission constraint) — the state spinner still reports. */

[data-pull-refresh-indicator] {
  display: none;
}

@media (hover: none) and (pointer: coarse) {
  [data-case-list] {
    position: relative;
    overscroll-behavior-y: contain;
  }
  [data-pull-refresh-indicator] {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    z-index: 2;
    display: flex;
    justify-content: center;
    padding-block: var(--space-micro);
    color: var(--text-secondary);
    transform: translateY(calc(-100% + var(--pull-refresh-dy, 0px)));
    opacity: 0;
    pointer-events: none;
  }
  [data-case-list][data-pull-refresh-state="pulling"] [data-pull-refresh-indicator],
  [data-case-list][data-pull-refresh-state="armed"] [data-pull-refresh-indicator] {
    opacity: 1;
  }
  [data-case-list][data-pull-refresh-state="armed"] [data-pull-refresh-indicator],
  [data-case-list][data-pull-refresh-state="refreshing"] [data-pull-refresh-indicator] {
    color: var(--color-primary);
  }
  [data-case-list][data-pull-refresh-state="refreshing"] [data-pull-refresh-indicator] {
    position: static;
    transform: none;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-case-list] [data-pull-refresh-indicator] {
    transform: none;
  }
}


/* ─── Filter Bar Bottom Sheet (Mobile) ───
   Transforms filter bar into bottom sheet on mobile (<48rem).
   JS sets --filter-sheet-open (0/1) and --filter-sheet-drag-y (px).
   CSS owns all visual state: backdrop, slide-up, drag feedback. */

@media (max-width: 47.99rem) {
  [data-primitive="FilterBar"] {
    --filter-sheet-open: 0;
    --filter-sheet-drag-y: 0px;
  }

  /*
   * Closed: collapse fieldsets into a single filter chip row (Apple HIG
   * compact filters). Only [data-filter-actions] + backdrop stay in flow so
   * the sheet trigger is the sole surface until open.
   */
  [data-primitive="FilterBar"]:not([data-filter-sheet-open="true"]) {
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-micro, 0.25rem);
    padding-block: var(--space-micro, 0.25rem);
  }
  [data-primitive="FilterBar"]:not([data-filter-sheet-open="true"]) > :not([data-filter-actions]):not([data-filter-sheet-backdrop]) {
    display: none;
  }
  [data-primitive="FilterBar"]:not([data-filter-sheet-open="true"]) > [data-filter-actions] {
    width: 100%;
    max-width: 100%;
    margin-inline-start: 0;
    justify-content: flex-end;
  }
  /* Closed chip row: only the sheet trigger — clear/export stay in the open sheet. */
  [data-primitive="FilterBar"]:not([data-filter-sheet-open="true"]) > [data-filter-actions] > :not([data-filter-sheet-trigger]) {
    display: none;
  }

  /*
   * Backdrop overlay.
   * Closed: pointer-events none + visibility hidden.
   * Never multiply --filter-sheet-open into pointer-events: numeric 0 is
   * invalid there and browsers fall back to auto, blocking the page.
   */
  /*
   * Scrim lives as a body sibling (not a bar child). The bar uses isolation
   * isolate for sticky dropdown stacking, so a child backdrop at z-overlay
   * paints over the bar's own un-z-indexed fieldsets and cannot cover the
   * page. Body portal + data-filter-sheet-open keeps the scrim full-viewport.
   */
  .bao-filter-sheet-backdrop {
    position: fixed;
    inset: 0;
    background: color-mix(in oklab, var(--color-base-100) 60%, transparent);
    /* Glass on chrome overlay only — reduced-transparency solidifies via a11y tokens. */
    backdrop-filter: blur(var(--liquid-blur-clear, 8px));
    z-index: var(--z-overlay, 1200);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--duration-normal, 200ms) var(--easing-standard);
  }

  .bao-filter-sheet-backdrop[data-filter-sheet-open="true"] {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  /* Open sheet: fixed bottom panel with safe-area dock clearance.
     z-modal (1300) outranks the sticky shell topbar (--z-shell-topbar:
     1250). Clear sticky top:0 via inset-block-start:auto — otherwise
     top + bottom + max-height over-constrain and CSS 2.1 section 10.6.4
     drops bottom, pinning the bottom sheet to the viewport top. */
  [data-primitive="FilterBar"][data-filter-sheet-open="true"] {
    position: fixed;
    inset-inline: 0;
    inset-block-start: auto;
    top: auto;
    inset-block-end: calc(
      var(--dock-height, 4rem) + var(--dock-safe-area-offset, 0px) +
      env(safe-area-inset-bottom, 0px)
    );
    max-height: min(80dvh, 36rem);
    overflow-y: auto;
    z-index: var(--z-modal, 1300);
    border-radius: var(--radius-box, 1rem) var(--radius-box, 1rem) 0 0;
    box-shadow: var(--shadow-glass-floating);
    transition: transform var(--duration-normal, 200ms) var(--easing-standard);
    background: var(--color-base-100);
    border: 1px solid var(--color-base-300);
    padding: var(--space-inline, 0.5rem);
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
  }

  /* Fieldsets above any residual in-bar chrome; relative stacks within the bar. */
  [data-primitive="FilterBar"][data-filter-sheet-open="true"] > :not([data-filter-actions]) {
    position: relative;
    z-index: 1;
  }

  /*
   * Drag transform is gated on an ACTIVE drag (JS stamps
   * data-filter-sheet-dragging): any non-none transform makes the bar the
   * containing block for the fixed backdrop, which would shrink the scrim to
   * the bar's own box and leave the page (incl. the sticky header)
   * un-scrimmed and tappable.
   */
  [data-primitive="FilterBar"][data-filter-sheet-open="true"][data-filter-sheet-dragging="true"] {
    transform: translateY(var(--filter-sheet-drag-y));
  }

  /* Drag handle indicator */
  [data-primitive="FilterBar"][data-filter-sheet-open="true"]::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 0.25rem;
    border-radius: var(--radius-full, 9999px);
    background: color-mix(in oklab, var(--text-tertiary) 30%, transparent);
    margin: var(--space-micro) auto var(--space-inline);
  }

  /* Trigger button: full-width filter chip when closed. */
  .bao-filter-sheet-trigger {
    gap: var(--space-micro);
  }
  [data-primitive="FilterBar"]:not([data-filter-sheet-open="true"]) .bao-filter-sheet-trigger {
    flex: 1 1 auto;
    justify-content: center;
    min-height: var(--touch-target-min, 2.75rem);
  }
  /* Active filter count badge (Mail/Photos idiom) — only when count > 0. */
  .bao-filter-active-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding-inline: var(--space-micro, 0.25rem);
    border-radius: var(--radius-full, 9999px);
    font-size: var(--text-2xs, 0.6875rem);
    font-weight: 700;
    line-height: 1;
    background: var(--color-primary);
    color: var(--color-primary-content);
    font-variant-numeric: tabular-nums;
  }
  .bao-filter-active-count[hidden] {
    display: none;
  }
  [data-primitive="FilterBar"][data-filter-active="true"]:not([data-filter-sheet-open="true"]) .bao-filter-sheet-trigger {
    border-color: color-mix(in oklab, var(--color-primary) 40%, var(--border-base, var(--color-base-300)));
    background: color-mix(in oklab, var(--color-primary) 10%, var(--bg-panel, var(--color-base-100)));
  }

  /*
   * Progressive hide when a FilterBar has nothing to filter (training connector
   * offline, empty API keys inventory, etc.). JS stamps
   * data-training-filters-deferred or data-filter-bar-deferred. Specificity
   * must beat phone FilterBar display:grid + closed-sheet chip layout so the
   * sheet trigger leaves the layout (not just aria-hidden).
   */
  form[data-primitive="FilterBar"][data-training-filters][data-training-filters-deferred],
  [data-primitive="FilterBar"][data-training-filters][data-training-filters-deferred],
  form[data-training-filters][data-training-filters-deferred],
  [data-training-filters][data-training-filters-deferred],
  form[data-primitive="FilterBar"][data-filter-bar-deferred],
  [data-primitive="FilterBar"][data-filter-bar-deferred],
  form[data-filter-bar][data-filter-bar-deferred],
  [data-filter-bar][data-filter-bar-deferred] {
    display: none;
    visibility: hidden;
    pointer-events: none;
    height: 0;
    max-height: 0;
    min-height: 0;
    overflow: hidden;
    margin: 0;
    padding: 0;
    border-width: 0;
  }
}

/* Desktop/tablet: same deferred hide (filter noise without inventory). */
form[data-primitive="FilterBar"][data-training-filters][data-training-filters-deferred],
[data-primitive="FilterBar"][data-training-filters][data-training-filters-deferred],
form[data-training-filters][data-training-filters-deferred],
[data-training-filters][data-training-filters-deferred],
form[data-primitive="FilterBar"][data-filter-bar-deferred],
[data-primitive="FilterBar"][data-filter-bar-deferred],
form[data-filter-bar][data-filter-bar-deferred],
[data-filter-bar][data-filter-bar-deferred] {
  display: none;
  visibility: hidden;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .bao-filter-sheet-backdrop,
  [data-primitive="FilterBar"][data-filter-sheet-open="true"] {
    transition: none;
  }
}

/* ─── Print Optimization ─── */

@media print {
  #app-header,
  #app-sidebar,
  /* ID-level: tokens-layout-dock-base is emitted after this module and its
     .bao-mobile-dock.dock display:flex outranks a plain class here, so the
     dock printed on every page. */
  #mobile-dock.dock.bao-mobile-dock,
  nav#mobile-dock[data-mobile-dock],
  #toast-region,
  .drawer-toggle,
  [data-page-header-actions],
  .htmx-indicator {
    display: none;
  }
  #app-main {
    overflow: visible;
    padding: 0;
  }
  .card.card-border,
  [data-dashboard-widget] {
    break-inside: avoid;
    box-shadow: none;
    border: 1px solid color-mix(in oklab, var(--color-base-content) 15%, transparent);
  }
}


/* ─── Desktop File Drop Zone (drag-and-drop upload affordance) ───
   JS sets data-bao-drop-active + data-state via app-shell-file-drop.ts;
   CSS owns every visual. Surface styles are gated to fine-pointer devices;
   preview list + error styles apply everywhere. */

@media (hover: hover) and (pointer: fine) {
  .bao-file-drop-zone {
    display: flex;
    flex-direction: column;
    gap: var(--space-inline, 0.5rem);
    border: 2px dashed var(--border-base, var(--color-base-300));
    border-radius: var(--radius-box, 1rem);
    padding: var(--space-page, 1.5rem);
    text-align: center;
    cursor: pointer;
    background: color-mix(in oklab, var(--bg-panel, var(--color-base-100)) 80%, transparent);
    transition: border-color var(--duration-normal, 200ms) var(--easing-standard, ease),
                background var(--duration-normal, 200ms) var(--easing-standard, ease),
                box-shadow var(--duration-normal, 200ms) var(--easing-standard, ease);
  }
  .bao-file-drop-zone[data-bao-drop-active="true"] {
    border-color: var(--color-primary);
    background: color-mix(in oklab, var(--color-primary) 10%, transparent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 20%, transparent);
  }
  .bao-file-drop-zone[data-state="error"] {
    border-color: var(--color-error);
    background: color-mix(in oklab, var(--color-error) 8%, transparent);
  }
  .bao-file-drop-zone:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
  }
}
.bao-file-drop-zone__prompt {
  margin: 0;
  font-size: var(--text-sm, 0.875rem);
  color: var(--text-secondary, var(--color-base-content));
}
.bao-file-drop-zone__files {
  display: flex;
  flex-direction: column;
  gap: var(--space-micro, 0.25rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.bao-file-drop-zone__file-item {
  display: flex;
  align-items: center;
  gap: var(--space-inline, 0.5rem);
  padding: var(--space-micro, 0.25rem) var(--space-inline, 0.5rem);
  border: 1px solid var(--border-base, var(--color-base-300));
  border-radius: var(--radius-field, 0.75rem);
  background: var(--bg-panel, var(--color-base-100));
  text-align: start;
}
.bao-file-drop-zone__file-thumb {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-xs, 0.25rem);
  object-fit: cover;
  flex-shrink: 0;
}
.bao-file-drop-zone__file-meta {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 0.125rem;
}
.bao-file-drop-zone__file-name {
  font-size: var(--text-sm, 0.875rem);
  color: var(--text-primary, var(--color-base-content));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bao-file-drop-zone__file-size {
  font-size: var(--text-xs, 0.75rem);
  color: var(--text-tertiary, var(--color-base-content));
  font-variant-numeric: tabular-nums;
}
.bao-file-drop-zone__more {
  font-size: var(--text-xs, 0.75rem);
  color: var(--text-tertiary, var(--color-base-content));
  text-align: start;
  padding-inline: var(--space-inline, 0.5rem);
}
.bao-file-drop-zone__error {
  margin: 0;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-error-content, var(--color-error));
}
.bao-file-drop-zone__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
@media (prefers-reduced-motion: reduce) {
  .bao-file-drop-zone {
    transition: none;
  }
}

/* ─── Dockable Panels (Desktop) ───
   State lives on data-bao-dock-edge / data-bao-dock-floating; geometry on
   host CSS vars --bao-dock-{x,y,w,h} published by app-shell-dockable-panels.
   Only mounted on desktop fine-pointer hosts; mobile/tablet untouched. */

.bao-dockable-panel {
  position: relative;
  transition:
    box-shadow var(--duration-normal, 200ms) var(--easing-standard),
    opacity var(--duration-normal, 200ms) var(--easing-standard);
}

.bao-dockable-panel[data-bao-docking="true"] {
  opacity: 0.8;
  box-shadow: var(--shadow-glass-floating);
  z-index: var(--z-overlay);
  transition: none;
}

.bao-dockable-panel[data-bao-dock-floating="true"] {
  position: fixed;
  inset-inline-start: var(--bao-dock-x, 0);
  inset-block-start: var(--bao-dock-y, 0);
  width: var(--bao-dock-w, 20rem);
  height: var(--bao-dock-h, 24rem);
  z-index: var(--z-overlay);
}

.bao-dockable-panel[data-bao-dock-resizing="true"] {
  transition: none;
  box-shadow: var(--shadow-glass-floating);
}

[data-bao-dock-resize] {
  position: absolute;
  inline-size: 0.75rem;
  block-size: 0.75rem;
  cursor: nwse-resize;
  background: transparent;
}[data-bao-dock-resize="n"],
[data-bao-dock-resize="s"] { inset-inline: 0; cursor: ns-resize; block-size: 0.5rem; }[data-bao-dock-resize="e"],
[data-bao-dock-resize="w"] { inset-block: 0; cursor: ew-resize; inline-size: 0.5rem; }
[data-bao-dock-resize="n"] { inset-block-start: -0.25rem; }
[data-bao-dock-resize="s"] { inset-block-end: -0.25rem; }
[data-bao-dock-resize="w"] { inset-inline-start: -0.25rem; }
[data-bao-dock-resize="e"] { inset-inline-end: -0.25rem; }
[data-bao-dock-resize="ne"] { inset-block-start: -0.25rem; inset-inline-end: -0.25rem; cursor: nesw-resize; }
[data-bao-dock-resize="nw"] { inset-block-start: -0.25rem; inset-inline-start: -0.25rem; cursor: nwse-resize; }
[data-bao-dock-resize="se"] { inset-block-end: -0.25rem; inset-inline-end: -0.25rem; cursor: nwse-resize; }
[data-bao-dock-resize="sw"] { inset-block-end: -0.25rem; inset-inline-start: -0.25rem; cursor: nesw-resize; }

[data-bao-dock-handle] {
  cursor: grab;
  touch-action: none;
}
[data-bao-dock-handle]:active { cursor: grabbing; }

.bao-dock-zone {
  position: fixed;
  inset: auto;
  pointer-events: none;
  border-radius: var(--radius-box, 1rem);
  transition:
    background-color var(--duration-fast, 120ms) var(--easing-standard),
    border-color var(--duration-fast, 120ms) var(--easing-standard);
}
.bao-dock-zone[data-bao-dock-zone="left"] {
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 22%;
}
.bao-dock-zone[data-bao-dock-zone="right"] {
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: 22%;
}
.bao-dock-zone[data-bao-dock-zone="top"] {
  inset-inline: 0;
  inset-block-start: 0;
  block-size: 22%;
}
.bao-dock-zone[data-bao-dock-zone="bottom"] {
  inset-inline: 0;
  inset-block-end: 0;
  block-size: 22%;
}
.bao-dock-zone[data-bao-dock-zone="float"] {
  inset: 25% 30%;
}
.bao-dock-zone[data-bao-drop-target="true"] {
  background: color-mix(in oklab, var(--accent-primary) 15%, transparent);
  border: 2px dashed var(--accent-primary);
}

@media (prefers-reduced-motion: reduce) {.bao-dockable-panel,
.bao-dock-zone {
    transition: none;
  }
}


/* Keep a co-hosted toast out of mobile dialog actions. The toast remains in the top layer so it is dismissible, but it moves to the dialog's upper edge instead of covering the sticky footer. */
#toast-region.toast[data-toast-cohosted="true"] {
  inset-block-start: max(var(--space-inline, 0.5rem), env(safe-area-inset-top, 0px));
  inset-block-end: auto;
  inset-inline-start: auto;
  inset-inline-end: max(var(--space-inline, 0.5rem), env(safe-area-inset-right, 0px));
}



/* ─── Dashboard: widget actions are always visible ───
   The same hover-reveal that was removed from table rows below also ran on
   every .card-actions inside a widget host, and it was not clutter control —
   it was a second owner of visibility fighting the real one.

   [data-dashboard-widget] is not only the dashboard's KPI tiles: the Models
   page wraps its model catalogue in data-widget="models-predefined", so every
   model card's .card-actions footer matched. Each Download button — the
   primary CTA of that page — painted at opacity 0 with pointer-events intact
   until the operator happened to sweep the pointer across the panel, while
   assistive tech announced the whole row the entire time.

   The widget's own control cluster never needed the fade either: the layout
   customizer fieldset carries [hidden], and
   tokens-layout-dashboard-widgets-part-b.ts owns that state unlayered so it
   beats daisyUI's .card-actions. Visibility has one owner again.

   No @media (hover: none) / (pointer: coarse) escape replaces this. A pointer
   query cannot rescue a control that should never have been hidden: a desktop
   with a touchscreen reports both, and a keyboard user reports neither. */

/* ─── Dashboard: Staggered Widget Entrance Animation ───
   Sequential fade-in for dashboard widgets on page load. */

@keyframes bao-widget-entrance {
  from {
    opacity: 0;
    transform: translateY(var(--space-section, 0.75rem));
  }
  to {
    opacity: 1;
    transform: none;
  }
}

[data-dashboard-widget] {
  animation: bao-widget-entrance var(--duration-slow, 320ms) var(--easing-entrance) both;
}

[data-dashboard-widget]:nth-child(1) { animation-delay: var(--duration-instant); }
[data-dashboard-widget]:nth-child(2) { animation-delay: calc(var(--stagger-step) * 2); }
[data-dashboard-widget]:nth-child(3) { animation-delay: calc(var(--stagger-step) * 4); }
[data-dashboard-widget]:nth-child(4) { animation-delay: calc(var(--stagger-step) * 6); }
[data-dashboard-widget]:nth-child(5) { animation-delay: calc(var(--stagger-step) * 8); }
[data-dashboard-widget]:nth-child(6) { animation-delay: calc(var(--stagger-step) * 10); }
[data-dashboard-widget]:nth-child(7) { animation-delay: calc(var(--stagger-step) * 12); }
[data-dashboard-widget]:nth-child(8) { animation-delay: calc(var(--stagger-step) * 14); }
[data-dashboard-widget]:nth-child(9) { animation-delay: calc(var(--stagger-step) * 16); }
[data-dashboard-widget]:nth-child(10) { animation-delay: calc(var(--stagger-step) * 18); }

@media (prefers-reduced-motion: reduce) {
  [data-dashboard-widget] {
    animation: none;
  }
}

/* ─── Table row actions: always visible ───
   SSOT action host is bao-table-pinned-action (Table.bao pinnedActionCell);
   bao-table-row-actions is the inner control cluster stamped on the value span.

   These controls used to fade in on row hover. That is removed, not tuned.
   Hover-reveal put opacity 0 on controls that kept pointer-events auto,
   so a worklist row shipped six invisible-but-clickable buttons under a
   visibly EMPTY "Actions" column — a pathologist could click a row action
   they could not see, and could not discover the actions at all without
   sweeping the mouse across every row. Assistive tech announced them the
   whole time, so sighted and screen-reader users saw different UIs.

   A clinical worklist is not a marketing table: row actions are primary
   affordances and stay painted in every state, at every density and pointer
   type. Keeping them visible also deletes an entire reactive state — there is
   no hover/touch/container-width matrix left to get wrong. */

/* ─── Form Fields: Focus Animation ───
   Subtle scale and glow on active input fields. */

/* Focus ring only — no scale transform (scale caused CLS / layout thrash on
   dense enterprise forms; Apple HIG favors stable geometry + ring feedback). */
.input:focus,
.select:focus,
.textarea:focus {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 20%, transparent);
  transition: box-shadow var(--duration-fast, 120ms) var(--easing-standard);
}

@media (prefers-reduced-motion: reduce) {
  .input:focus,
  .select:focus,
  .textarea:focus {
    transition: none;
  }
}

/* ─── AI Analysis: Progress Animation ───
   Smooth progress bar fill with gradient sweep. */

@keyframes bao-progress-fill {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}

.progress::-webkit-progress-value {
  background-image: linear-gradient(
    90deg,
    var(--color-primary) 0%,
    color-mix(in oklab, var(--color-primary) 80%, var(--color-accent)) 50%,
    var(--color-primary) 100%
  );
  background-size: 200% 100%;
  animation: bao-progress-fill var(--duration-progress) var(--easing-standard) infinite;
  transition: width var(--duration-normal, 200ms) var(--easing-standard);
}

@media (prefers-reduced-motion: reduce) {
  .progress::-webkit-progress-value {
    animation: none;
    background-image: none;
    background-color: var(--color-primary);
  }
}

/* ─── Help: FAQ Accordion Animation ───
   Smooth expand/collapse for FAQ sections. */

details.collapse[open] .collapse-content {
  animation: bao-accordion-expand var(--duration-normal, 200ms) var(--easing-entrance) both;
}

@keyframes bao-accordion-expand {
  from {
    opacity: 0;
    max-height: 0;
  }
  to {
    opacity: 1;
    max-height: 1000px;
  }
}

@media (prefers-reduced-motion: reduce) {
  details.collapse[open] .collapse-content {
    animation: none;
  }
}

/* ─── Card Hover Lift with Shadow ───
   Subtle elevation increase on card hover for depth hierarchy. */

/* Lift only when tilt is inactive — one transform owner per state. */
@media (hover: hover) and (pointer: fine) {
  .card.card-border:hover:not([data-bao-tilt="true"]),
  [data-dashboard-widget]:hover:not([data-bao-tilt="true"]):not([data-bao-dragging="true"]) {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
    transition: transform var(--duration-normal, 200ms) var(--easing-standard),
                box-shadow var(--duration-normal, 200ms) var(--easing-standard);
  }
}

@media (prefers-reduced-motion: reduce) {
  .card.card-border:hover,
  [data-dashboard-widget]:hover {
    transform: none;
  }
}

/*
 * Mobile dock + phone densify must stay UNLAYERED: nested inside
 * @layer utilities the ≥80rem display:none is dropped on Chromium ~101
 * (WebView ignores @layer bodies), so daisyUI .dock stays painted beside
 * the permanent rail at 1280. Co-owned with tokens-glass-shell-drawer.
 */


  :root {
    --dock-height: 4rem;
    --dock-icon-size: 1.25rem;
    --dock-label-size: 0.6875rem;
    --dock-safe-area-offset: env(safe-area-inset-bottom, 0px);
  }
  .bao-mobile-dock.dock,
  .bao-mobile-dock {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: space-around;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--z-overlay, 1200);
    width: 100%;
    max-width: 100%;
    min-height: calc(var(--dock-height) + var(--dock-safe-area-offset));
    height: calc(var(--dock-height) + var(--dock-safe-area-offset));
    padding-bottom: var(--dock-safe-area-offset);
    border-top: 1px solid color-mix(in oklab, var(--border-base, var(--color-base-300)) 40%, transparent);
    color: var(--color-base-content, var(--text-primary));
    /* Material: bao-surface-glass-regular on .bao-mobile-dock (app-shell.bao). */
    box-shadow:
      0 -1px 0 0 color-mix(in oklab, var(--border-base, var(--color-base-300)) 50%, transparent),
      var(--shadow-glass-elevated);
    /* Swipe-friendly overflow when locale labels widen past 5 slots. */
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    touch-action: pan-x;
  }
  .bao-mobile-dock.dock::-webkit-scrollbar,
  .bao-mobile-dock::-webkit-scrollbar {
    display: none;
  }
  [data-reduced-transparency="true"] .bao-mobile-dock.dock,
  [data-reduced-transparency="true"] .bao-mobile-dock {
    /* Glass a11y solidifies .bao-surface-glass-regular — dock keeps edge only. */
    box-shadow:
      0 -1px 0 0 color-mix(in oklab, var(--border-base, var(--color-base-300)) 50%, transparent);
  }
  @media (prefers-reduced-transparency: reduce) {
    .bao-mobile-dock.dock,
    .bao-mobile-dock {
      box-shadow:
        0 -1px 0 0 color-mix(in oklab, var(--border-base, var(--color-base-300)) 50%, transparent);
    }
  }
  /*
   * Tablet band and up (≥48rem / 768px at 16px root): the topbar hamburger is
   * the single nav affordance (Apple HIG — iPad-class widths get the sidebar,
   * not a phone tab bar), so the dock yields. Without this the 48–79.99rem band
   * painted BOTH the dock and the hamburger.
   *
   * Zeroing --dock-height is what retires the reserved bottom gutter: every
   * clearance consumer (#app-main padding, conversation footer, capture shutter
   * offsets, canvas-island inset, scroll-padding) reads the token, so no
   * per-surface media forks are needed and none can drift out of sync.
   *
   * This is a nav-affordance band, NOT the permanent-rail band. The rail stays
   * ≥80rem only (Android/WebView phones report ≥1024 CSS px) — see the separate
   * block below and tokens-glass-shell-drawer.
   */
  @media (min-width: 48rem) {
    :root {
      --dock-height: 0rem;
    }
    #mobile-dock.dock.bao-mobile-dock,
    nav#mobile-dock[data-mobile-dock],
    .bao-mobile-dock.dock,
    .bao-mobile-dock {
      display: none;
      height: 0;
      min-height: 0;
      overflow: hidden;
      pointer-events: none;
      visibility: hidden;
    }
  }
  /*
   * Permanent-rail band (≥80rem / 1280px at 16px root): dock yields to the
   * in-flow sidebar. ID selectors beat daisyUI .dock display:flex (components
   * layer) without !important. Mirror lives in tokens-glass-shell-drawer.
   * Subsumed by the ≥48rem hide above; kept explicit because the rail band is
   * co-owned and WebView 101 needs the ID-level rule stated at this breakpoint.
   */
  @media (min-width: 80rem) {
    #mobile-dock.dock.bao-mobile-dock,
    nav#mobile-dock[data-mobile-dock],
    .bao-mobile-dock.dock,
    .bao-mobile-dock {
      display: none;
      height: 0;
      min-height: 0;
      overflow: hidden;
      pointer-events: none;
      visibility: hidden;
    }
  }
  .bao-mobile-dock a,
  .bao-mobile-dock-item,
  .bao-mobile-dock [data-mobile-dock-more],
  .bao-mobile-dock button.bao-mobile-dock-item,
  .bao-mobile-dock button[data-mobile-dock-more] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2xs, 0.125rem);
    /* Five primary slots + More: equal flex tracks, never clip the last item
     * (Apple HIG tab bar — all tabs fully visible, no half-cut "More"). */
    min-width: 0;
    max-width: var(--dock-item-max-width, 20%);
    min-height: var(--touch-target-min, 2.75rem);
    flex: 1 1 0;
    padding-block: var(--space-micro, 0.375rem);
    padding-inline: var(--space-2xs, 0.125rem);
    margin: 0;
    color: color-mix(in oklab, var(--color-base-content, currentColor) 75%, transparent);
    text-decoration: none;
    cursor: pointer;
    border: 0;
    border-radius: var(--radius-field, 0.75rem);
    background: transparent;
    box-shadow: none;
    appearance: none;
    -webkit-appearance: none;
    font: inherit;
    line-height: inherit;
    transition-property: background-color, color, opacity, transform;
    transition-duration: var(--duration-fast, 120ms);
    transition-timing-function: var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
    scroll-snap-align: center;
    flex-shrink: 1;
  }
  /* Ultra-narrow: icon-only dock (label remains aria-label). */
  @media (max-width: 22.5rem) {
    .bao-mobile-dock .dock-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    .bao-mobile-dock a,
    .bao-mobile-dock-item,
    .bao-mobile-dock [data-mobile-dock-more] {
      max-width: none;
      min-width: var(--touch-target-min, 2.75rem);
    }
  }
  /* More is a <button> — never look selected unless the rail is open. */
  .bao-mobile-dock [data-mobile-dock-more][aria-expanded="true"] {
    color: var(--color-primary, var(--accent-primary, currentColor));
    background: color-mix(in oklab, var(--color-primary) 12%, transparent);
  }
  .bao-mobile-dock a:active,
  .bao-mobile-dock-item:active,
  .bao-mobile-dock [data-mobile-dock-more]:active {
    background: color-mix(in oklab, var(--color-primary) 16%, transparent);
    opacity: var(--opacity-chrome, 0.92);
    transform: scale(0.96);
  }
  .bao-mobile-dock a:focus-visible,
  .bao-mobile-dock-item:focus-visible,
  .bao-mobile-dock [data-mobile-dock-more]:focus-visible {
    outline: 2px solid var(--color-primary, currentColor);
    outline-offset: 2px;
  }
  .bao-mobile-dock a.dock-active,
  .bao-mobile-dock a[aria-current="page"],
  .bao-mobile-dock-item.dock-active,
  .bao-mobile-dock-item[aria-current="page"] {
    color: var(--color-primary, var(--accent-primary, currentColor));
    background: color-mix(in oklab, var(--color-primary) 14%, var(--color-base-100));
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-primary) 28%, transparent);
  }
  /* Apple HIG active: filled glyph weight via stroke; inactive stays outline. */
  .bao-mobile-dock a.dock-active svg,
  .bao-mobile-dock a[aria-current="page"] svg,
  .bao-mobile-dock-item.dock-active svg,
  .bao-mobile-dock-item[aria-current="page"] svg {
    stroke-width: 2.25;
    fill: color-mix(in oklab, var(--color-primary) 18%, transparent);
  }
  .bao-mobile-dock svg,
  .bao-mobile-dock .bao-dock-icon {
    width: var(--dock-icon-size);
    height: var(--dock-icon-size);
    flex-shrink: 0;
  }
  .bao-mobile-dock .dock-label {
    font-size: var(--dock-label-size);
    font-weight: 500;
    line-height: 1.1;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-mobile-dock a,
    .bao-mobile-dock-item,
    .bao-mobile-dock [data-mobile-dock-more] {
      transition-property: none;
      transition-duration: var(--duration-instant, 0ms);
    }
    .bao-mobile-dock a:active,
    .bao-mobile-dock-item:active,
    .bao-mobile-dock [data-mobile-dock-more]:active {
      transform: none;
    }
  }
  [data-reduced-motion="true"] .bao-mobile-dock a:active,
  [data-reduced-motion="true"] .bao-mobile-dock-item:active,
  [data-reduced-motion="true"] .bao-mobile-dock [data-mobile-dock-more]:active {
    transform: none;
  }

  /* ─── FAB (imp-t08): floating page-primary action ───
     Position SSOT: floats above the bottom dock + home-indicator safe area on
     phone, and above the page gutter on tablet; inline-end honors the trailing
     safe-area inset. Visible only below the 64rem desktop band — the header
     CTA owns the verb on desktop (one primary affordance per band).
     Geometry (56px target) and surface live in FAB.bao tokens; this block
     owns placement, band visibility, and the scroll-aware hidden state
     (app-shell-fab.ts stamps data-fab-hidden). */
  .bao-fab {
    position: fixed;
    inset-block-end: calc(var(--dock-height, 4rem) + var(--dock-safe-area-offset, env(safe-area-inset-bottom, 0px)) + var(--space-section, 1rem));
    inset-inline-end: max(var(--space-section, 1rem), env(safe-area-inset-right, 0px));
    z-index: var(--z-sticky, 1100);
    display: inline-flex;
    box-shadow: var(--shadow-glass-floating);
    transition-property: transform, opacity;
    transition-duration: var(--duration-normal, 200ms);
    transition-timing-function: var(--easing-standard, cubic-bezier(0.2, 0, 0, 1));
  }
  /* Scroll-aware hide: slide below the dock edge and drop hit-testing. */
  .bao-fab[data-fab-hidden="true"] {
    transform: translateY(calc(100% + var(--space-section, 1rem)));
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
  }
  /* Desktop band: header CTA owns the primary verb — FAB yields entirely.
     The repeated class selector lifts this rule to (0,4,0) so the shell-wide
     .btn:not([hidden]):not(.hidden) display rule (0,3,0) cannot re-assert
     inline-flex above 64rem — the same outrank pattern documented in
     tokens-layout-page-shells-capture-responsive (attribute repetition). */
  @media (min-width: 64rem) {
    .bao-fab.bao-fab.bao-fab.bao-fab {
      display: none;
    }
  }
  /* Phone band (< 40rem): the page header's action zone goes full-width in
     flow there (max-sm:w-full, the same 40rem threshold), so the header CTA
     is already a full-bleed primary button a thumb reaches without leaving the
     document flow. The FAB is a fixed second copy of that one verb, and being
     fixed it prints over the list: at 390x844 on /cases it covered the card
     stack's row-select control outright — elementFromPoint at the checkbox
     centre [348,725] returned button.bao-fab, so no tap could reach it. One
     primary action per surface: the in-flow CTA survives because it cannot
     occlude content, and the FAB yields here for the same reason it already
     yields above 64rem. It keeps the 40-64rem band, where the header CTA
     collapses to a trailing btn-sm well out of thumb reach.
     Same (0,4,0) repetition as the desktop rule, for the same reason. */
  @media (max-width: 39.99rem) {
    .bao-fab.bao-fab.bao-fab.bao-fab {
      display: none;
    }
  }
  /* Tablet band has no dock: drop the FAB to the page gutter clearance. */
  @media (min-width: 48rem) and (max-width: 63.99rem) {
    .bao-fab {
      inset-block-end: calc(var(--space-section, 1rem) + var(--dock-safe-area-offset, env(safe-area-inset-bottom, 0px)));
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .bao-fab {
      transition-property: none;
      transition-duration: var(--duration-instant, 0ms);
    }
    .bao-fab[data-fab-hidden="true"] {
      transform: none;
    }
  }

@media (max-width: 47.99rem) {
  [data-page="capture"] [data-capture-workbench] {
    flex: 0 0 auto;
    min-height: 0;
  }

  [data-page="capture"] [data-capture-workbench-primary] {
    flex: 0 0 auto;
    min-height: 0;
  }

  [data-page="capture"] [data-camera-preview].bao-capture-preview {
    aspect-ratio: 4 / 3;
    flex: 0 0 auto;
    min-height: 0;
  }
}
[data-primitive="ScannerPreview"].bao-scanner-preview {
  aspect-ratio: 16 / 9;
  flex: 0 0 auto;
  min-height: 0;
}


  /*
   * PIN pad default: hidden at all widths. Phone media (max-width 40rem) under
   * the densify block turns it into a 3-column grid. Must live outside phone
   * media so desktop never paints an on-screen keypad as a block div.
   */
  .bao-login-pin-pad {
    display: none;
    width: 100%;
    min-width: 0;
  }
  .bao-login-pin-pad-spacer {
    display: none;
    min-width: 0;
    min-height: 0;
    pointer-events: none;
  }
  /* Login PIN otp — solid daisyUI slots; password input remains SSOT. */
  [data-page="login"] .bao-login-pin-otp.otp {
    width: 100%;
    justify-content: center;
    margin-block-end: var(--space-micro, 0.25rem);
  }
  [data-page="login"] .bao-login-pin-otp input[data-operator-pin] {
    letter-spacing: var(--tracking-otp, 0.35em);
  }
  /*
   * OTP tracking applies to the entered digits only. Letting it inherit into
   * the placeholder stretches the prompt copy into unreadable spaced capitals.
   */
  [data-page="login"] .bao-login-pin-otp input[data-operator-pin]::placeholder {
    letter-spacing: normal;
  }
  [data-page="login"] .bao-login-pin-otp + .validator-hint {
    display: none;
  }
  [data-page="login"] .bao-login-pin-otp:has(input:user-invalid) + .validator-hint {
    display: block;
  }
  /* Collection tables: hide low-priority columns on phone/tablet. */
  @media (max-width: 47.99rem) {
    [data-primitive="DataTable"] [data-col-priority="low"],
    [data-primitive="DataTable"] th[data-col-priority="low"],
    [data-primitive="DataTable"] td[data-col-priority="low"] {
      display: none;
    }
  }
  @media (max-width: 63.99rem) {
    [data-primitive="DataTable"] [data-col-priority="medium"],
    [data-primitive="DataTable"] th[data-col-priority="medium"],
    [data-primitive="DataTable"] td[data-col-priority="medium"] {
      display: none;
    }
  }
  /*
   * Dock clearance SSOT — must beat higher-specificity gutter rules
   * (#app-main:not(:has(...))) and WebView 101 data-main-page-type mirrors.
   * Permanent rail (≥80rem) has no fixed dock; clearance drops to zero there.
   */
  @media (max-width: 79.99rem) {
    #app-main,
    #app-main:not([data-main-page-type="media"]):not([data-main-page-type="conversation"]),
    #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
      padding-bottom: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--space-section, 0.75rem) + var(--touch-target-min, 2.75rem));
      padding-block-end: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--space-section, 0.75rem) + var(--touch-target-min, 2.75rem));
    }
    #app-main[data-main-page-type="media"],
    #app-main[data-main-page-type="conversation"],
    #app-main:has([data-page-type="media"]),
    #app-main:has([data-page-type="conversation"]) {
      padding-bottom: calc(var(--dock-height) + var(--dock-safe-area-offset));
      padding-block-end: calc(var(--dock-height) + var(--dock-safe-area-offset));
    }
  }
  @media (min-width: 80rem) {
    #app-main,
    #app-main:not([data-main-page-type="media"]):not([data-main-page-type="conversation"]),
    #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
      padding-bottom: var(--page-gutter-block, var(--space-section, 0.75rem));
      padding-block-end: var(--page-gutter-block, var(--space-section, 0.75rem));
    }
  }
  /* Phone densify: tighter page gutters so workbench fills the docked viewport. */
  @media (max-width: 47.99rem) {
    #app-main,
    #app-main:not([data-main-page-type="media"]):not([data-main-page-type="conversation"]),
    #app-main:not(:has([data-page-type="media"])):not(:has([data-page-type="conversation"])) {
      padding-inline: var(--space-inline, 0.5rem);
      padding-block-start: var(--space-micro, 0.25rem);
      padding-bottom: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--space-section, 0.75rem) + var(--touch-target-min, 2.75rem));
      padding-block-end: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--space-section, 0.75rem) + var(--touch-target-min, 2.75rem));
    }
    #app-main[data-main-page-type="media"],
    #app-main[data-main-page-type="conversation"],
    #app-main:has([data-page-type="media"]),
    #app-main:has([data-page-type="conversation"]) {
      padding-inline: 0;
      padding-block-start: 0;
      padding-bottom: calc(var(--dock-height) + var(--dock-safe-area-offset));
      padding-block-end: calc(var(--dock-height) + var(--dock-safe-area-offset));
    }
    [data-page="settings"] .bao-settings-group {
      padding: var(--space-inline, 0.5rem);
    }
    [data-page="settings"] .bao-tabs-scroll {
      margin-inline: 0;
    }
    /* Capture context grid: 2-up on phone, not 4 tiny truncated chips. */
    [data-page="capture"] [data-capture-context-strip],
    [data-capture-context-strip] {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    /*
     * Login AuthShell densify — single card, phone-first scan strip, no dock.
     * Hides chrome that only applies to authenticated workbench pages.
     */
    [data-page="login"] {
      max-width: 100%;
      width: 100%;
      padding-inline: 0;
      padding-block-end: var(--space-section, 0.75rem);
    }
    /* Kill nested daisyUI fieldset border cages inside solid AuthShell panel. */
    [data-page="login"] [data-auth-panel] fieldset,
    [data-page="login"] [data-auth-panel] .fieldset,
    [data-page="login"] [data-operator-login] fieldset.fieldset {
      border: 0;
      padding: 0;
      margin: 0;
      background: transparent;
      box-shadow: none;
      outline: none;
    }
    [data-page="login"] [data-auth-panel] .fieldset-legend,
    [data-page="login"] [data-auth-panel] legend {
      padding-inline: 0;
      margin-bottom: var(--space-micro, 0.25rem);
      float: none;
    }
    /* Phone: AuthShell full-bleed; brand mark must not dominate first viewport. */
    [data-page="login"] [data-operator-login] {
      width: 100%;
      max-width: none;
      padding-inline: var(--space-inline, 0.5rem);
    }
    [data-page="login"] [data-operator-login] > .relative.z-10,
    [data-page="login"] [data-operator-login] > [class*="max-w-auth"] {
      max-width: none;
      width: 100%;
    }
    [data-page="login"] [data-auth-panel] {
      width: 100%;
      border-radius: var(--radius-box, 0.5rem);
    }
    [data-page="login"] .size-brand-mark {
      width: 1.75rem;
      height: 1.75rem;
    }
    [data-page="login"] .size-brand-mark svg {
      width: 1rem;
      height: 1rem;
    }
    /*
     * Auth pages must not show workbench dock chrome. Unlayered + full
     * daisyUI+id specificity (no !important — cascade ownership stays clean).
     */
    body:has([data-page="login"]) #mobile-dock.dock.bao-mobile-dock,
    body:has([data-page="login"]) nav#mobile-dock[data-mobile-dock] {
      display: none;
      height: 0;
      min-height: 0;
      overflow: hidden;
      pointer-events: none;
      visibility: hidden;
    }
    /*
     * Auth plane (design.md §4): hide workbench context strip, sidebar rail,
     * session rail, and secondary topbar status chrome — only brand + AuthShell.
     * Specificity beats workbench display:flex rules without !important.
     */
    body:has([data-page="login"]) .drawer-side#app-sidebar,
    body:has([data-page="login"]) .drawer-side,
    body:has([data-page="login"]) #session-rail.bao-shell-session-rail,
    body:has([data-page="login"]) #session-rail,
    body:has([data-page="login"]) #app-header #context-switcher[data-context-switcher],
    body:has([data-page="login"]) #app-header [data-context-switcher],
    body:has([data-page="login"]) #app-header .bao-shell-status-badge[data-runtime-status],
    body:has([data-page="login"]) #app-header .bao-shell-status-badge[data-registry-status],
    body:has([data-page="login"]) #app-header .bao-shell-status-badge[data-device-status],
    body:has([data-page="login"]) #app-header [data-topbar-search],
    body:has([data-page="login"]) #app-header .bao-shell-kbd-control {
      display: none;
      width: 0;
      min-width: 0;
      max-width: 0;
      height: 0;
      min-height: 0;
      overflow: hidden;
      pointer-events: none;
      visibility: hidden;
    }
    body:has([data-page="login"]) #app-header .navbar-start {
      max-width: none;
      flex: 1 1 auto;
    }
    body:has([data-page="login"]) #app-header #shell-page-title[data-shell-page-title],
    body:has([data-page="login"]) #app-header [data-shell-page-title] {
      position: static;
      width: auto;
      height: auto;
      max-width: 12rem;
      margin: 0;
      overflow: hidden;
      clip: auto;
      white-space: nowrap;
      text-overflow: ellipsis;
      visibility: visible;
      pointer-events: auto;
    }
    body:has([data-page="login"]) #app-main {
      padding-bottom: var(--space-section, 0.75rem);
      padding-block-end: var(--space-section, 0.75rem);
      /* Auth plane owns scroll if needed; never inflate body/document width. */
      overflow-x: clip;
      overflow-y: auto;
    }
    [data-page="login"] [data-operator-login] {
      justify-content: flex-start;
      /* Dense AuthShell so 1280×720 desktop smoke fits without vertical overflow. */
      padding-block: var(--space-micro, 0.25rem);
      gap: var(--space-micro, 0.25rem);
      min-height: 0;
    }
    [data-page="login"] [data-operator-login] .card-body {
      padding: var(--space-inline, 0.5rem);
      gap: var(--space-micro, 0.25rem);
    }
    [data-page="login"] [data-state-content] {
      gap: var(--space-micro, 0.25rem);
    }
    /* Non-success auth states stay out of layout when [hidden]. */
    [data-page="login"] [data-state]:not([data-state="success"])[hidden] {
      display: none;
    }
    [data-page="login"] [data-login-scanner-root] [data-login-nfc-scan],
    [data-page="login"] [data-login-scanner-root] [data-login-barcode-scan] {
      min-width: var(--touch-target-min, 2.75rem);
      min-height: var(--touch-target-min, 2.75rem);
      justify-content: center;
    }
    [data-page="login"] [data-login-scanner-root] [data-login-barcode-cancel] {
      width: 100%;
      min-height: var(--touch-target-min, 2.75rem);
      justify-content: center;
    }
    /*
     * Badge and PIN inputs take the HIG floor from the AuthShell
     * scannerJoinInput / pinInput tokens, which carry .bao-touch-target at
     * every width — measured 44px at 375px and 1280px with the join row flush.
     * A duplicate phone-scoped min-height here was a second owner of one
     * contract, and it also listed [data-operator-name], a field
     * pages-renderers-login.test.ts bans from the emitted markup. Only the
     * sticky submit is not an AuthShell input, so only it needs a floor here.
     */
    [data-page="login"] .bao-login-sticky-submit {
      min-height: var(--touch-target-min, 2.75rem);
    }
    /* Sticky primary CTA — short viewports keep Sign-in reachable. */
    [data-page="login"] .bao-login-submit-dock,
    [data-page="login"] .bao-login-sticky-submit {
      position: sticky;
      bottom: 0;
      z-index: 10;
    }
    [data-page="login"] .bao-login-submit-dock {
      background: var(--color-base-100);
      padding-block-start: var(--space-micro, 0.25rem);
      padding-block-end: max(var(--space-inline, 0.5rem), env(safe-area-inset-bottom, 0px));
    }
    /* Join scan toggles stay icon-first; cancel reveals as full-width soft. */
    [data-page="login"] [data-login-scanner-root] .join .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    [data-page="login"] [data-login-barcode-preview-shell]:not([hidden]) {
      margin-block-start: var(--space-micro, 0.25rem);
    }
    /*
     * Phone densify (≤47.99rem): demote credential helper copy to AT-only.
     * Placeholders + field legends carry the scan path (HIG density).
     */
    [data-page="login"] #operator-login-credential-hint {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    /*
     * On-screen PIN keypad — phone + tablet (max-width 64rem).
     * Touch-first enterprise badge stations use pad; desktop uses keyboard
     * password field only (base display:none outside this media).
     * Do not use HTML hidden for the pad (phone/tablet CSS owns visibility).
     */
    @media (max-width: 64rem) {
      [data-page="login"] [data-login-pin-pad].bao-login-pin-pad,
      [data-page="login"] [data-login-pin-pad] {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: var(--space-micro, 0.25rem);
        margin-block-start: var(--space-micro, 0.25rem);
      }
      [data-page="login"] .bao-login-pin-pad-spacer {
        display: block;
        min-height: var(--touch-target-min, 2.75rem);
      }
      [data-page="login"] [data-login-pin-pad] button {
        min-height: var(--touch-target-min, 2.75rem);
        justify-content: center;
        align-items: center;
        font-size: var(--text-lg, 1.125rem);
        font-weight: 600;
        line-height: 1;
        transition: transform var(--motion-duration-fast, 120ms) var(--motion-ease-standard, ease);
      }
      [data-page="login"] [data-login-pin-pad] button:active {
        transform: scale(0.96);
      }
      @media (prefers-reduced-motion: reduce) {
        [data-page="login"] [data-login-pin-pad] button {
          transition: none;
        }
        [data-page="login"] [data-login-pin-pad] button:active {
          transform: none;
        }
      }
      [data-page="login"] [data-login-pin-pad] button svg {
        width: 1.25rem;
        height: 1.25rem;
      }
    }

/* Collection phone: keep last row above fixed dock for reachability centres. */
@media (max-width: 47.99rem) {
  #app-main:has([data-page-type="collection"]),
  #app-main[data-main-page-type="collection"] {
    padding-block-end: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
    scroll-padding-bottom: calc(var(--dock-height) + var(--dock-safe-area-offset) + var(--touch-target-min, 2.75rem) + var(--space-section, 0.75rem));
  }
}

    /* Chat context: horizontal chip rail, keep ≥44px touch. */
    [data-page="chat"] [data-chat-context],
    [data-chat-context] {
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      align-items: center;
      gap: var(--space-micro);
      overflow-x: auto;
      overscroll-behavior-x: contain;
      -webkit-overflow-scrolling: touch;
    }
    /*
     * Context-more dropdown escape hatch (same layer, later position): the
     * chip rail's overflow-y clip amputates the open menu on phone widths
     * and the strip's backdrop-filter traps its stacking context under the
     * message thread. Unclip and lift only while the menu is open.
     */
    [data-page="chat"] [data-chat-context]:has([data-chat-context-more][open]),
    [data-chat-context]:has([data-chat-context-more][open]) {
      overflow-x: visible;
      overflow-y: visible;
      z-index: var(--z-dropdown, 1250);
    }
    [data-page="chat"] [data-chat-context] button,
    [data-chat-context] button {
      min-height: var(--touch-target-min, 2.75rem);
    }
    /*
     * Collection FilterBar densify — 2-col phone grid (Apple dense filters).
     * Search + action row span full width; status/priority pair side-by-side.
     * Replaces column stack that burned 3+ viewport rows on cases/billing.
     */
    [data-page-type="collection"] [data-primitive="FilterBar"],
    [data-page="cases"] [data-case-filters],
    [data-case-filters],
    [data-primitive="FilterBar"],
    [data-filter-bar] {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      justify-content: stretch;
      height: auto;
      min-height: 0;
    }
    [data-case-filters] fieldset,
    [data-case-filters] .fieldset,
    [data-primitive="FilterBar"] fieldset,
    [data-primitive="FilterBar"] .fieldset,
    [data-filter-bar] fieldset,
    [data-filter-bar] .fieldset {
      width: 100%;
      max-width: none;
      min-height: 0;
      height: auto;
      align-self: stretch;
    }
    [data-case-filters] fieldset:has(input[type="search"]),
    [data-case-filters] .fieldset:has(input[type="search"]),
    [data-case-filters] fieldset:has([data-case-search]),
    [data-primitive="FilterBar"] fieldset:has(input[type="search"]),
    [data-primitive="FilterBar"] .fieldset:has(input[type="search"]),
    [data-primitive="FilterBar"] fieldset:has([data-filter-search]),
    [data-filter-bar] fieldset:has(input[type="search"]),
    /* Actions row only — fieldsets also use class "flex"; never select them. */
    [data-case-filters] > [data-filter-actions],
    [data-primitive="FilterBar"] > [data-filter-actions],
    [data-case-filters] > div.flex.shrink-0,
    [data-primitive="FilterBar"] > div.flex.shrink-0,
    [data-case-filters] > div.flex.items-center,
    [data-primitive="FilterBar"] > div.flex.items-center {
      grid-column: 1 / -1;
    }
    [data-case-filters] .select,
    [data-case-filters] .input,
    [data-primitive="FilterBar"] .select,
    [data-primitive="FilterBar"] .input {
      width: 100%;
      min-height: var(--touch-target-min, 2.75rem);
    }
    [data-case-filters] .btn,
    [data-primitive="FilterBar"] .btn {
      min-height: var(--touch-target-min, 2.75rem);
      justify-content: center;
    }
    /* Reset/clear stays full-width only when alone in the actions row. */
    [data-case-filters] > .flex .btn,
    [data-primitive="FilterBar"] > .flex .btn {
      width: auto;
      min-width: var(--touch-target-min, 2.75rem);
    }
    /*
     * Training jobs chrome: stack status then full-width primary CTA.
     * Prevents status | (refresh over submit) L-shape crush on phone.
     */
    [data-training-jobs-table] [data-training-jobs-chrome] {
      display: flex;
      flex-direction: column;
      align-items: stretch;
      gap: var(--space-micro, 0.25rem);
    }
    [data-training-jobs-table] [data-training-jobs-actions] {
      display: flex;
      flex-direction: column;
      align-items: stretch;
      width: 100%;
      gap: var(--space-micro, 0.25rem);
    }
    [data-training-jobs-table] [data-training-jobs-actions] .btn-primary {
      width: 100%;
      justify-content: center;
      order: 2;
    }
    [data-training-jobs-table] [data-training-jobs-actions] .btn-soft.btn-square {
      align-self: flex-end;
      order: 1;
    }
    /* Capture context: 2-up; chat context stays the horizontal rail above. */
    [data-capture-context-strip] {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: var(--space-micro);
    }
    [data-capture-context-strip] button,
    [data-capture-context-strip] [data-capture-context-button] {
      min-height: var(--touch-target-min, 2.75rem);
      max-width: 11rem;
    }
    /*
     * Editor/capture context badges — long image UUIDs single-line ellipsis
     * (never multi-line wrap blowout on phone chips).
     */


    /* Topbar context chips: single owner is tokens-glass-shell-context-density.ts. */
    /*
     * Collection pages (models, licensing, specimens, …): WebView 101 lacks
     * :has() — force single-column panels and full-width form controls.
     * Never fork per data-page id.
     */
    [data-page-type="collection"] {
      max-width: 100%;
      overflow-x: clip;
    }
    [data-page-type="collection"] .card-body,
    [data-page-type="collection"] .bao-data-panel,
    [data-page-type="collection"] [data-primitive="DataTable"],
    [data-page-type="collection"] .overflow-x-auto {
      max-width: 100%;
      min-width: 0;
    }
    [data-page-type="collection"] table {
      max-width: 100%;
    }
    [data-page-type="collection"] [role="tab"].tab,
    [data-page-type="collection"] [data-lifecycle-tab] {
      min-height: var(--touch-target-min, 2.75rem);
      height: auto;
    }
    [data-page-type="collection"] form .input,
    [data-page-type="collection"] form .select,
    [data-page-type="collection"] form .btn {
      width: 100%;
      min-height: var(--touch-target-min, 2.75rem);
    }
    [data-page="specimens"] [data-specimen-create-form] .btn {
      width: auto;
      min-width: var(--touch-target-min, 2.75rem);
    }
    /* Pagination join controls always ≥44px on phone. */
    [data-pagination] .btn,
    .join .btn.join-item {
      min-height: var(--touch-target-min, 2.75rem);
      min-width: var(--touch-target-min, 2.75rem);
    }
    /*
     * Collection sticky toolbars (evidence, models, …): icon-first density.
     * Soft actions stay a horizontal rail; primary CTAs drop to a full-width
     * second row so long verbs (e.g. Generate pathologist report) never clip
     * mid-word against the dock edge (Apple HIG — essentials fully labeled).
     */
    [data-page-type="collection"] [role="toolbar"] {
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
      align-items: center;
      justify-content: flex-start;
      gap: var(--space-micro);
      overflow-x: auto;
      overscroll-behavior-x: contain;
      -webkit-overflow-scrolling: touch;
      max-width: 100%;
    }
    [data-page-type="collection"] [role="toolbar"] .btn {
      flex: 0 0 auto;
      min-height: var(--touch-target-min, 2.75rem);
      min-width: var(--touch-target-min, 2.75rem);
    }
    /* Why-tip hosts wrap disabled primaries — the host is the flex item. */
    [data-page-type="collection"] [role="toolbar"] > .btn-primary,
    [data-page-type="collection"] [role="toolbar"] > [data-disabled-tip-host]:has(.btn-primary),
    [data-page-type="collection"] [role="toolbar"] > span:has(> .btn-primary),
    [data-page-type="collection"] [role="toolbar"] > span:has(> button.btn-primary) {
      flex: 1 0 100%;
      order: 20;
      max-width: 100%;
      width: 100%;
    }
    [data-page-type="collection"] [role="toolbar"] .btn-primary {
      justify-content: center;
      width: 100%;
      max-width: 100%;
    }
    [data-page-type="collection"] [role="toolbar"] .btn-primary .bao-toolbar-label {
      display: inline-block;
      max-width: none;
      margin-inline-start: var(--space-micro, 0.25rem);
      opacity: 1;
      overflow: visible;
      white-space: nowrap;
    }
    /* Mobile card rows: kill desktop registry column widths that crush labels. */

    .bao-registry-table thead {
      display: none;
    }
    .bao-registry-table tbody,
    .bao-registry-table tr,
    .bao-registry-table td {
      width: auto;
      max-width: none;
      white-space: normal;
    }
    .bao-registry-table tr[data-state="success"],
    .bao-registry-table tbody > tr {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      gap: 0;
    }
    /* Primary stacked fields only — demotable columns (captures/updated)
       stay display:none from table-responsive densify; never resurrect them
       as flex rows (phone case cards grew Captures+Updated grey chrome). */
    .bao-registry-table td:not(.bao-table-column-demotable) {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: var(--space-inline);
      padding-block: var(--space-micro);
    }
    .bao-registry-table td.bao-table-column-demotable {
      display: none;
    }
    .bao-registry-table td > span:first-child {
      flex: 0 0 auto;
      max-width: 40%;
    }
    .bao-registry-table td > span:last-child:not(.bao-table-identity-value):not(.bao-table-row-actions) {

      flex: 1 1 auto;
      min-width: 0;
      text-align: end;
      overflow-wrap: anywhere;
    }
    /* Identity stacks (name + mono id): start-align on phone densify cards. */
    .bao-registry-table td > span.bao-table-identity-value {
      flex: 1 1 auto;
      min-width: 0;
      max-width: 100%;
      text-align: start;
      overflow-wrap: anywhere;
      align-items: flex-start;
    }
  }/*
   * Touch SSOT — header actions + settings tabs must meet 44px on phone /
   * BlueStacks coarse pointers (btn-sm alone is ~32–36px).
   */
  @media (max-width: 47.99rem),
(pointer: coarse) {[data-page-header-actions] .btn,
[data-page-header-actions] a.btn,
[data-settings-tab].tab,
[data-settings-tab].bao-settings-tab,
.tab.bao-settings-tab,
.bao-session-mode-tab.tab,
[data-session-mode-tab].tab,
[data-capture-context-strip] button,
[data-chat-context] button,
.bao-context-card {
      min-height: var(--touch-target-min, 2.75rem);
      height: auto;
      min-width: var(--touch-target-min, 2.75rem);
    }
    /*
     * Icon-first density (Apple HIG phone chrome) — hide verb captions under
     * toolbar actions. Single owner for: shell topbar, page headers, chat
     * composer, capture action strips, editor chrome toolbars. Never
     * per-page forks.
     * Page-header soft/primary clinical CTAs (finish case, generate report,
     * new case) keep permanent verbs — touch has no hover to reveal them
     * (design.md §6); the labeled reveal lives in glass-shell-page-header.
     *
     * Clipped, NOT display:none. The caption is the only accessible name
     * these controls have — the glyph is aria-hidden and S123 (one
     * accessible name) forbids also stamping aria-label, so removing the
     * caption from the box tree removes the name outright. Proven by the
     * rendered-tree audit: axe reported button-name critical on
     * /editor@phone for the tool-filter buttons, whose captions this rule
     * collapsed. sr-only clipping keeps them silent to sighted users and
     * intact for assistive tech.
     *
     * The editor dock is NOT in this list. It declares its own label density
     * through data-toolbar-density, which tokens-glass-chrome-body.ts owns;* clipping it here made this file a second owner of that pixel and the
     * dock's declaration meaningless. Density is the contract — a surface that
     * asks for labels gets labels.
     */
    [data-chat-composer] .bao-toolbar-action .bao-toolbar-label,
#app-header .bao-toolbar-action .bao-toolbar-label,
[data-page-header-actions] .bao-toolbar-action:not(.btn-primary):not(.btn-soft) .bao-toolbar-label,
[data-page-header-actions] .btn:not(.btn-primary):not(.btn-soft) .bao-toolbar-label,
[data-capture-action-bar] .bao-toolbar-action .bao-toolbar-label,
[data-primitive="ExportBar"] .bao-toolbar-action .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }
    /*
     * Topbar densify — hide brand detail + long status chrome;keep icon
     * actions at 44px. Context chips stay horizontal scroll (not 5-up crush).
     */
    .bao-shell-brand-detail,
.navbar .bao-brand-detail,
#app-header .bao-brand-detail {
      display: none;
    }#app-header .navbar-end .btn,
#app-header .navbar-end button.btn,
#app-header .navbar-end .bao-shell-status-badge,
#app-header .navbar-end [data-registry-status],
[data-page-header-actions] .btn,
[data-page-header-actions] a.btn {
      min-height: var(--touch-target-min, 2.75rem);
      min-width: var(--touch-target-min, 2.75rem);
    }
    /* Status badges: label is sr-only on phone;keep HIG 44px hit target. */
    #app-header .bao-shell-status-badge [data-status-label],
#app-header [data-registry-status] [data-status-label] {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
    /* Square primary CTAs on phone when caption is hidden (finish/new case). */
    [data-page-header-actions] .btn:not(.btn-square):has(> svg):has(> .bao-toolbar-label) {
      padding-inline: var(--toolbar-action-pad-inline, 0.625rem);
    }
    /* Dock: short labels only; very narrow phones can still read 4–5 glyphs. */
    .bao-mobile-dock .dock-label {
      max-width: var(--dock-label-max-width, 4.5rem);
    }/*
     * Collection dual-panel forms (licensing credentials | serials,
models
     * lifecycle | discover): one full-width column — never half-width crush.
     * Reinforces page-shell-surfaces without per-page forks.
     */
    [data-page-type="collection"] .bao-grid-reactive-panels {
      grid-template-columns: minmax(0, 1fr);
    }
    [data-page-type="collection"] .bao-grid-reactive-panels > * {
      grid-column: 1 / -1;
      max-width: none;
      min-width: 0;
    }
    /*
     * FilterBar phone densify — 2-col for select/status filters;search +
     * actions span full width. Beats max-sm:flex-col stack that wasted 3+
     * viewport rows on cases/billing/models (Apple dense filter pattern).
     * Specificity must beat utility flex class on the same host (no !important).
     */
    #app-main [data-primitive="FilterBar"],
#app-main div[data-primitive="FilterBar"] {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: end;
      gap: var(--space-micro, 0.25rem);
    }#app-main [data-primitive="FilterBar"] > .fieldset,
#app-main [data-primitive="FilterBar"] > fieldset {
      min-width: 0;
      width: 100%;
    }#app-main [data-primitive="FilterBar"] > .fieldset:has(input[type="search"]),
#app-main [data-primitive="FilterBar"] > fieldset:has(input[type="search"]),
#app-main [data-primitive="FilterBar"] > .fieldset:has([data-filter-search]),
#app-main [data-primitive="FilterBar"] > fieldset:has([data-filter-search]),
#app-main [data-primitive="FilterBar"] > .fieldset:has([data-case-search]),
#app-main [data-primitive="FilterBar"] > [data-filter-actions],
#app-main [data-primitive="FilterBar"] > div.flex.shrink-0,
#app-main [data-primitive="FilterBar"] > div.flex.items-center {
      grid-column: 1 / -1;
    }/*
     * Settings tabs: labels always painted (Apple vertical list on phone).
     * Icon-only hide was killing scannability for 11 destinations.
     */
    [data-settings-tab].tab,
.bao-settings-tab.tab {
      min-height: var(--touch-target-min, 2.75rem);
    }/*
     * Form field helpers restate the label/placeholder on phone — hide the
     * visual row,
keep aria-describedby for AT (sr-only geometry).
     */
    #app-main [data-field-helper] {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border-width: 0;
    }/*
     * Empty AI overlay hosts must not reserve a tall gray slab on phone —
     * empty copy + border is enough (Apple form density).
     */
    #app-main [data-ai-overlays-empty],
#app-main [data-ai-overlays-empty] .bao-min-h-panel-body,
#app-main [data-page="ai-analysis"] [data-primitive="VisualizationLayers"][data-state="empty"],
#app-main [data-page="ai-analysis"] [data-primitive="VisualizationLayers"] .bao-min-h-panel-body {
      min-height: 0;
    }
  }/*
   * FilterBar legends: the selected value is meant to carry the cue,
and
   * uppercase legend rows double the vertical cost of every select on
   * models/cases. Markup is <label.fieldset><span.fieldset-legend>… (daisyUI 5).
   *
   * Width-only on purpose. This rule previously rode the
   * (max-width: 47.99rem),
(pointer: coarse) block above,
so a touch tablet at
   * 1024px+ lost its legends despite having room for them — and the "value
   * carries the cue" premise fails there anyway,
because status and priority
   * both default to the same "All" text,
leaving two identical unlabeled
   * selects. Vertical cost is a function of width,
not of pointer type.
   */
  @media (max-width: 47.99rem) {#app-main [data-primitive="FilterBar"] .fieldset-legend,
#app-main [data-primitive="FilterBar"] .fieldset > legend,
#app-main [data-filter-bar] .fieldset-legend,
#app-main [data-filter-bar] .fieldset > legend {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border-width: 0;
    }
  }

/*
 * Editor compact transformation must stay UNLAYERED for the same reason as
 * the registry surfaces above: the canvas region carries daisyUI utility
 * classes (rounded-box, border-*), and a cascade layer loses to them no
 * matter how specific the selector is.
 */
@media (max-width: 47.99rem) {
  [data-page-type="media"] .bao-editor-canvas-column {
    min-block-size: 0;
  }
  [data-page-type="media"] .bao-editor-canvas.bao-editor-canvas-host-bounded {
    block-size: auto;
    max-block-size: none;
    max-height: none;
    border-radius: 0;
    border-inline-width: 0;
  }
}

[data-editor-3d-controls] {
  inset-inline: var(--space-micro, 0.25rem);
  inset-block-end: var(--space-micro, 0.25rem);
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: inline proximity;
  scrollbar-width: none;
  touch-action: pan-x;
}
[data-editor-3d-controls]::-webkit-scrollbar {
  display: none;
}
[data-editor-3d-controls] > button {
  flex: 0 0 var(--touch-target-min, 2.75rem);
  scroll-snap-align: start;
}
#app-main [data-editor-3d-controls] button[aria-label] .bao-toolbar-label {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
#app-main [data-editor-3d-controls] button[aria-label] {
  inline-size: var(--touch-target-min, 2.75rem);
  min-inline-size: var(--touch-target-min, 2.75rem);
  flex-basis: var(--touch-target-min, 2.75rem);
  padding-inline: 0;
}


  @media (max-width: 47.99rem) {
    .bao-editor-canvas-column > [data-editor-toolbar],
    .bao-editor-floating-toolbar {
      overflow-x: hidden;
    }
    [data-editor-tool-scroll] {
      display: flex;
      min-width: 0;
      flex: 1 1 auto;
      align-items: center;
      gap: var(--space-micro, 0.25rem);
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      touch-action: pan-x;
    }
    [data-editor-tool-scroll]::-webkit-scrollbar {
      display: none;
    }
    [data-editor-toolbar] .bao-toolbar-label {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }
    [data-editor-toolbar] > .bao-min-w-toolbar-spacer {
      display: none;
    }
    .bao-editor-canvas-column > [data-editor-toolbar] > [data-editor-more-tools],
    .bao-editor-canvas-column > [data-editor-toolbar] > [data-undo],
    .bao-editor-canvas-column > [data-editor-toolbar] > [data-redo],
    .bao-editor-floating-toolbar > [data-editor-more-tools],
    .bao-editor-floating-toolbar > [data-undo],
    .bao-editor-floating-toolbar > [data-redo] {
      flex: 0 0 auto;
    }
  }


@container (max-width: 48rem) {
  .bao-editor-workbench[data-editor-open-surface="inspector"] > [data-editor-canvas-overlays] {
    z-index: calc(var(--z-canvas-overlay, 30) + 1);
  }
}

[data-editor-toolbar] [data-editor-toolbar-controls],
[data-editor-toolbar] [data-tool-group-label] {
  display: none;
}

[data-editor-toolbar] [data-tool]:not([aria-pressed="true"]) {
  display: none;
}

[data-editor-toolbar]:not(:has([data-tool][aria-pressed="true"])) [data-tool-group="select"] [data-tool]:first-of-type {
  display: inline-flex;
}

[data-editor-toolbar] [data-tool-group][hidden] {
  display: none;
}

[data-editor-toolbar] [data-editor-more-tools] {
  display: inline-flex;
}

[data-editor-tool-catalog] {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--space-inline, 0.5rem);
}

[data-editor-tool-catalog] [data-editor-toolbar-controls] {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-micro, 0.25rem);
}

[data-editor-tool-catalog] [data-editor-toolbar-controls] > label {
  flex: 1 1 14rem;
  min-width: 0;
}

[data-editor-tool-catalog] [data-editor-toolbar-controls] > fieldset {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  gap: var(--space-micro, 0.25rem);
}

[data-editor-tool-catalog] [data-tool-group]:not([data-tool]) {
  display: grid;
  min-width: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  align-items: start;
  gap: var(--space-micro, 0.25rem);
}

[data-editor-tool-catalog] [data-tool-group][hidden] {
  display: none;
}

[data-editor-tool-catalog] [data-tool-group-label] {
  grid-column: 1 / -1;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
}

[data-editor-tool-catalog] [data-tool] {
  width: 100%;
  min-width: 0;
  justify-content: flex-start;
  gap: var(--space-inline, 0.5rem);
  padding-inline: var(--space-inline, 0.5rem);
}

#app-main [data-editor-tool-catalog] .bao-toolbar-action.btn-square .bao-toolbar-label {
  display: inline-block;
  max-width: var(--toolbar-label-reveal-max-width, 12rem);
  margin-inline-start: var(--space-micro, 0.25rem);
  opacity: 1;
  overflow: hidden;
  white-space: nowrap;
}

@media (max-width: 47.99rem) {
  [data-editor-tool-catalog] [data-editor-toolbar-controls] > label {
    flex-basis: 100%;
  }

  [data-editor-tool-catalog] [data-editor-toolbar-controls] > fieldset {
    flex-wrap: nowrap;
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    touch-action: pan-x;
  }

  [data-editor-tool-catalog] [data-editor-toolbar-controls] > fieldset::-webkit-scrollbar {
    display: none;
  }

  #app-main [data-editor-tool-catalog] [data-editor-tool-filter] .bao-toolbar-label {
    position: static;
    display: inline;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    white-space: nowrap;
  }

  [data-editor-tool-catalog] [data-tool-group]:not([data-tool]) {
    grid-template-columns: minmax(0, 1fr);
  }
}

  [data-editor-tool-catalog] fieldset[data-tool-group]:not([data-tool]) {
    inline-size: 100%;
    min-inline-size: 0;
    margin: 0;
    padding: 0;
    border: 0;
  }

  [data-tool-group="select"] { --editor-tool-tone: var(--color-neutral); }
  [data-tool-group="draw"] { --editor-tool-tone: var(--color-primary); }
  [data-tool-group="measure"] { --editor-tool-tone: var(--color-info); }
  [data-tool-group="assist"] { --editor-tool-tone: var(--color-secondary); }
  [data-tool-group="review"] { --editor-tool-tone: var(--color-accent); }
  [data-tool-group="redact"] { --editor-tool-tone: var(--color-error); }

  [data-editor-toolbar] [data-tool-group][data-tool],
  [data-editor-tool-catalog] [data-tool-group][data-tool] {
    color: color-mix(in oklab, var(--editor-tool-tone) 78%, var(--color-base-content));
    border-color: color-mix(in oklab, var(--editor-tool-tone) 30%, var(--liquid-border-clear));
    background-color: color-mix(in oklab, var(--editor-tool-tone) 8%, transparent);
    box-shadow: inset 0 1px 0 var(--glass-highlight-soft);
  }

  [data-editor-toolbar] [data-tool-group][data-tool][aria-pressed="true"],
  [data-editor-tool-catalog] [data-tool-group][data-tool][aria-pressed="true"] {
    color: color-mix(in oklab, var(--editor-tool-tone) 88%, var(--color-base-content));
    border-color: color-mix(in oklab, var(--editor-tool-tone) 62%, transparent);
    background-color: color-mix(in oklab, var(--editor-tool-tone) 20%, transparent);
    box-shadow: inset 0 1px 0 var(--glass-highlight-strong), 0 0 0 2px color-mix(in oklab, var(--editor-tool-tone) 38%, transparent);
  }

  @media (forced-colors: active) {
    [data-editor-toolbar] [data-tool-group][data-tool],
    [data-editor-tool-catalog] [data-tool-group][data-tool] {
      border-color: ButtonText;
      color: ButtonText;
    }
  }

  #app-main [data-editor-tool-catalog] .btn.bao-toolbar-action.btn-square[data-tool] {
    width: 100%;
    min-width: 0;
    justify-content: flex-start;
    gap: var(--space-inline, 0.5rem);
    padding-inline: var(--space-inline, 0.5rem);
  }

  @media (min-width: 48rem) {
    #app-main [data-editor-toolbar] [data-editor-more-tools] {
      display: inline-flex;
    }
  }
[data-editor-tool-palette-dialog] [data-editor-tool-catalog] > [data-editor-toolbar-controls] {
  flex: 0 0 auto;
  align-items: stretch;
}

[data-editor-tool-palette-dialog] [data-editor-tool-catalog] > [data-editor-toolbar-controls] > label {
  min-block-size: var(--touch-target-min, 2.75rem);
}
@media (min-width: 48rem) and (max-height: 32rem) {
  [data-page-type="media"] .bao-media-page-root {
    position: relative;
    gap: 0;
  }

  [data-page-type="media"] .bao-media-page-root > [data-primitive="PageHeader"] {
    position: absolute;
    inset-block-start: 0;
    inset-inline-end: var(--page-gutter-inline, var(--space-section));
    z-index: var(--z-canvas-overlay);
    width: auto;
    min-width: 0;
    padding-block-end: 0;
    border-block-end: 0;
  }

  [data-page-type="media"] [data-editor-context-bar] {
    display: none;
  }

  [data-page-type="media"] [data-session-mode-tabs]:not([data-session-rail-mode-tabs]) {
    position: relative;
    inset: auto;
    z-index: auto;
  }

  [data-page-type="media"] .bao-editor-workbench,
  [data-page-type="media"] .bao-editor-workbench > .bao-editor-canvas-column,
  [data-page-type="media"] .bao-editor-canvas {
    min-height: 0;
    max-height: 100%;
  }

  [data-page-type="media"] .bao-editor-canvas-column > [data-editor-toolbar] {
    max-width: calc(50% - var(--space-micro));
  }

  [data-page-type="media"] [data-editor-toolbar] .bao-toolbar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  [data-page-type="media"] .bao-editor-canvas-column > [data-editor-filmstrip-disclosure] > [data-editor-filmstrip] {
    position: absolute;
    inset-block-start: 0;
    inset-inline-end: 0;
    z-index: var(--z-canvas-overlay);
    inline-size: calc(50% - var(--space-micro));
    margin: 0;
  }
}

[data-editor-tool-palette-dialog] [data-editor-tool-catalog] {
  overscroll-behavior-y: contain;
}

@media (min-width: 48rem) {
  [data-editor-tool-palette-dialog] .bao-modal-body-scroll {
    max-block-size: var(--modal-scroll-max-height);
  }
}

.bao-editor-tool-palette-drag-handle {
  display: none;
}

@media (max-width: 47.99rem) {
  .bao-editor-tool-palette-drag-handle {
    display: block;
    width: var(--touch-target-min, 2.75rem);
    height: var(--space-micro, 0.25rem);
    margin: var(--space-micro, 0.25rem) auto 0;
    border-radius: var(--radius-full);
    background: color-mix(in oklab, var(--color-base-content) 30%, transparent);
    touch-action: pan-y;
  }

  [data-editor-tool-palette-dialog][data-editor-tool-palette-dismiss-pending] .modal-box {
    animation: bao-editor-tool-palette-dismiss var(--duration-fast, 120ms) var(--easing-standard) forwards;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-editor-tool-palette-dialog][data-editor-tool-palette-dismiss-pending] .modal-box {
    animation: none;
  }
}

@keyframes bao-editor-tool-palette-dismiss {
  to {
    transform: translateY(100%);
    opacity: 0;
  }
}


  [data-editor-tool-palette-dialog] [data-editor-tool-palette-close] {
    flex: 0 0 auto;
  }


  [data-editor-tool-palette-dialog] [data-review-mode-toggle] {
    flex: 0 0 auto;
  }

@media (max-width: 47.99rem) {
  [data-editor-tool-catalog] [data-editor-toolbar-controls] {
    align-items: stretch;
  }

  [data-editor-tool-catalog] [data-editor-toolbar-controls] [data-primitive="SearchField"] {
    flex-basis: 100%;
  }

  [data-editor-tool-catalog] [data-editor-toolbar-controls] > fieldset {
    flex-wrap: nowrap;
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
}


/*
 * Annotation is canvas-first. A persisted expanded navigation preference is
 * useful elsewhere, but it cannot consume the editor canvas at tablet width.
 *
 * Two scopes, one declaration set, because neither signal covers both cases:
 * the SSR body stamp is correct on first paint and in WebViews without :has(),
 * and the :has() branch keeps the rule live across an HTMX swap that replaces
 * the page without a document load. app-shell-page-type.ts re-stamps the body
 * attribute after each swap, so the two agree once script runs.
 */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned,
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned {
    grid-template-columns: var(--sidebar-collapsed) minmax(0, 1fr);
  }

  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .menu-title,
  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-expanded-only],
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .menu-title,
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-expanded-only] {
    display: none;
  }

  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item,
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item {
    justify-content: center;
    padding-inline: 0;
  }

  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::before,
  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::after,
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::before,
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::after {
    display: block;
  }

  body[data-shell-page="editor"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-rail-toggle],
  body:has(#app-main [data-page="editor"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-rail-toggle] {
    display: none;
  }
}

/*
 * The status HUD is passive: pointer events stay on the specimen, and none of
 * its chips are focus targets. It cannot own a scrollport. Tablet preserves
 * review, save, zoom, and AI provenance in flow while demoting draw telemetry.
 */
@media (min-width: 48rem) and (max-width: 64rem) {
  [data-page-type="media"] [data-editor-status-bar] {
    max-block-size: none;
    overflow: visible;
  }

  [data-page-type="media"] [data-editor-status-bar] [data-editor-status="cursor"],
  [data-page-type="media"] [data-editor-status-bar] [data-editor-status="input"],
  [data-page-type="media"] [data-editor-status-bar] [data-editor-status="calibration"],
  [data-page-type="media"] [data-editor-status-bar] [data-editor-status="layer"] {
    display: none;
  }
}
/* Capture annotate is canvas-first. At tablet width, a persisted expanded rail must yield its track to the canvas, just as the standalone editor does. SSR and live-swap scopes keep the rule effective in WebViews without :has() and after HTMX navigation. */
@media (min-width: 48rem) and (max-width: 79.99rem) {
  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned,
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned {
    grid-template-columns: var(--sidebar-collapsed) minmax(0, 1fr);
  }

  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .menu-title,
  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-expanded-only],
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .menu-title,
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-expanded-only] {
    display: none;
  }

  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item,
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item {
    justify-content: center;
    padding-inline: 0;
  }

  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::before,
  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::after,
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::before,
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar .bao-nav-item.tooltip::after {
    display: block;
  }

  body[data-shell-page="capture"] .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-rail-toggle],
  body:has(#app-main [data-page="capture"]) .bao-shell-drawer.bao-shell-sidebar-pinned #app-sidebar [data-sidebar-rail-toggle] {
    display: none;
  }
}
body[data-canvas-maximized="true"] #session-rail {
  display: none;
}

body[data-canvas-maximized="true"] [data-editor-chrome][data-annotation-ready="true"] [data-canvas-island][data-maximized="true"] {
  position: relative;
  inset: auto;
  z-index: auto;
  max-height: none;
  border-radius: var(--radius-box);
}

body[data-canvas-maximized="true"] [data-editor-chrome][data-annotation-ready="true"] [data-editor-toolbar] {
  position: static;
  inset: auto;
  z-index: auto;
  width: auto;
  max-width: 100%;
}

@container (max-width: 48rem) {
  [data-editor-stage-header] > div {
    flex-wrap: nowrap;
    overflow: hidden;
  }
  [data-editor-stage-header] [data-editor-stage-actions] {
    flex: 1 1 auto;
    flex-wrap: nowrap;
    justify-content: space-between;
    overflow: visible;
  }
  [data-editor-stage-title] {
    display: none;
  }
  [data-editor-context-bar] {
    flex: 1 1 auto;
    flex-wrap: nowrap;
    overflow: hidden;
  }
  [data-editor-context-bar] > .bao-context-badge:not(:first-child) {
    display: none;
  }
  [data-editor-context-bar] > .bao-context-badge:first-child {
    max-inline-size: min(10.5rem, 100%);
  }
  [data-editor-status-bar] {
    display: none;
  }
  .bao-editor-workbench > [data-editor-command-bar] {
    position: absolute;
    inline-size: 100%;
    max-inline-size: 100%;
    flex: 0 0 auto;
    flex-wrap: nowrap;
  }
  [data-editor-command-bar] > [data-editor-toolbar],
  [data-editor-command-bar] > [data-editor-save-form] {
    position: static;
    inset: auto;
    inline-size: auto;
    max-inline-size: none;
    flex: 0 0 auto;
  }
}

/*
 * Phone canvas priority is the final owner of phone-only editor visibility.
 * It follows the generalized compact HUD and toolbar rules so no earlier
 * layout concern can accidentally restore secondary chrome over the canvas.
 */
@media (max-width: 47.99rem) {
  [data-page-type="media"] [data-session-mode-tabs] {
    box-sizing: border-box;
    block-size: var(--touch-target-min, 2.75rem);
    min-block-size: var(--touch-target-min, 2.75rem);
    margin-block: 0;
    padding: 0;
    border: 0;
    background: transparent;
  }
  [data-page-type="media"] [data-session-mode-tabs] .bao-session-mode-tab {
    box-sizing: border-box;
    min-block-size: var(--touch-target-min, 2.75rem);
  }
  [data-page-type="media"] > [data-state-content] > [data-primitive="PageHeader"] {
    box-sizing: border-box;
    block-size: var(--touch-target-min, 2.75rem);
    min-block-size: var(--touch-target-min, 2.75rem);
    padding-block: 0;
  }
  [data-page-type="media"] [data-editor-stage-header] {
    min-block-size: var(--touch-target-min, 2.75rem);
  }
  [data-page-type="media"] [data-editor-stage-header] [data-editor-status-bar] [data-editor-status]:not([data-editor-status="zoom"]) {
    display: none;
  }
  [data-page-type="media"] [data-editor-stage-header] [data-editor-status-bar] [data-editor-status="zoom"] {
    display: flex;
  }
}

[data-editor-command-bar] > [data-inspector-toggle] {
  display: none;
}

@media (max-width: 47.99rem) {
  [data-page-type="media"] .bao-editor-workbench {
    --editor-command-bar-block-size: calc(var(--touch-target-min, 2.75rem) + 2 * var(--space-micro, 0.25rem) + 2px);
  }
  [data-page-type="media"] .bao-editor-workbench > [data-editor-command-bar] {
    position: absolute;
    inline-size: 100%;
    max-inline-size: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  [data-page-type="media"] .bao-editor-workbench > [data-editor-command-bar]::-webkit-scrollbar {
    display: none;
  }
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-save-form] {
    position: static;
    inset: auto;
    z-index: auto;
    inline-size: auto;
    min-inline-size: 0;
    max-inline-size: none;
    flex: 0 0 auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] .bao-toolbar-spacer,
  [data-page-type="media"] [data-editor-command-bar] > [data-tool-strip],
  [data-page-type="media"] [data-editor-command-bar] > [data-ai-suggestion-layer] {
    display: none;
  }
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] [data-editor-tool-scroll],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] [data-editor-more-tools],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] [data-undo],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] [data-redo],
  [data-page-type="media"] [data-editor-command-bar] > [data-inspector-toggle] {
    display: inline-flex;
  }
  [data-page-type="media"] [data-editor-command-bar] .bao-toolbar-label,
  [data-page-type="media"] [data-editor-command-bar] [data-editor-export-menu] summary > span {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-save-form] [data-disabled-tip-host],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-save-form] button[type="submit"] {
    inline-size: auto;
  }
}

@media (max-width: 47.99rem) {
  [data-page-type="media"] .bao-editor-workbench > [data-editor-command-bar],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar],
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-toolbar] > div,
  [data-page-type="media"] [data-editor-command-bar] > [data-editor-save-form] {
    gap: var(--space-2xs, 0.125rem);
  }
  [data-page-type="media"] .bao-editor-workbench > [data-editor-command-bar] {
    padding-inline: 0;
  }
  [data-page-type="media"] [data-editor-command-bar] fieldset:not(:has([data-tool]:not([hidden]))) {
    display: none;
  }
}

@media (max-width: 47.99rem) {
  [data-page-type="media"] [data-editor-stage-header] [data-viz-dimension-toggle] {
    flex: 0 0 var(--touch-target-min, 2.75rem);
    inline-size: var(--touch-target-min, 2.75rem);
    padding-inline: 0;
    font-size: var(--text-sm, 0.875rem);
  }
}

[data-viz-dimension-toggle] .swap-on,
[data-viz-dimension-toggle] .swap-off {
  inline-size: 100%;
  text-align: center;
}

@media (min-width: 48rem) and (max-height: 32rem) {
  [data-editor-canvas-region] {
    position: relative;
  }
  [data-editor-canvas-region] > .canvas-container {
    flex: 1 1 auto;
    min-block-size: 0;
    max-block-size: none;
  }
}

@media (min-width: 48rem) and (max-height: 32rem) {
  [data-editor-canvas-region] > [data-editor-stage-header],
  [data-editor-3d-stage-header] {
    min-block-size: var(--touch-target-min, 2.75rem);
    padding: var(--space-micro, 0.25rem);
  }
  [data-editor-canvas-region] > [data-editor-stage-header] [data-editor-stage-title],
  [data-editor-canvas-region] > [data-editor-stage-header] [data-viz-dimension-full],
  [data-editor-3d-stage-header] .bao-editor-3d-stage-label,
  [data-editor-3d-stage-header] [data-viz-dimension-full] {
    display: none;
  }
  [data-editor-canvas-region] > [data-editor-stage-header] [data-viz-dimension-compact],
  [data-editor-3d-stage-header] [data-viz-dimension-compact] {
    display: inline;
  }
  [data-editor-canvas-region] > [data-editor-stage-header] [data-editor-status-bar] {
    position: static;
    flex: 0 0 auto;
    max-block-size: var(--touch-target-min, 2.75rem);
    overflow: visible;
  }
  [data-editor-canvas-region] > [data-editor-stage-header] [data-editor-status]:not([data-editor-status="zoom"]) {
    display: none;
  }
}

[data-editor-tool-palette-dialog] .bao-dialog-chrome {
  background: transparent;
  border-block-end-color: var(--liquid-border-regular);
}


  @media (min-width: 48rem) {
    [data-editor-command-bar] {
      min-inline-size: 0;
      max-inline-size: 100%;
    }
    [data-editor-command-bar] > [data-editor-toolbar] {
      flex: 1 1 0%;
      width: auto;
      min-inline-size: 0;
      max-inline-size: 100%;
    }
    [data-editor-command-bar] > [data-editor-save-form] {
      flex: 0 1 auto;
      min-inline-size: 0;
      max-inline-size: 100%;
      margin-inline-start: auto;
    }
  }
  /* Keep every tablet command reachable by giving persistence its own row. */
  @media (min-width: 48rem) and (max-width: 64rem) {
    [data-page-type="media"] .bao-editor-workbench,
    [data-page="capture"] .bao-editor-workbench {
      padding-block-end: calc(2 * var(--touch-target-min, 2.75rem) + 4 * var(--space-micro, 0.25rem));
    }
    [data-page-type="media"] .bao-editor-workbench > [data-editor-command-bar],
    [data-page="capture"] .bao-editor-workbench > [data-editor-command-bar] {
      flex-wrap: wrap;
      overflow-x: hidden;
      row-gap: var(--space-micro, 0.25rem);
    }
    [data-page-type="media"] [data-editor-command-bar] > [data-editor-save-form],
    [data-page="capture"] [data-editor-command-bar] > [data-editor-save-form] {
      position: static;
      flex: 0 0 100%;
      justify-content: flex-end;
      max-inline-size: 100%;
    }
  }

/*
 * Runtime customizer host must stay UNLAYERED: the tray host carries daisyUI
 * classes (dropdown-content, menu) whose unlayered structure beats any
 * layered override regardless of specificity — the open tray shipped as a
 * 0x0 absolute shell from inside @layer utilities.
 */

/* ---- Runtime customizer host (unlayered structural geometry). ---- */
/* Header-anchored host (created at runtime next to the edit toggle). */
[data-dashboard-customizer-dropdown] {
  position: relative;
  display: inline-flex;
  z-index: var(--z-shell-topbar, 30);
}
[data-dashboard-customizer-dropdown]:not(.dropdown-open) > [data-dashboard-customizer-content] {
  display: none;
  visibility: hidden;
  pointer-events: none;
}
/*
 * Open tray geometry lives HERE, unlayered, at (0,3,0): daisyUI's
 * .dropdown .dropdown-content position:absolute is unlayered too and ties
 * lower specificity; the shared .bao-dropdown-panel-viewport rule cannot be
 * relied on for this host (no .bao-dropdown-panel base class). Anchor vars
 * come from the promote step (edit-toggle rect); absent vars mean the toggle
 * is collapsed and the compact sheet band below takes over.
 */
[data-dashboard-customizer-dropdown].dropdown-open > [data-dashboard-customizer-content] {
  display: flex;
  flex-direction: column;
  visibility: visible;
  pointer-events: auto;
  position: fixed;
  top: var(--bao-dropdown-top, auto);
  right: var(--bao-dropdown-right, var(--space-inline, 0.5rem));
  bottom: auto;
  left: auto;
  margin: 0;
  z-index: var(--z-dropdown, 1000);
  min-width: min(18rem, calc(100vw - 1.5rem));
  width: max-content;
  max-width: min(22rem, calc(100vw - 1.5rem));
  max-height: min(70vh, 28rem);
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
/*
 * §8.6 compact transformation: below the header-collapse band the customize
 * toggle is folded into the overflow menu (0x0 — nothing to anchor to), so
 * the open tray pins to the viewport bottom as a sheet.
 */
@media (max-width: 47.99rem) {
  [data-dashboard-customizer-dropdown].dropdown-open > [data-dashboard-customizer-content] {
    top: auto;
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-inline, 0.5rem));
    left: var(--space-inline, 0.5rem);
    right: var(--space-inline, 0.5rem);
    width: auto;
    max-width: none;
    max-height: min(70vh, var(--vv-height, 100dvh));
    z-index: var(--z-modal, 1300);
  }
}

/*
 * Native dialog visibility must stay UNLAYERED: Android WebView applies
 * daisyUI's layered .modal hidden state over a layered override, even when
 * the override is more specific. The dialog archive is kept whole here so
 * its collection/home rules and its open-state contract share one source.
 */

  /* Dialogs always sit above the shell drawer when both are open. */
  dialog.modal,
  dialog[open].modal {
    z-index: var(--z-modal, 1300);
  }
  /*
   * Closed dialogs must not paint or steal hits. daisyUI .modal uses display:grid
   * + pointer-events:none while closed; unlayered [hidden] must still win so a
   * pre-open dialog never sits over the shell as an invisible sheet.
   */
  dialog.modal[hidden]:not([open]),
  dialog.modal[hidden]:not(.modal-open) {
    display: none;
    pointer-events: none;
    visibility: hidden;
  }
  dialog.modal.modal-open,
  dialog.modal[open] {
    pointer-events: auto;
    visibility: visible;
  }
  /* Collection/home: single content track — header + primary surface, less chrome stacking. */
  [data-page-type="collection"] > [data-state-content],
  [data-page-type="home"] > [data-state-content] {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: var(--page-section-gap);
  }
  /* .bao-page-track structural SSOT lives in tokens-layout-framework.ts only. */
  /* Conversation column gutter (Happydumpling agent chat max-w-3xl track). */
  .bao-conversation-gutter {
    padding-inline: var(--page-gutter-inline);
  }
  [data-page-type="collection"] [data-primitive="PageHeader"],
  [data-page-type="home"] [data-primitive="PageHeader"] {
    margin-bottom: 0;
  }
  /* Toolbar under header: no second bordered island. Longhand color only —
     the background shorthand was wiping background-image and with it the
     bao-table-scroll-x scroll-edge cue on the same element.

     Sticky toolbars (.bao-sticky-toolbar, z-sticky) are excluded: once the
     bar pins over scrolling rows a transparent surface bleeds the table
     through the chrome (evidence Finish Case band read as overlapping the
     case-journey strip). Pinned chrome keeps its bg-base-100 plate. */
  [data-page-type="home"] > [data-state-content] > [role="toolbar"]:not(.bao-sticky-toolbar),
  [data-page-type="collection"] > [data-state-content] > [role="toolbar"]:not(.bao-sticky-toolbar) {
    border: 0;
    background-color: transparent;
    padding-inline: 0;
    padding-block: 0;
  }

/*
 * Tooltip geometry overrides must stay UNLAYERED: the daisyui cascade layer
 * is declared after utilities in tailwind-daisyui.css, so a layered override
 * loses to daisyUI's own tooltip rules (z-index: 2, max-width: 20rem) no
 * matter the specificity.
 */
/*
 * Tooltip geometry overrides — UNLAYERED on purpose: the daisyui layer
 * follows utilities in cascade-layer order,
so only unlayered author CSS
 * beats daisyUI's own .tooltip rules (z-index: 2,
max-width: 20rem).
 */
.tooltip > .tooltip-content,
.tooltip[data-tip]:before,
.tooltip:after {
  /* Toast-tier overlay: tips paint above sticky chrome and dropdowns. */
  z-index: var(--z-tooltip, 1400);
}.tooltip > .tooltip-content,
.tooltip[data-tip]:before {
  /* Viewport-aware cap: the tip never exceeds the viewport safe area. */
  max-width: min(var(--tooltip-max-width, 20rem), calc(100vw - (2 * var(--tooltip-viewport-inset, 0.75rem))));
  max-width: min(var(--tooltip-max-width, 20rem), calc(100dvw - (2 * var(--tooltip-viewport-inset, 0.75rem))));
}

