/* ==========================================================================
   Dapple HTML Starter: plain CSS using real design tokens.
   This file is the single source of truth for every token and component
   style in this system, hand-written, no build step, no framework.
   ========================================================================== */
/* dapple-design-system v1.1.1, styles.css hash: 581bad4c3aa7 (regenerate via html-starter/build-tokens.mjs --check) */


@font-face {
  font-family: "Geist";
  src: url("./fonts/Geist-300.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist";
  src: url("./fonts/Geist-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist";
  src: url("./fonts/Geist-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   Design Tokens (light mode)
   ========================================================================== */

:root {
  /* Core surfaces */
  --background: #FCFBFE;
  --foreground: #000000;
  --card: #FFFFFF;
  --card-foreground: #000000;
  --popover: #FFFFFF;
  --popover-foreground: #000000;

  /* Brand */
  --primary: #5C0FC9;
  --primary-foreground: #FFFFFF;
  --secondary: #EEEDE8;
  --secondary-foreground: #000000;
  --muted-surface: #EEEDE8;
  --muted-foreground: #6B7180;
  --accent: #F6F2FD;
  --accent-foreground: #000000;
  --destructive: #D6283A;
  --destructive-foreground: #FFFFFF;

  /* Borders and focus */
  --border: #DCDAD3;
  --border-violet: rgba(92, 15, 201, 0.18);
  --input: #FFFFFF;
  --ring: #5C0FC9;

  /* Violet ramp */
  --violet-deep: oklch(0.3312 0.1340 291.77);
  --violet-pressed: oklch(0.4228 0.2076 294.50);
  --violet: oklch(0.4404 0.2401 290.38);
  --violet-strong: oklch(0.5760 0.2170 297.35);
  --violet-medium: oklch(0.7109 0.1490 300.28);
  --violet-soft: oklch(0.8069 0.0949 300.96);
  --violet-tint: oklch(0.9175 0.0347 300.29);
  --violet-tint-2: oklch(0.9293 0.0367 304.78);
  --violet-tint-3: oklch(0.9674 0.0152 302.56);

  /* Business-area secondary families: accent / surface (40% tint) / text */
  --ba-executive-accent: #75BDE9;
  --ba-executive-surface: #B0DAF2;
  --ba-executive-text: #1F3D52;

  --ba-commercial-accent: #75BF65;
  --ba-commercial-surface: #AFDAA4;
  --ba-commercial-text: #2C521F;

  --ba-product-accent: #F48C69;
  --ba-product-surface: #F6C0A6;
  --ba-product-text: #5C2A14;

  --ba-infrastructure-accent: #3BB4C0;
  --ba-infrastructure-surface: #82CFD5;
  --ba-infrastructure-text: #17454A;

  --ba-customer-accent: #F183BF;
  --ba-customer-surface: #F5B8DA;
  --ba-customer-text: #5C1F42;

  --ba-corporate-accent: #F4BC40;
  --ba-corporate-surface: #F6D283;
  --ba-corporate-text: #5C4514;

  --ba-brand-accent: #8B4FE6;
  --ba-brand-surface: #CAB2F4;
  --ba-brand-text: #3A2073;

  /* Data-viz tier: categorical palette for charts, map layers, path legs,
     any data encoding that is NOT brand chrome. Aliases onto the
     documented series order (violet first, then the seven families), so
     the physical palette stays one set and every theme's values flow
     through automatically. Use these names in data contexts so a series
     never semantically claims a business area. Do not invent parallel
     per-project viz palettes. */
  --viz-1: var(--violet);
  --viz-2: var(--ba-executive-accent);
  --viz-3: var(--ba-commercial-accent);
  --viz-4: var(--ba-product-accent);
  --viz-5: var(--ba-infrastructure-accent);
  --viz-6: var(--ba-customer-accent);
  --viz-7: var(--ba-corporate-accent);
  --viz-8: var(--ba-brand-accent);

  /* Elevation scale: hairline border at rest, shadow reserved for hover/
     focus and for surfaces that sit above the page (popovers, dropdowns,
     modals). xs/sm are not yet used by any component in html-starter
     today, they're reserved for the lighter-weight overlay components
     (tooltip, chip, toast) the pattern layer adds next, keep them rather
     than re-adding them later. */
  --shadow-xs: 0 1px 0 0 rgba(11, 15, 43, 0.04);
  --shadow-sm: 0 1px 2px rgba(11, 15, 43, 0.06), 0 1px 0 rgba(11, 15, 43, 0.03);
  --shadow-md: 0 4px 12px rgba(11, 15, 43, 0.06), 0 1px 2px rgba(11, 15, 43, 0.04);
  --shadow-lg: 0 12px 32px rgba(11, 15, 43, 0.10), 0 2px 6px rgba(11, 15, 43, 0.04);
  --shadow-focus: 0 0 0 3px rgba(92, 15, 201, 0.32);

  /* Radius scale. --radius-xl (modals, large panels) has no consumer yet
     in html-starter, reserved for the generic modal/dialog component the
     pattern layer adds next. */
  --radius-xs: 3px;
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 999px;

  /* Motion. --dur-1/--ease-out have no consumer yet in html-starter,
     reserved for the fast state-flip and exit/dismiss timing the
     upcoming .menu primitive and toast pattern need. */
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;

  /* Theme-invariant constants: values that stay FIXED regardless of the
     active theme (a scrim, a pin's white ring, a polyline casing under
     colored routes). The pattern: define once here in :root, never
     redefine in a theme block, comment what the value anchors. Apps add
     their own invariants the same way (e.g. --marker-ring,
     --route-casing), that is sanctioned, not a gotcha-12 deviation. */
  --overlay-backdrop: rgba(11, 15, 43, 0.45); /* scrim behind drawer/modal/inspector */

  /* Fluid display type scale — reproduced from dapple.co production values */
  --text-hero: clamp(34px, 4.4vw, 58px);
  --text-hero--line-height: 1.08;
  --text-hero--letter-spacing: -0.05em;

  --text-statement: clamp(40px, 5.2vw, 72px);
  --text-statement--line-height: 1.05;
  --text-statement--letter-spacing: -0.075em;

  --text-feature: clamp(22px, 2.5vw, 30px);
  --text-feature--line-height: 1.2;
  --text-feature--letter-spacing: -0.02em;

  --text-hero-subtitle: clamp(20px, 2vw, 26px);
  --text-hero-subtitle--line-height: 1.35;
  --text-hero-subtitle--letter-spacing: -0.02em;

  --text-callout: clamp(17px, 1.65vw, 22px);
  --text-callout--line-height: 1.55;
  --text-callout--letter-spacing: -0.01em;

  --text-display-body: clamp(16px, 1.2vw, 18px);
  --text-display-body--line-height: 1.7;
  --text-display-body--letter-spacing: -0.01em;

  /* Font stack */
  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Breakpoints. phone = base (<640) · tablet = 768+ · desktop = 1024+.
     Named for reference in comments/docs, plain CSS has no way to plug a
     custom property into a @media query condition, so these describe the
     same numbers @media (min-width: ...) rules below use directly. */
  --breakpoint-sm: 40rem;  /* 640px */
  --breakpoint-md: 48rem;  /* 768px */
  --breakpoint-lg: 64rem;  /* 1024px */
  --breakpoint-xl: 80rem;  /* 1280px */
  --breakpoint-2xl: 96rem; /* 1536px */

  /* Page shell and prose width caps. */
  --container-dapple: 80rem;      /* 1280px, .container's max-width */
  --container-dapple-text: 42rem; /* 672px, prose measure, ~70ch at 17px */

  /* Spacing scale. Fixed rem steps for gaps, padding, and margins.
     Negative steps exist for pulling a child past its parent's edge
     (e.g. an icon overlapping a card corner), not for typical layout. */
  --space-000: -0.25rem;
  --space-00: -0.125rem;
  --space-1: 0.125rem;
  --space-2: 0.25rem;
  --space-3: 0.5rem;
  --space-4: 0.75rem;
  --space-5: 1rem;
  --space-6: 1.25rem;
  --space-7: 1.5rem;
  --space-8: 1.75rem;
  --space-9: 2rem;
  --space-10: 2.5rem;
  --space-11: 3rem;
  --space-12: 4rem;
  --space-13: 4.5rem;
  --space-14: 5rem;
  --space-15: 6rem;

  /* Fluid spacing. Shrinks smoothly between the two bounds instead of
     jumping at a breakpoint, for page-level gutters and section gaps
     that should scale with viewport width rather than snap. */
  --space-fluid-1: clamp(0.5rem, 1vw + 0.35rem, 1rem);
  --space-fluid-2: clamp(1rem, 2vw + 0.5rem, 1.5rem);
  --space-fluid-3: clamp(1.5rem, 3vw + 0.75rem, 2.5rem);
  --space-fluid-4: clamp(2rem, 5vw + 1rem, 4rem);
  --space-fluid-5: clamp(3rem, 8vw + 1.5rem, 6rem);
}

/* ==========================================================================
   Dark Mode
   ========================================================================== */

[data-theme="dark"] {
  --background: #0B0F2B;
  --foreground: #FFFFFF;
  --card: #1A1F3A;
  --card-foreground: #FFFFFF;
  --popover: #1F2540;
  --popover-foreground: #FFFFFF;

  --primary: #F5F3FF;
  --primary-foreground: #0B0F2B;
  --secondary: #1F2540;
  --secondary-foreground: #FFFFFF;
  --muted-surface: #1A1F3A;
  --muted-foreground: #9A9FC0;
  --accent: #2D2A45;
  --accent-foreground: #FFFFFF;
  --destructive: #D6283A;
  --destructive-foreground: #FFFFFF;

  --border: #2D3245;
  --border-violet: rgba(139, 79, 230, 0.22);
  --input: #141829;
  --ring: #8B4FE6;

  --violet-soft: oklch(0.3300 0.0700 300.96);
  --violet-tint: oklch(0.2800 0.0550 300.29);
  --violet-tint-2: oklch(0.2500 0.0500 304.78);
  --violet-tint-3: oklch(0.2200 0.0350 302.56);

  --ba-executive-surface: #062C40;
  --ba-executive-text: #A0CBE6;

  --ba-commercial-surface: #192F14;
  --ba-commercial-text: #B0CEAA;

  --ba-product-surface: #3F1E13;
  --ba-product-text: #E7B8A8;

  --ba-infrastructure-surface: #003036;
  --ba-infrastructure-text: #96D0D6;

  --ba-customer-surface: #3C1D2E;
  --ba-customer-text: #E2B5CB;

  --ba-corporate-surface: #362602;
  --ba-corporate-text: #D7C198;

  --ba-brand-surface: #2D2240;
  --ba-brand-text: #CABCE6;

  --shadow-xs: 0 1px 0 0 rgba(11, 15, 43, 0.20);
  --shadow-sm: 0 1px 2px rgba(11, 15, 43, 0.24), 0 1px 0 rgba(11, 15, 43, 0.12);
  --shadow-md: 0 4px 12px rgba(11, 15, 43, 0.24), 0 1px 2px rgba(11, 15, 43, 0.16);
  --shadow-lg: 0 12px 32px rgba(11, 15, 43, 0.40), 0 2px 6px rgba(11, 15, 43, 0.16);
  --shadow-focus: 0 0 0 3px rgba(139, 79, 230, 0.40);
}

/* ==========================================================================
   Vibrant Mode
   Third official theme. Same structure as light (off-white surface, near-
   black text, hairline borders, no shadow at rest) — only the color source
   changes. --primary switches from plain violet to the Brand family's
   intensified accent, and the seven business-area families deepen from
   their light-mode pastel values to a punchier "-vibrant" ramp so
   decoration (badges, alerts, the pattern weave) can read as genuinely
   colorful. Radius, shadow geometry, and motion timing are untouched.
   ========================================================================== */

[data-theme="vibrant"] {
  --background: #FCFBFF;
  --foreground: #0B0F2B;
  --card: #FFFFFF;
  --card-foreground: #0B0F2B;
  --popover: #FFFFFF;
  --popover-foreground: #0B0F2B;

  --primary: #6317CF;
  --primary-foreground: #FFFFFF;
  --secondary: #F1ECFB;
  --secondary-foreground: #0B0F2B;
  --muted-surface: #F1ECFB;
  --muted-foreground: #6D6080;
  --accent: #F3E8FE;
  --accent-foreground: #0B0F2B;
  --destructive: #D6283A;
  --destructive-foreground: #FFFFFF;

  --border: #DCD3F2;
  --border-violet: rgba(99, 23, 207, 0.22);
  --input: #FFFFFF;
  --ring: #6317CF;

  --ba-executive-accent: #0A76B8;
  --ba-executive-surface: #B3D8EF;
  --ba-executive-text: #053E61;

  --ba-commercial-accent: #1C7E07;
  --ba-commercial-surface: #BEEFB3;
  --ba-commercial-text: #114E04;

  --ba-product-accent: #CB3B0B;
  --ba-product-surface: #EFC2B3;
  --ba-product-text: #611C05;

  --ba-infrastructure-accent: #0B828E;
  --ba-infrastructure-surface: #B3EAEF;
  --ba-infrastructure-text: #04474E;

  --ba-customer-accent: #D10574;
  --ba-customer-surface: #EFB3D4;
  --ba-customer-text: #610537;

  --ba-corporate-accent: #916608;
  --ba-corporate-surface: #EFDCB3;
  --ba-corporate-text: #4E3704;

  --ba-brand-accent: #6317CF;
  --ba-brand-surface: #CCB3EF;
  --ba-brand-text: #3C0788;

  --shadow-focus: 0 0 0 3px rgba(99, 23, 207, 0.32);
}

/* ==========================================================================
    Accessibility: Reduced Motion
    ========================================================================== */

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

/* ==========================================================================
    Base Styles
    ========================================================================== */

* {
  box-sizing: border-box;
}

html {
  font-family: var(--font-sans);
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background-color: var(--background);
  color: var(--foreground);
  line-height: 1.6;
  transition: background-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out);
}

.container {
  max-width: var(--container-dapple);
  margin: 0 auto;
  padding: 0 2rem;
}

.section {
  margin-bottom: 4.5rem;
}

.section-head {
  margin-bottom: 1.75rem;
}

.demo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  margin-top: 1.5rem;
  /* Container query boundary for .card: cards adapt to the width of this
     grid's own column, not the raw viewport, see the Cards section. */
  container-type: inline-size;
  container-name: card-grid;
}

.demo-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1rem;
  align-items: center;
}

/* ==========================================================================
   Utilities
   ========================================================================== */

.text-eyebrow {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--primary);
}

/* Quiet variant, composes with .text-eyebrow: violet reads as THE label
   of a view (one hero or section head), muted reads as one label among
   many. Use in data-dense panels where several eyebrows share the
   screen. */
.text-eyebrow.text-eyebrow-quiet {
  color: var(--muted-foreground);
}

.text-eyebrow-sm {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--primary);
}

/* Visually hidden but present for screen readers. Use when a label needs
   to disappear at a breakpoint (icon-only nav item, collapsed button
   label) without losing its accessible name, never delete the label
   text outright. */
.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;
}

.text-caption {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--muted-foreground);
  margin: 0;
}

.text-muted {
  color: var(--muted-foreground);
}

/* ==========================================================================
   Pattern — plain-CSS equivalent of the DapplePattern component. Seven
   vertical bands, one per business-area family, each a repeating stripe of
   thin rules at low opacity. Built entirely from background-image layers on
   a single element, no extra markup. Decorative only: aria-hidden it at the
   call site and never let it carry information. Tokens resolve per theme
   automatically, so the same class reads pastel in light, inverted in dark,
   and saturated in vibrant with zero extra CSS.
   ========================================================================== */

.pattern-weave {
  position: relative;
  overflow: hidden;
}

.pattern-weave::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-repeat: repeat-y, repeat-y, repeat-y, repeat-y, repeat-y, repeat-y, repeat-y;
  background-size: 14.2857% 12px, 14.2857% 12px, 14.2857% 12px, 14.2857% 12px, 14.2857% 12px, 14.2857% 12px, 14.2857% 12px;
  /* Percentages here use the (container - image) background-position
     formula, so with a 14.2857%-wide image per layer the seven bands land
     edge-to-edge with no gap: k/(7-1) * 100 for k = 0..6. */
  background-position: 0% 0, 16.6667% 4px, 33.3333% 8px, 50% 2px, 66.6667% 6px, 83.3333% 10px, 100% 3px;
  background-image:
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-executive-accent) 45%, transparent) 0px, color-mix(in oklch, var(--ba-executive-accent) 45%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-commercial-accent) 55%, transparent) 0px, color-mix(in oklch, var(--ba-commercial-accent) 55%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-product-accent) 35%, transparent) 0px, color-mix(in oklch, var(--ba-product-accent) 35%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-infrastructure-accent) 65%, transparent) 0px, color-mix(in oklch, var(--ba-infrastructure-accent) 65%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-customer-accent) 45%, transparent) 0px, color-mix(in oklch, var(--ba-customer-accent) 45%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-corporate-accent) 55%, transparent) 0px, color-mix(in oklch, var(--ba-corporate-accent) 55%, transparent) 3px, transparent 3px, transparent 12px),
    repeating-linear-gradient(to bottom, color-mix(in oklch, var(--ba-brand-accent) 35%, transparent) 0px, color-mix(in oklch, var(--ba-brand-accent) 35%, transparent) 3px, transparent 3px, transparent 12px);
}

/* A bolder cousin for hero/marketing surfaces: a multi-hue gradient built
   from four family accents, meant for vibrant-flavored empty states and
   section dividers rather than the thin stripe weave above. */
.gradient-weave {
  background-image: linear-gradient(
    115deg,
    var(--ba-brand-accent) 0%,
    var(--ba-customer-accent) 33%,
    var(--ba-product-accent) 66%,
    var(--ba-corporate-accent) 100%
  );
}

/* ==========================================================================
   Navigation
   ========================================================================== */

.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background-color: var(--background);
  border-bottom: 1px solid var(--border);
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.nav-inner {
  max-width: 80rem;
  margin: 0 auto;
  padding: 0 2rem;
  height: 4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

/* Layout rule: the nav's horizontal bounds always match the page body's
   own width class, on every viewport width. Two cases, judged by the body
   as a whole, not by any one region inside it:

   - Centered, max-width body (index.html's `.container`: max-width: 80rem,
     centered, 2rem side padding): the nav matches, same max-width, same
     centering, plain `.nav-inner`.
   - Full-bleed body (no max-width, spans the full viewport): the nav is
     full-bleed too, `.nav-inner--full` (max-width: none, same 2rem
     padding, no centering).

   A fixed-width sidebar inside a full-bleed body (`.app-shell`,
   `.chat-shell`) does not change which case applies, the sidebar is part
   of that full-bleed body, not a second body to align around. Do not
   inset the nav to clear a sidebar's width, that was tried and reverted,
   it made the logo match the content pane instead of matching the body,
   which is not the rule. `login.html`'s `.auth-shell` is full-bleed too
   (its card is centered within it, but the shell itself has no cap), so
   it also uses `.nav-inner--full`, same reasoning, no sidebar to
   consider either way. */
.nav-inner--full {
  max-width: none;
}

.logo {
  display: inline-flex;
  align-items: center;
  color: var(--foreground);
  text-decoration: none;
}

.logo-mark {
  display: inline-block;
  height: 1.5rem;
  width: 7.45rem;
  background-color: currentColor;
  -webkit-mask: url("./assets/dapple-mark.svg") no-repeat center / contain;
          mask: url("./assets/dapple-mark.svg") no-repeat center / contain;
}

/* Optional product name next to the mark (DAPPLE-DESIGN-SYSTEM.md section 6,
   "Logo & app name"). Sized at the h4 scale, never smaller, so it reads as a
   paired identity with the 24px-tall mark instead of a caption-sized
   afterthought. Color is a call-site decision (brand accent apps may tint
   it), size/weight/line-height are not. */
.logo-title {
  margin-left: var(--space-3);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.5rem;
  color: var(--foreground);
  white-space: nowrap;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  justify-content: center;
}

.nav-links a {
  display: inline-block;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-sm);
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: 0.9375rem;
  transition: color var(--dur-2) var(--ease-in-out),
              background-color var(--dur-2) var(--ease-in-out);
}

.nav-links a:hover {
  color: var(--foreground);
  background-color: var(--accent);
}

.nav-links a.active {
  color: var(--foreground);
  font-weight: 500;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background-color: transparent;
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.theme-toggle:hover {
  background-color: var(--accent);
}

.theme-toggle:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.theme-toggle svg {
  width: 18px;
  height: 18px;
}

/* Top-nav help entry point: same icon-button shape/tokens as .theme-toggle
   (border, radius, hover/focus treatment), a distinct class because it is
   a semantically different control (opens the help .menu, not a theme
   cycle), not a color or size variant of one. See
   APP-SHELL-REFERENCE.md, "Help & support" for when this belongs vs the
   contextual inline help popover below vs the marketing Support card. */
.help-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background-color: transparent;
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.help-trigger:hover {
  background-color: var(--accent);
}

.help-trigger:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.help-trigger svg {
  width: 18px;
  height: 18px;
}

/* Contextual inline help: a small "i" affordance sitting directly next to
   one non-obvious control (a settings field, a workspace switcher), not
   in the header. Deliberately smaller and borderless, it reads as part of
   the label row, not as a second global entry point. Opens the same
   .menu primitive, just holding a plain explanatory paragraph
   (.menu-note) instead of a list of destinations. */
.help-inline-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.125rem;
  height: 1.125rem;
  padding: 0;
  margin-left: 0.35rem;
  background-color: transparent;
  color: var(--muted-foreground);
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color var(--dur-2) var(--ease-in-out);
}

.help-inline-trigger:hover {
  color: var(--foreground);
}

.help-inline-trigger:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.help-inline-trigger svg {
  width: 100%;
  height: 100%;
}

/* Plain, non-interactive text content inside a .menu, for popovers that
   answer a question in a sentence rather than offering a list of
   destinations. Legitimate use of the .menu primitive alongside, or
   instead of, .menu-item, see menu.js's comment: only .menu-item entries
   participate in roving-tabindex/arrow-key navigation, a .menu-note does
   not need to. */
.menu-note {
  margin: 0;
  padding: 0.5rem 0.65rem;
  font-size: 0.8125rem;
  color: var(--popover-foreground);
  max-width: 15rem;
}

/* Touch-friendly minimum touch targets: 44px (WCAG 2.5.8 / iOS HIG) */
@media (pointer: coarse) {
  .theme-toggle {
    width: 2.75rem;
    height: 2.75rem;
  }

  .help-trigger {
    width: 2.75rem;
    height: 2.75rem;
  }

  .btn-icon {
    width: 2.75rem;
    height: 2.75rem;
  }
}

/* The quick toggle cycles the theme MODE (light -> dark -> system), not the
   individual themes. Which concrete theme a mode resolves to (e.g. vibrant
   as your day theme) is chosen in settings.html. The icon shows the CURRENT
   mode. theme.js sets data-mode on <html>; before it runs, no icon shows,
   which also avoids a wrong-icon flash. */
.icon-sun,
.icon-moon,
.icon-auto {
  display: none;
}

html[data-mode="light"] .icon-sun {
  display: block;
}

html[data-mode="dark"] .icon-moon {
  display: block;
}

html[data-mode="system"] .icon-auto {
  display: block;
}

/* Day/night theme-slot toggle: a second, logically-different control from
   .theme-toggle above. .theme-toggle cycles the MODE (light/dark/system);
   this picks which concrete theme "day" (light mode, and system mode
   during the day) resolves to, light or vibrant, the same choice
   settings.html's Appearance section exposes as radio cards. A compact
   2-button segmented group, not a third full radio-card, since the header
   only has room for a quick toggle, not a labeled choice with hints. Same
   class is reused unchanged inside .nav-drawer-section below, the
   ancestor-scoped media query further down is what hides the header copy
   below md, not a separate drawer-only class. */
.theme-slot-toggle {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem;
  gap: 0.15rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.theme-slot-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0.75rem;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--muted-foreground);
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out);
}

.theme-slot-btn:hover {
  color: var(--foreground);
}

.theme-slot-btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.theme-slot-btn[aria-pressed="true"] {
  background-color: var(--accent);
  color: var(--foreground);
  font-weight: 500;
}

/* Header copy only: below md it demotes into .nav-drawer-section instead
   of shrinking in place, same rule .nav-links already follows. Scoped to
   .nav-inner so the drawer's copy of the same .theme-slot-toggle class is
   unaffected. */
@media (max-width: 767.98px) {
  .nav-inner .theme-slot-toggle {
    display: none;
  }
}

/* Drawer copy: stretch to the section's full width instead of the
   header's compact inline sizing. */
.nav-drawer-section .theme-slot-toggle {
  width: 100%;
}

.nav-drawer-section .theme-slot-btn {
  flex: 1;
}

/* Compact top-nav identity affordance: an .avatar wrapped in a plain
   trigger button, .menu carries the name/email/actions detail. This is
   the default for any flat top-nav app (no sidebar to pin a full
   .user-account footer control to, see nav.html for that variant):
   collapse to the avatar at every viewport width, never inline a name or
   email in the header itself, the popover carries that detail instead. */
.nav-identity {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: box-shadow var(--dur-2) var(--ease-in-out);
}

.nav-identity:hover {
  box-shadow: var(--shadow-md);
}

.nav-identity:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/* Burger button: phone only. Mobile-first per the responsive contract,
   the unprefixed rule is the phone layout and md+ restores the inline
   links. 44x44 minimum touch target (WCAG 2.5.8 / iOS HIG). */
.nav-burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  background-color: transparent;
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.nav-burger:hover {
  background-color: var(--accent);
}

.nav-burger:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.nav-burger svg {
  width: 20px;
  height: 20px;
}

@media (min-width: 768px) {
  .nav-burger {
    display: none;
  }
}

/* Below md the inline links hide and live in the drawer instead. The rule
   set on .nav-links above still defines the desktop layout, this only
   gates visibility per tier. */
@media (max-width: 767.98px) {
  .nav-links {
    display: none;
  }
}

/* Mobile nav drawer: a native <dialog> opened with showModal(), which
   provides the focus trap, Escape-to-close, and backdrop for free, no
   focus-trap library. Slides in from the right. @starting-style powers the
   entry transition in modern browsers and degrades to an instant open
   elsewhere, functionality is identical either way. */
.nav-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(20rem, 85vw);
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: none;
  border-left: 1px solid var(--border);
  background-color: var(--background);
  color: var(--foreground);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform var(--dur-2) var(--ease-in-out),
              overlay var(--dur-2) allow-discrete,
              display var(--dur-2) allow-discrete;
}

.nav-drawer[open] {
  transform: translateX(0);
}

@starting-style {
  .nav-drawer[open] {
    transform: translateX(100%);
  }
}

.nav-drawer::backdrop {
  background-color: var(--overlay-backdrop);
}

.nav-drawer-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
  /* clear notches/home indicators on phones */
  padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom, 0px));
}

.nav-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.25rem 0.25rem 1rem;
  border-bottom: 1px solid var(--border);
}

.nav-drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  background-color: transparent;
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.nav-drawer-close:hover {
  background-color: var(--accent);
}

.nav-drawer-close:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.nav-drawer-close svg {
  width: 18px;
  height: 18px;
}

.nav-drawer-links {
  list-style: none;
  margin: 0;
  padding: 1rem 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.nav-drawer-links a {
  display: block;
  padding: 0.75rem 0.75rem;
  border-radius: var(--radius-md);
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: 1rem;
  transition: color var(--dur-2) var(--ease-in-out),
              background-color var(--dur-2) var(--ease-in-out);
}

.nav-drawer-links a:hover {
  color: var(--foreground);
  background-color: var(--accent);
}

.nav-drawer-links a.active {
  color: var(--foreground);
  font-weight: 500;
  background-color: var(--accent);
}

/* Labeled, non-link drawer section: for controls that demote into the
   drawer alongside the plain link list (e.g. the day/night theme-slot
   toggle) rather than being reachable only as a link. .nav-drawer-links
   stays a pure <ul> of page links, this is the general-purpose sibling for
   anything else a page's drawer needs to carry. Heading reuses
   .sidebar-label's exact visual style rather than inventing a second label
   treatment. */
.nav-drawer-section {
  padding: 0.75rem 0.25rem 0.25rem;
  border-top: 1px solid var(--border);
}

.nav-drawer-section .sidebar-label {
  padding: 0 0.5rem;
}

/* ==========================================================================
   Local nav — tier 2 of the two-tier topbar (DAPPLE-DESIGN-SYSTEM.md
   section 7, rule 12; APP-SHELL-REFERENCE.md section 1).
   Apple's global + local nav pattern (developer.apple.com, apple.com
   product pages): a thin section bar directly under the global .nav.
   Section title on the left, section-scoped links on the right, optional
   page action after them. Optional per page: auth pages, error pages, and
   app shells whose sidebar or in-pane header already provides local
   navigation (nav.html, chat.html) omit it entirely.

   Single markup serves every width: a <details> wraps title (summary) and
   links. At md+ the links are forced visible and the summary is inert
   (chevron hidden), below md it behaves as a native disclosure, the links
   drop down as a panel, zero JavaScript. The mark stays top-left in tier 1
   at every width, tier 2 never carries a logo.
   ========================================================================== */

/* Anchor targets must scroll clear of the sticky header stack: without
   this, following a #section link puts the target at the very top of the
   viewport and the sticky .nav + .localnav (about 7.1rem together) sit
   ON TOP of the section heading, so the reader lands mid-lead with no
   title in sight. scroll-margin-top only participates in scroll-into-
   view positioning, it changes no layout, so applying it to every [id]
   is safe: 8rem = the 4rem nav + ~3.1rem localnav + breathing room.
   Pages without a localnav get a little extra air above the target,
   which reads fine. Keep in sync with .nav-inner and .localnav heights. */
[id] {
  scroll-margin-top: 8rem;
}

.localnav {
  position: sticky;
  top: 4rem; /* directly under .nav, keep in sync with .nav-inner height */
  z-index: 19; /* one under .nav's 20, above page content */
  background-color: var(--background);
  border-bottom: 1px solid var(--border);
  transition:
    background-color var(--dur-2) var(--ease-in-out),
    border-color var(--dur-2) var(--ease-in-out);
}

.localnav-inner {
  max-width: 80rem;
  margin: 0 auto;
  padding: 0 2rem;
  min-height: 3rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.localnav-inner.nav-inner--full {
  max-width: none;
}

.localnav-d {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex: 1;
  min-width: 0;
}

.localnav-d summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  list-style: none;
  cursor: default;
}

.localnav-d summary::-webkit-details-marker {
  display: none;
}

.localnav-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--foreground);
  white-space: nowrap;
}

.localnav-sub {
  font-size: 0.75rem;
  color: var(--muted-foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.localnav-chevron {
  display: none; /* mobile-only affordance, see the media query below */
  width: 14px;
  height: 14px;
  color: var(--muted-foreground);
  transition: transform var(--dur-1) var(--ease-in-out);
}

.localnav-links {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: auto;
}

.localnav-links a {
  display: inline-block;
  padding: 0.35rem 0.6rem;
  border-radius: var(--radius-sm);
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: 0.8125rem;
  white-space: nowrap;
  transition:
    color var(--dur-2) var(--ease-in-out),
    background-color var(--dur-2) var(--ease-in-out);
}

.localnav-links a:hover {
  color: var(--foreground);
  background-color: var(--accent);
}

.localnav-links a.active {
  color: var(--foreground);
  font-weight: 500;
}

.localnav-action {
  flex: none;
}

@media (max-width: 767.98px) {
  .localnav-inner {
    padding: 0 1rem;
  }

  /* Native disclosure below md: summary becomes the toggle, links drop
     down as a full-width panel under the bar. */
  .localnav-d {
    display: block;
    position: static;
  }

  .localnav-d summary {
    cursor: pointer;
    min-height: 2.75rem; /* 44px touch target */
  }

  .localnav-d summary:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
    border-radius: var(--radius-sm);
  }

  .localnav-chevron {
    display: block;
  }

  .localnav-d[open] .localnav-chevron {
    transform: rotate(180deg);
  }

  .localnav-links {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    margin: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background-color: var(--background);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    padding: 0.25rem 1rem 0.75rem;
  }

  .localnav {
    position: sticky;
  }

  .localnav-links a {
    padding: 0.65rem 0.5rem;
    font-size: 0.9375rem;
  }
}

@media (min-width: 768px) {
  /* The markup ships <details open>, so links are natively visible here.
     The summary is made inert so it cannot be collapsed at md+ (a closed
     details hides its content at the UA level, CSS cannot override it).
     nav.js guards the keyboard path the same way and closes the
     disclosure on mobile load, see its localnav block. */
  .localnav-d summary {
    pointer-events: none;
  }
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  padding: 5rem 0 4rem;
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  top: -14rem;
  left: 50%;
  transform: translateX(-50%);
  width: 60rem;
  height: 26rem;
  background: radial-gradient(closest-side, var(--violet-tint-3), transparent);
  z-index: -1;
  pointer-events: none;
}

.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.75rem;
  margin-bottom: 1.5rem;
  background-color: var(--accent);
  border: 1px solid var(--border-violet);
  border-radius: var(--radius-pill);
}

.hero h1 {
  max-width: 42rem;
  font-size: var(--text-hero);
  line-height: var(--text-hero--line-height);
  letter-spacing: var(--text-hero--letter-spacing);
  font-weight: 500;
  margin: 0 0 1.25rem;
}

.hero-subhead {
  max-width: 38rem;
  font-size: var(--text-hero-subtitle);
  line-height: var(--text-hero-subtitle--line-height);
  letter-spacing: var(--text-hero-subtitle--letter-spacing);
  color: var(--muted-foreground);
  font-weight: 400;
  margin: 0 0 2rem;
}

.hero-actions {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

/* ==========================================================================
   Typography
   ========================================================================== */

h1 {
  font-size: 2rem;
  font-weight: 500;
  margin: 0 0 0.5rem 0;
  line-height: 1.2;
}

h2 {
  font-size: 1.5rem;
  font-weight: 500;
  margin: 0 0 0.5rem 0;
  line-height: 1.3;
}

h3 {
  font-size: 1.25rem;
  font-weight: 500;
  margin: 0 0 0.5rem 0;
  line-height: 1.35;
}

h4 {
  font-size: 1.0625rem;
  font-weight: 500;
  margin: 0 0 0.5rem 0;
  line-height: 1.4;
}

p {
  font-size: 1rem;
  font-weight: 400;
  margin: 0 0 1rem 0;
}

.lead {
  font-size: var(--text-callout);
  line-height: var(--text-callout--line-height);
  letter-spacing: var(--text-callout--letter-spacing);
  color: var(--muted-foreground);
  max-width: 42rem;
}

.type-scale {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: 2rem;
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.type-scale > * {
  margin: 0;
}

.type-scale .text-caption {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Color palette swatches (index.html's Color section). Each chip reads
   its color from the --sw custom property set inline on the element,
   i.e. the swatch IS the live token: switch mode or day theme in the
   header and the section re-renders itself, it cannot drift from this
   file. The hairline border keeps near-background chips (background,
   card, accent) visible on their own surface. */
.swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 1rem;
}

.swatch-group {
  margin-top: 2rem;
}

.swatch-group-title {
  font-size: 0.9375rem;
  font-weight: 500;
  margin: 0 0 0.25rem;
}

.swatch-group-note {
  font-size: 0.875rem;
  color: var(--muted-foreground);
  margin: 0 0 1rem;
  max-width: 42rem;
}

.swatch-chip {
  /* --sw is the chip's one parameter, set inline per swatch in the
     markup (style="--sw: var(--token)"). Defined here with a safe
     default so the token is canonical and a chip missing its inline
     value renders as an obvious hole, not a wrong color. */
  --sw: transparent;
  display: block;
  height: 3.5rem;
  border-radius: var(--radius-md);
  background-color: var(--sw);
  border: 1px solid var(--border);
}

.swatch code {
  display: block;
  margin-top: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted-foreground);
  overflow-wrap: anywhere;
}

/* Business-area families: one row per family, three chips (accent,
   surface, text) sharing the family label. */
.swatch-family {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  gap: 1rem;
  align-items: center;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}

.swatch-family:last-child {
  border-bottom: none;
}

.swatch-family-name {
  font-size: 0.875rem;
  font-weight: 500;
  margin: 0;
}

.swatch-family-chips {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
}

.swatch-family-chips .swatch-chip {
  height: 2.25rem;
}

@media (max-width: 767.98px) {
  .swatch-family {
    grid-template-columns: 1fr;
    gap: 0.4rem;
  }
}

.type-body {
  font-size: var(--text-display-body);
  line-height: var(--text-display-body--line-height);
  letter-spacing: var(--text-display-body--letter-spacing);
  color: var(--foreground);
  max-width: 40rem;
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1.125rem;
  font-size: 1rem;
  font-weight: 500;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out),
              opacity var(--dur-2) var(--ease-in-out);
  text-decoration: none;
  font-family: var(--font-sans);
}

.btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.btn-primary {
  background-color: var(--primary);
  color: var(--primary-foreground);
  border-color: var(--primary);
}

.btn-primary:hover {
  opacity: 0.9;
}

.btn-secondary {
  background-color: var(--secondary);
  color: var(--secondary-foreground);
  border-color: var(--border);
}

.btn-secondary:hover {
  background-color: var(--accent);
}

.btn-ghost {
  background-color: transparent;
  color: var(--foreground);
  border-color: transparent;
}

.btn-ghost:hover {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.btn:disabled {
  cursor: not-allowed;
  background-color: var(--muted-surface);
  color: var(--muted-foreground);
  border-color: var(--border);
  opacity: 1;
}

.btn:disabled:hover {
  background-color: var(--muted-surface);
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background-color: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
  transition: box-shadow var(--dur-3) var(--ease-in-out),
              border-color var(--dur-3) var(--ease-in-out);
}

.card:hover {
  box-shadow: var(--shadow-md);
}

.card h3 {
  margin-top: 0;
}

.card p {
  color: var(--muted-foreground);
  margin-bottom: 0;
}

.card-link {
  display: inline-block;
  margin-top: 1rem;
  color: var(--primary);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
}

.card-link:hover {
  text-decoration: underline;
}

/* Container query: once a single card's own column drops below 22rem
   (a narrow sidebar rail, a stacked mobile column, a dense dashboard
   panel), tighten the padding rather than waiting for a viewport
   breakpoint that may not correlate with how wide this card actually
   renders. Falls back to an equivalent viewport check on browsers
   without container query support. */
@supports (container-type: inline-size) {
  @container card-grid (max-width: 22rem) {
    .card {
      padding: 1.25rem;
    }
  }
}

@supports not (container-type: inline-size) {
  @media (max-width: 22rem) {
    .card {
      padding: 1.25rem;
    }
  }
}

.card-featured {
  position: relative;
  border-color: var(--border-violet);
}

.card-badge {
  position: absolute;
  top: 1.75rem;
  right: 1.75rem;
}

.card-price {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin: 0.5rem 0 1.25rem;
}

.card-price-amount {
  font-size: var(--text-feature);
  line-height: var(--text-feature--line-height);
  letter-spacing: var(--text-feature--letter-spacing);
  font-weight: 500;
}

.card-price-period {
  color: var(--muted-foreground);
  font-size: 0.9375rem;
}

.card-list {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.card-list li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.9375rem;
  color: var(--card-foreground);
}

.card-list svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--primary);
}

.card-full-width {
  width: 100%;
}

/* ==========================================================================
   Form Elements
   ========================================================================== */

.form-card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2rem;
  max-width: 34rem;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem 1.25rem;
}

.label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: 0.5rem;
  color: var(--foreground);
}

.input,
textarea.input,
select.input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  font-size: 1rem;
  font-family: var(--font-sans);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--input);
  color: var(--foreground);
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-in-out);
}

textarea.input {
  resize: vertical;
  min-height: 6rem;
  line-height: 1.5;
}

.input:focus,
textarea.input:focus,
select.input:focus {
  outline: none;
  border-color: var(--ring);
  box-shadow: var(--shadow-focus);
}

.input:disabled,
textarea.input:disabled,
select.input:disabled {
  cursor: not-allowed;
  background-color: var(--muted-surface);
  color: var(--muted-foreground);
  border-color: var(--border);
  opacity: 0.6;
}

.input-group {
  margin-bottom: 1.5rem;
}

.input-hint {
  margin: 0.4rem 0 0;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}

.select-wrapper {
  position: relative;
}

.select-wrapper select.input {
  appearance: none;
  padding-right: 2.25rem;
  cursor: pointer;
}

.select-wrapper::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0.9rem;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid var(--muted-foreground);
  transform: translateY(-50%);
  pointer-events: none;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.toggle {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 24px;
  flex-shrink: 0;
}

.toggle input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
  z-index: 1;
}

.toggle-track {
  position: absolute;
  inset: 0;
  background-color: var(--secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background-color: var(--card);
  border-radius: var(--radius-pill);
  transition: transform var(--dur-2) var(--ease-in-out);
}

.toggle input:checked ~ .toggle-track {
  background-color: var(--primary);
  border-color: var(--primary);
}

.toggle input:checked ~ .toggle-thumb {
  transform: translateX(16px);
}

.toggle input:focus-visible ~ .toggle-track {
  box-shadow: var(--shadow-focus);
}

.toggle input:disabled ~ .toggle-track {
  cursor: not-allowed;
  background-color: var(--muted-surface);
  border-color: var(--border);
  opacity: 0.6;
}

.toggle input:disabled ~ .toggle-thumb {
  background-color: var(--muted-foreground);
}

.toggle-label {
  font-size: 0.9375rem;
  color: var(--foreground);
}

/* ==========================================================================
   Badges
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.75rem;
  font-size: 0.75rem;
  font-weight: 500;
  border-radius: var(--radius-pill);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.badge-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

.badge-executive { background-color: var(--ba-executive-surface); color: var(--ba-executive-text); }
.badge-executive .badge-dot { background-color: var(--ba-executive-accent); }

.badge-commercial { background-color: var(--ba-commercial-surface); color: var(--ba-commercial-text); }
.badge-commercial .badge-dot { background-color: var(--ba-commercial-accent); }

.badge-product { background-color: var(--ba-product-surface); color: var(--ba-product-text); }
.badge-product .badge-dot { background-color: var(--ba-product-accent); }

.badge-infrastructure { background-color: var(--ba-infrastructure-surface); color: var(--ba-infrastructure-text); }
.badge-infrastructure .badge-dot { background-color: var(--ba-infrastructure-accent); }

.badge-customer { background-color: var(--ba-customer-surface); color: var(--ba-customer-text); }
.badge-customer .badge-dot { background-color: var(--ba-customer-accent); }

.badge-corporate { background-color: var(--ba-corporate-surface); color: var(--ba-corporate-text); }
.badge-corporate .badge-dot { background-color: var(--ba-corporate-accent); }

.badge-brand { background-color: var(--ba-brand-surface); color: var(--ba-brand-text); }
.badge-brand .badge-dot { background-color: var(--ba-brand-accent); }

/* ==========================================================================
   Alerts
   ========================================================================== */

.alert {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--muted-surface);
}

.alert-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  margin-top: 0.1rem;
}

.alert-title {
  margin: 0 0 0.25rem;
  font-weight: 500;
  font-size: 0.9375rem;
}

.alert-text {
  margin: 0;
  font-size: 0.9375rem;
}

.alert-info {
  background-color: var(--muted-surface);
  border-color: var(--border);
}

.alert-info .alert-icon,
.alert-info .alert-title {
  color: var(--foreground);
}

.alert-info .alert-text {
  color: var(--muted-foreground);
}

.alert-success {
  background-color: var(--ba-commercial-surface);
  border-color: transparent;
}

.alert-success .alert-icon,
.alert-success .alert-title,
.alert-success .alert-text {
  color: var(--ba-commercial-text);
}

.alert-warning {
  background-color: var(--ba-corporate-surface);
  border-color: transparent;
}

.alert-warning .alert-icon,
.alert-warning .alert-title,
.alert-warning .alert-text {
  color: var(--ba-corporate-text);
}

.alert-danger {
  background-color: var(--muted-surface);
  border-color: var(--destructive);
}

.alert-danger .alert-icon,
.alert-danger .alert-title {
  color: var(--destructive);
}

.alert-danger .alert-text {
  color: var(--muted-foreground);
}

/* ==========================================================================
   Footer
   ========================================================================== */

/* ==========================================================================
   Markdown viewer (md-viewer.html + serve.py's generated wrapper)
   ========================================================================== */

.markdown-body {
  max-width: var(--markdown-max-width, 46rem);
  margin: 0 auto;
  padding: 3rem 0 4.5rem;
}

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4 {
  font-weight: 500;
  margin: 2rem 0 0.75rem;
}

.markdown-body h1 {
  font-size: var(--text-feature);
  margin-top: 0;
}

.markdown-body h2 {
  font-size: 1.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.markdown-body h3 {
  font-size: 1.2rem;
}

.markdown-body p,
.markdown-body ul,
.markdown-body ol,
.markdown-body blockquote {
  margin: 0 0 1rem;
  line-height: 1.7;
}

.markdown-body ul,
.markdown-body ol {
  padding-left: 1.5rem;
}

.markdown-body li {
  margin-bottom: 0.35rem;
}

.markdown-body a {
  color: var(--primary);
}

.markdown-body code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  background-color: var(--muted-surface);
  padding: 0.15em 0.4em;
  border-radius: var(--radius-xs);
}

.markdown-body pre {
  background-color: var(--muted-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1rem 1.25rem;
  overflow-x: auto;
  margin: 0 0 1rem;
}

.markdown-body pre code {
  background: none;
  padding: 0;
  font-size: 0.8125rem;
  line-height: 1.6;
}

.markdown-body blockquote {
  border-left: 3px solid var(--border-violet);
  padding-left: 1rem;
  color: var(--muted-foreground);
}

.markdown-body hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2rem 0;
}

.markdown-body table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 1.5rem;
  font-size: 0.9rem;
}

.markdown-body th,
.markdown-body td {
  border: 1px solid var(--border);
  padding: 0.5rem 0.75rem;
  text-align: left;
  vertical-align: top;
}

.markdown-body th {
  background-color: var(--muted-surface);
  font-weight: 500;
}

.footer {
  border-top: 1px solid var(--border);
  padding: 2.5rem 0;
}

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}

.footer-links {
  display: flex;
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-links a {
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: 0.875rem;
  transition: color var(--dur-2) var(--ease-in-out);
}

.footer-links a:hover {
  color: var(--foreground);
}

/* ==========================================================================
   Small button variants (shared by sample pages)
   ========================================================================== */

.btn-sm {
  padding: 0.35rem 0.75rem;
  font-size: 0.8125rem;
}

.btn-icon {
  width: 2rem;
  height: 2rem;
  padding: 0;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

.btn-icon svg {
  width: 16px;
  height: 16px;
}

.btn-social-icon {
  width: 16px;
  height: 16px;
  margin-right: 0.5rem;
}

.btn-icon-sm {
  width: 14px;
  height: 14px;
  margin-right: 0.35rem;
}

/* ==========================================================================
   Settings page (settings.html + settings.js)
   Mobile-first: sections stack. md+ adds a sticky section rail on the left.
   ========================================================================== */

.settings-page {
  padding: 3rem 0 4.5rem;
}

.settings-head {
  margin-bottom: 2rem;
}

.settings-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
  align-items: start;
}

.settings-rail {
  display: none;
}

@media (min-width: 768px) {
  .settings-layout {
    grid-template-columns: 200px 1fr;
  }

  .settings-rail {
    display: block;
    position: sticky;
    top: 5rem;
  }
}

.settings-rail a {
  display: block;
  padding: 0.45rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--muted-foreground);
  text-decoration: none;
  font-size: 0.9375rem;
  transition: color var(--dur-2) var(--ease-in-out),
              background-color var(--dur-2) var(--ease-in-out);
}

.settings-rail a:hover {
  color: var(--foreground);
  background-color: var(--accent);
}

.settings-section {
  margin-bottom: 1.5rem;
  /* card base look without the hover lift, settings sections are not
     clickable surfaces */
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
}

.settings-section > h2 {
  font-size: 1.125rem;
  font-weight: 500;
  margin: 0 0 0.25rem;
}

.settings-section-desc {
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  margin: 0 0 1rem;
}

/* One setting per row: label + hint on the left, control on the right.
   Wraps to stacked on very narrow screens. */
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1rem 0;
  border-top: 1px solid var(--border);
}

.settings-row:first-child {
  border-top: none;
  padding-top: 0;
}

.settings-row-label {
  min-width: 0;
}

.settings-row-label .label {
  margin-bottom: 0.15rem;
}

.settings-row-hint {
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  margin: 0;
}

.settings-row-control {
  flex-shrink: 0;
}

.settings-row-control .select-wrapper {
  min-width: 10rem;
}

/* Mode picker: three radio cards (Light / Dark / System). Real radio
   inputs, visually hidden, card styled via :has(). */
.radio-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
  gap: 0.75rem;
  width: 100%;
}

.radio-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.85rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--card);
  cursor: pointer;
  text-align: center;
  transition: border-color var(--dur-2) var(--ease-in-out),
              background-color var(--dur-2) var(--ease-in-out);
}

.radio-card input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.radio-card:hover {
  background-color: var(--accent);
}

.radio-card:has(input:checked) {
  border-color: var(--ring);
  background-color: var(--accent);
}

.radio-card:has(input:focus-visible) {
  box-shadow: var(--shadow-focus);
}

.radio-card-title {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--foreground);
}

.radio-card-hint {
  font-size: 0.75rem;
  color: var(--muted-foreground);
}

/* ==========================================================================
   App shell (sidebar nav sample)
   ========================================================================== */

.app-shell {
  display: flex;
  height: calc(100vh - 4rem);
  overflow: hidden;
}

.app-sidebar {
  width: 260px;
  flex-shrink: 0;
  background-color: var(--muted-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 1rem 0.75rem;
  overflow-y: auto;
  /* Container query boundary for the user-account identity chip: it
     adapts to this rail's own width (e.g. a future collapsed icon rail),
     not the viewport, see .user-account-email below. */
  container-type: inline-size;
  container-name: app-sidebar;
}

.workspace-switcher {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.55rem 0.65rem;
  margin-bottom: 1.25rem;
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--foreground);
  font-family: var(--font-sans);
  cursor: pointer;
  text-align: left;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.workspace-switcher:hover {
  background-color: var(--accent);
}

.workspace-mark {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background-color: var(--primary);
  color: var(--primary-foreground);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 500;
}

.workspace-info {
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}

.workspace-name {
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.workspace-plan {
  font-size: 0.75rem;
  color: var(--muted-foreground);
}

.workspace-switcher svg.chevron {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--muted-foreground);
}

.sidebar-group {
  margin-bottom: 1.5rem;
}

.sidebar-label {
  padding: 0 0.65rem;
  margin-bottom: 0.4rem;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

.sidebar-nav {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--foreground);
  text-decoration: none;
  font-size: 0.875rem;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.sidebar-link svg {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  color: var(--muted-foreground);
}

.sidebar-link:hover {
  background-color: var(--accent);
}

.sidebar-link.active {
  background-color: var(--card);
  border: 1px solid var(--border);
  font-weight: 500;
  padding: calc(0.5rem - 1px) calc(0.65rem - 1px);
}

.sidebar-link.active svg {
  color: var(--primary);
}

.sidebar-footer {
  margin-top: auto;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

/* A <button>, not a <div>, since popovertarget only invokes a popover
   natively on <button>/<input type="button">, see the .menu contract
   comment above. Resets the button's UA border/background/font so it
   still reads as the plain identity chip it always was. */
.user-account {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  color: var(--foreground);
  font-family: var(--font-sans);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.user-account:hover {
  background-color: var(--accent);
}

.user-account:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.avatar {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background-color: var(--primary);
  color: var(--primary-foreground);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8rem;
  font-weight: 500;
}

.avatar-sm {
  width: 28px;
  height: 28px;
  font-size: 0.7rem;
}

.user-account-info {
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}

.user-account-name {
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-account-email {
  font-size: 0.75rem;
  color: var(--muted-foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Container query: if the sidebar this chip renders in narrows past a
   comfortable label width (a collapsed icon rail, a dense panel), drop
   the email line and keep just the name, rather than truncating both
   lines into ellipses. Falls back to an equivalent viewport check on
   browsers without container query support. */
@supports (container-type: inline-size) {
  @container app-sidebar (max-width: 12.5rem) {
    .user-account-email {
      display: none;
    }
  }
}

@supports not (container-type: inline-size) {
  @media (max-width: 12.5rem) {
    .user-account-email {
      display: none;
    }
  }
}

.user-account svg.chevron {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--muted-foreground);
}

/* ==========================================================================
   Menu (Popover API + CSS anchor positioning, no fallback: modern browsers
   only, per the accepted browser-floor decision)
   ========================================================================== */

/* Markup contract: a trigger <button popovertarget="xId"> and a matching
   <div id="xId" popover class="menu"> holding .menu-item buttons/links
   (optionally split by a plain <hr class="menu-divider"> since <hr>'s
   implicit "separator" role and its exclusion from the .menu-item
   selector both come for free). Open/close, outside-click dismiss, and
   Escape all come from the platform via the Popover API, nothing here
   reimplements them. menu.js only adds arrow-key roving-tabindex
   navigation and keeps aria-expanded on the trigger in sync (there is no
   native event for that on the trigger, it listens to the popover's own
   `toggle` event instead). Positioning is anchor-name/position-anchor +
   position-area, with position-try-fallbacks: flip-block so a menu that
   would overflow the viewport edge flips to the other side automatically,
   no hand-rolled JS positioning math. anchor-name binds to one specific
   element, not a class, so each trigger/menu pair on a page needs its own
   pair of names, see the workspaceSwitcher and userAccount anchor rules
   below for the concrete pattern to copy for a new instance. (Never write
   a literal star-slash sequence inside this or any CSS comment: it
   terminates the comment early and the stray text that follows becomes a
   garbage prelude that swallows the NEXT rule whole. Exactly that bug
   shipped here once, killing .menu in every browser, see validate.mjs's
   comment-terminator check.) */
.menu {
  margin: 0;
  padding: 0.35rem;
  min-width: 12rem;
  max-width: 16rem;
  /* No `display` here, ON PURPOSE. The UA hides closed popovers with
     `[popover]:not(:popover-open) { display: none }`, and author rules
     beat UA rules regardless of specificity, so a `display: flex` in
     this base rule would UNHIDE every closed menu: invisible (opacity 0
     below) but hit-testable, anchored right over the header, silently
     swallowing clicks meant for the localnav underneath. That exact bug
     shipped once. `display: flex` lives on :popover-open below, and the
     allow-discrete display transition keeps it through the exit
     animation. flex-direction/gap are inert without display and safe
     here. */
  flex-direction: column;
  gap: 0.1rem;
  background-color: var(--popover);
  color: var(--popover-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transition: opacity var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out),
              overlay var(--dur-1) allow-discrete,
              display var(--dur-1) allow-discrete;
}

.menu:popover-open {
  display: flex;
  opacity: 1;
  transform: translateY(0) scale(1);
}

@starting-style {
  .menu:popover-open {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
  }
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.5rem 0.65rem;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--popover-foreground);
  font-family: var(--font-sans);
  font-size: 0.875rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out);
}

.menu-item svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--muted-foreground);
}

.menu-item:hover {
  background-color: var(--accent);
}

.menu-item:focus-visible {
  outline: none;
  background-color: var(--accent);
  box-shadow: var(--shadow-focus);
}

/* Current-selection state for menus like the workspace switcher, a
   weight bump plus the same accent tint hover already uses rather than a
   new color, so switching context reads as an in-place highlight, not a
   different semantic. */
.menu-item[aria-current="true"] {
  background-color: var(--accent);
  font-weight: 500;
}

.menu-divider {
  height: 1px;
  margin: 0.35rem 0.15rem;
  border: none;
  background-color: var(--border);
}

/* Non-interactive name/email header inside a .menu, for identity menus
   that have room for it (chat.html's top-nav identity chip). Reuses the
   same .user-account-name/.user-account-email text styles the sidebar's
   .user-account already defines, just in a plain wrapper instead of a
   button, since this block isn't itself clickable. */
.menu-identity {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.5rem 0.65rem 0.65rem;
}

/* Anchor pairs. anchor-name binds one specific element, so each of the
   four trigger/menu pairs on this page (workspace switcher and user
   account, each duplicated once for the desktop .app-sidebar and once for
   the <768px sidebarDrawer copy) gets its own name rather than sharing
   one across a class. */
#workspaceSwitcherDesktop { anchor-name: --workspace-anchor-desktop; }
#workspaceSwitcherMobile { anchor-name: --workspace-anchor-mobile; }
#userAccountDesktop { anchor-name: --user-anchor-desktop; }
#userAccountMobile { anchor-name: --user-anchor-mobile; }

/* Workspace switcher sits at the top of the sidebar: open below it,
   left-aligned with the trigger (position-area's span-right keyword).
   flip-block covers the case where the sidebar is short enough that
   "below" would overflow the bottom of the viewport. */
#workspaceMenuDesktop,
#workspaceMenuMobile {
  position-area: bottom span-right;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}
#workspaceMenuDesktop { position-anchor: --workspace-anchor-desktop; }
#workspaceMenuMobile { position-anchor: --workspace-anchor-mobile; }

/* User account sits at the bottom of the sidebar footer: open above it by
   default, since there is rarely room below a footer-pinned trigger.
   flip-block covers a very short viewport where even "above" would
   overflow the top edge. */
#userMenuDesktop,
#userMenuMobile {
  position-area: top span-right;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}
#userMenuDesktop { position-anchor: --user-anchor-desktop; }
#userMenuMobile { position-anchor: --user-anchor-mobile; }

/* chat.html's top-nav identity chip: sits at the top-right of the header,
   so its menu opens below and extends left (span-left) instead of right,
   so it doesn't clip past the viewport edge the way the sidebar's
   bottom-anchored menus (which extend right) don't need to worry about. */
#chatIdentityTrigger { anchor-name: --chat-identity-anchor; }
#chatIdentityMenu {
  position-anchor: --chat-identity-anchor;
  position-area: bottom span-left;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* nav.html's top-nav help entry point, next to the theme controls, same
   anchoring shape as the chat identity chip above: opens below, extends
   left so it doesn't clip the viewport's right edge. */
#helpTrigger { anchor-name: --help-anchor; }
#helpMenu {
  position-anchor: --help-anchor;
  position-area: bottom span-left;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* The top-nav More overflow menu (rule 10's Priority+ pattern, every
   page): the trigger sits at the right end of the inline link list, in
   the middle stretch of the header, so the menu opens below and extends
   left like the other header menus. This pair was originally forgotten,
   which left navMoreMenu on the UA popover default (position: fixed,
   inset: 0, margin: auto), dead-center of the viewport: a menu that is
   not anchored does not fall back to "near its trigger", it falls back
   to CENTERED, so never ship a .menu without its anchor pair. */
#navMoreTrigger { anchor-name: --nav-more-anchor; }
#navMoreMenu {
  position-anchor: --nav-more-anchor;
  position-area: bottom span-left;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* settings.html's contextual inline help popover, anchored to the small
   "i" trigger next to the Landing page field instead of a header control. */
#landingPageHelpTrigger { anchor-name: --landing-help-anchor; }
#landingPageHelpMenu {
  position-anchor: --landing-help-anchor;
  position-area: bottom span-right;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* site_detail.html.tmpl's "Access requests" heading, same contextual
   inline help shape as the landing-page pair above, next to a section
   heading instead of a field. */
#accessHelpTrigger { anchor-name: --access-help-anchor; }
#accessHelpMenu {
  position-anchor: --access-help-anchor;
  position-area: bottom span-right;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* ==========================================================================
   Identity session states (signed out, switching workspace, impersonating)
   See APP-SHELL-REFERENCE.md, "Identity session states" for when each
   applies. Signed in is the existing .user-account/.nav-identity + .menu
   pattern above, these three add the states around it.
   ========================================================================== */

/* Switching workspace/account: a just-clicked .menu-item goes into a
   disabled, dimmed, spinner state for the (real or, here, simulated)
   duration of the switch, rather than closing the popover instantly as if
   the switch was synchronous. */
.menu-item.is-switching {
  opacity: 0.6;
  pointer-events: none;
  cursor: default;
}

.menu-item-spinner {
  width: 14px;
  height: 14px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--border);
  border-top-color: var(--foreground);
  flex-shrink: 0;
  animation: menu-item-spin 0.6s linear infinite;
}

@keyframes menu-item-spin {
  to { transform: rotate(360deg); }
}

/* Impersonating: an admin viewing the product as another user. Reuses the
   same ba-corporate surface/text tokens .alert-warning already uses (see
   the Alerts section above), a distinct, unmistakable warning treatment
   built from existing tokens rather than a new semantic color. Sits at
   the very top of .app-content, above the page's own header, so it reads
   as "true for the whole view", not scoped to one section. */
.impersonation-banner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 1.75rem;
  background-color: var(--ba-corporate-surface);
  color: var(--ba-corporate-text);
  border-bottom: 1px solid var(--ba-corporate-accent);
  font-size: 0.875rem;
  flex-shrink: 0;
}

.impersonation-banner-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.impersonation-banner-text {
  flex: 1;
  min-width: 0;
}

.impersonation-banner-text strong {
  font-weight: 500;
}

.impersonation-exit {
  flex-shrink: 0;
}

.app-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background-color: var(--background);
  overflow-y: auto;
}

.app-content-header {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1rem 1.75rem;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

/* Sidebar drawer trigger: hidden md+ (reuses .nav-burger's own display
   rule), phone-only otherwise. See the responsive block below for what
   happens to .app-sidebar at the same breakpoint. */
.app-sidebar-burger {
  flex-shrink: 0;
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
}

.breadcrumb a {
  color: var(--muted-foreground);
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--foreground);
}

.breadcrumb .current {
  color: var(--foreground);
  font-weight: 500;
}

.breadcrumb-sep {
  color: var(--muted-foreground);
}

.app-content-body {
  padding: 1.75rem;
  /* Container query boundary: cards inside adapt to the column width they
     actually render in (this pane), not the raw viewport. See the .card
     rules further up for the adaptation itself. */
  container-type: inline-size;
  container-name: app-content;
}

/* Below md, .app-sidebar moves into the sidebar drawer dialog (same
   contents, duplicated markup, opened by .app-sidebar-burger above) instead
   of rendering in flow. This removes the fixed 260px width from the flex
   layout entirely, which is what was forcing .app-shell's overflow: hidden
   to clip horizontal overflow instead of the page ever needing to scroll
   sideways. .app-shell/.app-content also drop the fixed-height two-pane
   scroll model on phones in favor of natural page scroll, which is both
   simpler and friendlier to mobile browser chrome (address bar show/hide)
   than a nested overflow-y: auto pane. */
@media (max-width: 767.98px) {
  .app-sidebar {
    display: none;
  }

  .app-shell {
    height: auto;
    min-height: calc(100vh - 4rem);
    overflow: visible;
  }

  .app-content {
    overflow-y: visible;
  }
}

/* ==========================================================================
   Login / auth sample
   ========================================================================== */

.auth-shell {
  position: relative;
  min-height: calc(100vh - 4rem);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1.5rem;
  overflow: hidden;
}

.auth-shell::before {
  content: "";
  position: absolute;
  top: -12rem;
  left: 50%;
  transform: translateX(-50%);
  width: 50rem;
  height: 24rem;
  background: radial-gradient(closest-side, var(--violet-tint-3), transparent);
  z-index: -1;
  pointer-events: none;
}

.auth-card {
  width: 100%;
  max-width: 24rem;
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2.25rem;
}

.auth-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.auth-logo .logo-mark {
  height: 2rem;
  width: 9.94rem;
}

.auth-card h2 {
  text-align: center;
}

.auth-subtext {
  text-align: center;
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  margin: 0 0 1.75rem;
}

.label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

.label-row .label {
  margin-bottom: 0;
}

.text-link {
  font-size: 0.8125rem;
  color: var(--primary);
  text-decoration: none;
}

.text-link:hover {
  text-decoration: underline;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin: 1.5rem 0;
  color: var(--muted-foreground);
  font-size: 0.8125rem;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background-color: var(--border);
}

.auth-footer {
  text-align: center;
  margin-top: 1.5rem;
}

/* ==========================================================================
   Chat sample
   ========================================================================== */

.chat-shell {
  display: flex;
  height: calc(100vh - 4rem);
  overflow: hidden;
}

.chat-sidebar {
  width: 280px;
  flex-shrink: 0;
  background-color: var(--muted-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.chat-sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1rem 0.75rem;
  flex-shrink: 0;
}

.thread-list {
  list-style: none;
  margin: 0;
  padding: 0 0.65rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.thread-item {
  display: block;
  padding: 0.6rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--foreground);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.thread-item:hover {
  background-color: var(--accent);
}

.thread-item.active {
  background-color: var(--card);
  border-color: var(--border);
}

.thread-item-title {
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: 0.15rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.thread-item-preview {
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Below md, .chat-sidebar moves into the chat sidebar drawer dialog (same
   thread list, duplicated markup, opened by .chat-sidebar-burger in the
   chat header) instead of rendering in flow, the same treatment as
   .app-sidebar above and for the same reason: it removes the fixed 280px
   width that was forcing .chat-shell's overflow: hidden to clip instead of
   the page ever needing to scroll sideways, and drops the fixed-height
   two-pane scroll model in favor of natural page scroll on phones. */
@media (max-width: 767.98px) {
  .chat-sidebar {
    display: none;
  }

  .chat-shell {
    height: auto;
    min-height: calc(100vh - 4rem);
    overflow: visible;
  }

  .chat-messages {
    overflow-y: visible;
  }
}

.chat-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background-color: var(--background);
}

.chat-main-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9375rem;
  font-weight: 500;
  flex-shrink: 0;
}

.chat-main-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Chat sidebar drawer trigger: hidden md+ (reuses .nav-burger's own
   display rule), phone-only otherwise. */
.chat-sidebar-burger {
  flex-shrink: 0;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.message {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  max-width: 42rem;
}

.message-user {
  flex-direction: row-reverse;
  margin-left: auto;
}

.message-assistant {
  margin-right: auto;
}

.message-avatar {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 500;
}

.message-user .message-avatar {
  background-color: var(--secondary);
  color: var(--secondary-foreground);
}

.message-assistant .message-avatar {
  background-color: var(--primary);
  color: var(--primary-foreground);
}

.message-bubble {
  padding: 0.75rem 1rem;
  border-radius: var(--radius-lg);
  font-size: 0.9375rem;
  line-height: 1.55;
}

.message-bubble p {
  margin: 0 0 0.6rem;
}

.message-bubble p:last-child {
  margin-bottom: 0;
}

.message-bubble strong {
  font-weight: 500;
}

.message-user .message-bubble {
  background-color: var(--primary);
  color: var(--primary-foreground);
}

.message-assistant .message-bubble {
  background-color: var(--card);
  border: 1px solid var(--border);
  color: var(--foreground);
}

.message-bubble code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background-color: var(--secondary);
  padding: 0.15rem 0.4rem;
  border-radius: var(--radius-xs);
}

.message-bubble pre {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  background-color: var(--secondary);
  padding: 0.85rem 1rem;
  border-radius: var(--radius-md);
  overflow-x: auto;
  margin: 0.6rem 0;
}

.message-bubble pre code {
  background: none;
  padding: 0;
}

.chat-composer {
  border-top: 1px solid var(--border);
  padding: 1rem 1.5rem 1.25rem;
  flex-shrink: 0;
}

.composer-box {
  display: flex;
  align-items: flex-end;
  gap: 0.65rem;
  padding: 0.6rem 0.6rem 0.6rem 0.9rem;
  background-color: var(--input);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-in-out);
}

.composer-box:focus-within {
  border-color: var(--ring);
  box-shadow: var(--shadow-focus);
}

.composer-input {
  flex: 1;
  min-height: 1.5rem;
  max-height: 8rem;
  padding: 0.3rem 0;
  border: none;
  background: none;
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  line-height: 1.5;
  resize: none;
  outline: none;
}

.composer-input::placeholder {
  color: var(--muted-foreground);
}

/* ==========================================================================
   Feedback: toast / notification
   Fixed bottom-right stack, created lazily by toast.js. Variant classes
   mirror .alert's existing token mapping exactly (success = commercial
   family, warning = corporate family, danger = destructive token, info =
   muted surface), no new semantic color layer. Floating over the page, so
   unlike hairline-at-rest components it earns --shadow-lg immediately,
   the same "surface that sits above the page" exception .menu already
   uses.
   ========================================================================== */

.toast-region {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: min(22rem, calc(100vw - 2rem));
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(8px) scale(0.98);
  transition: opacity var(--dur-2) var(--ease-in-out),
              transform var(--dur-2) var(--ease-in-out);
}

.toast-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.toast-text {
  margin: 0;
  font-size: 0.9375rem;
  flex: 1;
}

.toast-close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  color: inherit;
  border-radius: var(--radius-sm);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--dur-1) var(--ease-out),
              background-color var(--dur-1) var(--ease-out);
}

.toast-close svg {
  width: 14px;
  height: 14px;
}

.toast-close:hover {
  opacity: 1;
  background-color: color-mix(in oklch, currentColor 12%, transparent);
}

.toast-close:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.toast-info {
  background-color: var(--muted-surface);
  color: var(--foreground);
}

.toast-info .toast-text {
  color: var(--muted-foreground);
}

.toast-success {
  background-color: var(--ba-commercial-surface);
  border-color: transparent;
  color: var(--ba-commercial-text);
}

.toast-warning {
  background-color: var(--ba-corporate-surface);
  border-color: transparent;
  color: var(--ba-corporate-text);
}

.toast-danger {
  background-color: var(--muted-surface);
  border-color: var(--destructive);
  color: var(--destructive);
}

.toast-danger .toast-text {
  color: var(--muted-foreground);
}

/* ==========================================================================
   Feedback: inline form validation
   .input/textarea.input/select.input had no error state at all before this.
   Reuses --destructive exclusively, no new red. The error-tinted focus ring
   below mirrors --shadow-focus's own alpha (0.32) via color-mix rather than
   inventing a second focus-ring token.
   ========================================================================== */

.input-error,
textarea.input-error,
select.input-error,
.input[aria-invalid="true"],
textarea.input[aria-invalid="true"],
select.input[aria-invalid="true"] {
  border-color: var(--destructive);
}

.input-error:focus,
textarea.input-error:focus,
select.input-error:focus,
.input[aria-invalid="true"]:focus,
textarea.input[aria-invalid="true"]:focus,
select.input[aria-invalid="true"]:focus {
  border-color: var(--destructive);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--destructive) 32%, transparent);
}

.error-text {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0.4rem 0 0;
  font-size: 0.8125rem;
  color: var(--destructive);
}

/* ==========================================================================
   Feedback: modal / confirmation dialog
   A native <dialog> (not .menu), matching how .nav-drawer already uses
   showModal() for a real focus-trapped, blocking surface. .modal is the
   generic shape (arbitrary body content); a confirmation dialog is just a
   .modal with a one-sentence body and a footer holding one destructive
   .btn-danger plus one .btn-ghost cancel, never two competing primary
   actions. Uses --radius-xl, reserved for exactly this in the token
   comment above. Wired by modal.js.
   ========================================================================== */

.modal {
  margin: auto;
  padding: 0;
  width: min(26rem, 90vw);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background-color: var(--card);
  color: var(--card-foreground);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(8px) scale(0.98);
  transition: opacity var(--dur-2) var(--ease-in-out),
              transform var(--dur-2) var(--ease-in-out),
              overlay var(--dur-2) allow-discrete,
              display var(--dur-2) allow-discrete;
}

.modal[open] {
  opacity: 1;
  transform: translateY(0) scale(1);
}

@starting-style {
  .modal[open] {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
}

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

/* Wider variant for a generic modal holding real content (a form, a list),
   rather than a one-sentence confirmation. */
.modal-wide {
  width: min(34rem, 92vw);
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.5rem 1.5rem 0;
}

.modal-title {
  margin: 0;
  font-size: 1.125rem;
}

.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  flex-shrink: 0;
  background-color: transparent;
  color: var(--muted-foreground);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out);
}

.modal-close svg {
  width: 16px;
  height: 16px;
}

.modal-close:hover {
  background-color: var(--accent);
  color: var(--foreground);
}

.modal-close:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.modal-body {
  padding: 1.25rem 1.5rem;
  color: var(--muted-foreground);
  font-size: 0.9375rem;
  line-height: 1.6;
}

.modal-body p {
  margin: 0;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  padding: 0 1.5rem 1.5rem;
}

/* Destructive action button, for the one-and-only primary action inside a
   confirmation dialog. Distinct from .btn-primary (violet) so a destructive
   confirm never reads as the everyday brand action. */
.btn-danger {
  background-color: var(--destructive);
  color: var(--destructive-foreground);
  border-color: var(--destructive);
}

.btn-danger:hover {
  opacity: 0.9;
}

/* ==========================================================================
   States: spinner
   CSS-only rotation. The blanket prefers-reduced-motion rule at the top of
   this file already forces animation-duration to 0.01ms and
   iteration-count to 1 for every animation, including this one, so a
   reduced-motion user gets a static ring instead of a spin, no extra
   override needed here.
   ========================================================================== */

.spinner {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--border);
  border-top-color: currentColor;
  border-radius: var(--radius-pill);
  animation: spinner-rotate 0.6s linear infinite;
  flex-shrink: 0;
}

.spinner-lg {
  width: 2rem;
  height: 2rem;
  border-width: 3px;
  color: var(--primary);
}

@keyframes spinner-rotate {
  to {
    transform: rotate(360deg);
  }
}

.btn .spinner {
  margin-right: 0.5rem;
  color: currentColor;
}

.btn[aria-busy="true"] {
  cursor: progress;
}

/* ==========================================================================
   States: skeleton loading
   Shimmer built from --muted-surface/--accent, same reduced-motion
   coverage as .spinner above (blanket rule handles it).
   ========================================================================== */

.skeleton {
  background: linear-gradient(100deg, var(--muted-surface) 30%, var(--accent) 50%, var(--muted-surface) 70%);
  background-size: 200% 100%;
  border-radius: var(--radius-sm);
  animation: skeleton-shimmer 1.6s ease-in-out infinite;
}

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

.skeleton-text {
  height: 0.75rem;
  margin-bottom: 0.5rem;
}

.skeleton-text:last-child {
  margin-bottom: 0;
}

.skeleton-text--sm {
  width: 55%;
}

.skeleton-avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-pill);
}

.skeleton-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
}

/* ==========================================================================
   States: empty state and error state
   Full-bounds states for when a whole section has nothing to show, or
   failed to load. .empty-state is the first real use of .gradient-weave
   as the "empty state" backdrop its own code comment has always
   documented it for. .error-state mirrors the same shape with the
   destructive token instead, no new color family for either.
   ========================================================================== */

.empty-state,
.error-state {
  position: relative;
  text-align: center;
  padding: 3rem 2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.empty-state-backdrop {
  position: absolute;
  inset: 0 0 auto 0;
  height: 7rem;
  opacity: 0.16;
  pointer-events: none;
}

.empty-state-icon,
.error-state-icon {
  position: relative;
  width: 2.75rem;
  height: 2.75rem;
  margin: 0 auto 1.25rem;
  color: var(--muted-foreground);
}

.error-state-icon {
  color: var(--destructive);
}

.empty-state h3,
.error-state h3 {
  position: relative;
  margin: 0 0 0.5rem;
}

.empty-state p,
.error-state p {
  position: relative;
  color: var(--muted-foreground);
  margin: 0 0 1.5rem;
  max-width: 26rem;
  margin-left: auto;
  margin-right: auto;
}

.empty-state .btn,
.error-state .btn {
  position: relative;
}

/* ==========================================================================
   Data display: table
   A real interactive table, distinct from .markdown-body table (that one
   renders arbitrary Markdown content, this one is an app data grid).
   Sortable-looking headers are a visual affordance only, real sort JS is
   optional. Row actions reuse .menu/.menu-item, the exact "table row
   overflow actions" case the nav decision-rule already anticipated, not a
   second popover mechanism.
   ========================================================================== */

.data-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.data-table th {
  text-align: left;
  padding: 0.75rem 1rem;
  background-color: var(--muted-surface);
  color: var(--muted-foreground);
  font-weight: 500;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.data-table th.sortable {
  cursor: pointer;
  user-select: none;
}

.data-table th.sortable .sort-icon {
  width: 12px;
  height: 12px;
  margin-left: 0.3rem;
  vertical-align: middle;
  opacity: 0.6;
}

.data-table td {
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border);
  color: var(--foreground);
}

.data-table tbody tr:last-child td {
  border-bottom: none;
}

.data-table tbody tr {
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.data-table tbody tr:hover {
  background-color: var(--accent);
}

.data-table td.actions-cell {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* Numeric/currency columns right-align (header included, so the digits'
   place values stack for at-a-glance comparison), everything else stays
   left. Never center a data column, centering makes digit place-values
   drift per row instead of lining up. See PATTERNS-REFERENCE.md, P6. */
.data-table th.numeric-cell,
.data-table td.numeric-cell {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Row action menus: one anchor pair per row, same idiom as the sidebar's
   workspace switcher/user account menus above (anchor-name binds one
   specific element, not a class). Demo table on index.html has four rows. */
#rowMenuTrigger1 { anchor-name: --row-anchor-1; }
#rowMenuTrigger2 { anchor-name: --row-anchor-2; }
#rowMenuTrigger3 { anchor-name: --row-anchor-3; }
#rowMenuTrigger4 { anchor-name: --row-anchor-4; }

#rowMenu1 { position-anchor: --row-anchor-1; }
#rowMenu2 { position-anchor: --row-anchor-2; }
#rowMenu3 { position-anchor: --row-anchor-3; }
#rowMenu4 { position-anchor: --row-anchor-4; }

.data-table .menu {
  position-area: bottom span-left;
  position-try-fallbacks: flip-block;
  margin-block: 0.35rem;
}

/* ==========================================================================
   Data display: list
   A vertical list of records (label + secondary text + trailing action),
   distinct from .sidebar-nav which is navigation-specific.
   ========================================================================== */

.list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.list-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

.list-item:last-child {
  border-bottom: none;
}

.list-item-body {
  flex: 1;
  min-width: 0;
}

.list-item-label {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--foreground);
}

.list-item-secondary {
  margin: 0.15rem 0 0;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}

.list-item-action {
  flex-shrink: 0;
}

/* ==========================================================================
   Data display: pagination
   Previous/next plus page numbers. Below 640px the word labels drop and
   only the arrow buttons remain, per the responsive rule already
   documented in DAPPLE-DESIGN-SYSTEM.md section 7.
   ========================================================================== */

.pagination {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.pagination-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  justify-content: center;
  min-width: 2.25rem;
  height: 2.25rem;
  padding: 0 0.65rem;
  background-color: var(--card);
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-family: var(--font-sans);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out);
}

.pagination-btn svg {
  width: 14px;
  height: 14px;
}

.pagination-btn:hover {
  background-color: var(--accent);
}

.pagination-btn[aria-current="page"] {
  background-color: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
}

.pagination-btn:disabled {
  cursor: not-allowed;
  color: var(--muted-foreground);
  opacity: 0.5;
}

.pagination-btn:disabled:hover {
  background-color: var(--card);
}

.pagination-btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

@media (max-width: 639.98px) {
  .pagination-page,
  .pagination-label {
    display: none;
  }
}

/* ==========================================================================
   Tooltip
   Hover-triggered, but also focus-triggered (:focus-within on the wrapper
   fires from a keyboard-focused or tapped-then-focused trigger), never
   hover-only, per this system's own responsive rule 5.
   ========================================================================== */

.tooltip {
  position: relative;
  display: inline-flex;
}

.tooltip-bubble {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  padding: 0.35rem 0.65rem;
  background-color: var(--popover);
  color: var(--popover-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  font-size: 0.8125rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  z-index: 30;
  transition: opacity var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out);
}

.tooltip:hover .tooltip-bubble,
.tooltip:focus-within .tooltip-bubble {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ==========================================================================
   Tabs
   Real ARIA tabs (role="tablist"/"tab"/"tabpanel"), roving tabindex and
   arrow-key navigation wired by tabs.js, the same idiom menu.js already
   established for .menu-item.
   ========================================================================== */

.tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border);
}

.tab {
  padding: 0.65rem 1rem;
  margin-bottom: -1px;
  background: transparent;
  color: var(--muted-foreground);
  border: none;
  border-bottom: 2px solid transparent;
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--dur-2) var(--ease-in-out),
              border-color var(--dur-2) var(--ease-in-out);
}

.tab:hover {
  color: var(--foreground);
}

.tab[aria-selected="true"] {
  color: var(--foreground);
  border-color: var(--primary);
}

.tab:focus-visible {
  outline: none;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-focus);
}

.tab-panels {
  padding: 1.5rem 0;
}

.tab-panel[hidden] {
  display: none;
}

/* ==========================================================================
   Chip
   An interactive tag: removable or toggleable, distinct from .badge (a
   static status label). Reuses .secondary/.accent tokens, no new color.
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem 0.3rem 0.75rem;
  background-color: var(--secondary);
  color: var(--secondary-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 0.8125rem;
  font-weight: 500;
  font-family: var(--font-sans);
}

.chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  padding: 0;
  background: transparent;
  color: var(--muted-foreground);
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}

.chip-remove svg {
  width: 11px;
  height: 11px;
}

.chip-remove:hover {
  background-color: var(--accent);
  color: var(--foreground);
}

.chip-remove:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/* A chip that toggles selected state rather than being removed (e.g. a
   filter). aria-pressed carries the state, no separate class needed for
   the pressed look beyond the attribute selector below. */
.chip-toggle {
  cursor: pointer;
  background-color: var(--card);
}

.chip-toggle[aria-pressed="true"] {
  background-color: var(--accent);
  border-color: var(--border-violet);
  color: var(--foreground);
}

/* ==========================================================================
   Split view (split.html + split.js) — DAPPLE-DESIGN-SYSTEM.md section 7
   rule 9's full-bleed shell family, pattern per Apple HIG "Split views":
   a primary pane (the list) drives a secondary pane (the detail), with an
   optional tertiary inspector. Widths:
     >=1280px  list + detail + inspector (inspector toggleable)
     768-1279  list + detail, inspector becomes an overlay <dialog>
     <768      navigation stack: list OR detail full-screen, back returns,
               wired to location.hash so the browser back button works
               (split.js). data-view="list|detail" on the shell drives it.
   ========================================================================== */

.split-shell {
  display: flex;
  height: calc(100vh - 4rem);
  overflow: hidden;
}

/* -- primary pane: the list ------------------------------------------------ */

.split-list {
  width: 320px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  background-color: var(--background);
}

.split-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1rem 0.75rem;
  flex-shrink: 0;
}

.split-list-title {
  font-size: 0.9375rem;
  font-weight: 500;
}

.split-list-count {
  font-size: 0.75rem;
  color: var(--muted-foreground);
}

.split-items {
  list-style: none;
  margin: 0;
  padding: 0 0.5rem 1rem;
  overflow-y: auto;
  flex: 1;
}

.split-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  padding: 0.7rem 0.75rem;
  border-radius: var(--radius-md);
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.split-item:hover {
  background-color: var(--accent);
}

.split-item:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.split-item[aria-current='true'] {
  background-color: var(--accent);
}

.split-item-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.split-item-title {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--foreground);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.split-item-time {
  font-size: 0.6875rem;
  color: var(--muted-foreground);
  flex: none;
}

.split-item-preview {
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  margin: 0.15rem 0 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* -- secondary pane: the detail -------------------------------------------- */

.split-detail {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background-color: var(--background);
}

.split-detail-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1.5rem;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  min-height: 3.5rem;
}

.split-back {
  display: none; /* phone-only, see the media query below */
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--foreground);
  cursor: pointer;
  flex: none;
  transition: background-color var(--dur-2) var(--ease-in-out);
}

.split-back:hover {
  background-color: var(--accent);
}

.split-back:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.split-back svg {
  width: 18px;
  height: 18px;
}

.split-detail-title {
  font-size: 0.9375rem;
  font-weight: 500;
  margin: 0;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.split-detail-body {
  flex: 1;
  overflow-y: auto;
  padding: 1.5rem;
  max-width: 46rem;
}

.split-detail-body h2 {
  font-size: 1.35rem;
  font-weight: 500;
  margin: 0 0 0.75rem;
}

.split-detail-body p {
  line-height: 1.7;
  margin: 0 0 1rem;
}

.split-pane[hidden] {
  display: none;
}

/* -- tertiary pane: the inspector ------------------------------------------ */

.split-inspector {
  width: 300px;
  flex-shrink: 0;
  border-left: 1px solid var(--border);
  background-color: var(--muted-surface);
  overflow-y: auto;
  padding: 1.25rem;
}

.split-inspector h3 {
  font-size: 0.8125rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-foreground);
  margin: 0 0 0.75rem;
}

.split-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.4rem 1rem;
  font-size: 0.8125rem;
  margin: 0;
}

.split-meta dt {
  color: var(--muted-foreground);
}

.split-meta dd {
  margin: 0;
  color: var(--foreground);
}

/* [hidden] loses to the class's display: grid at equal specificity, make
   the hidden state explicit, same guard .split-pane[hidden] has. */
.split-meta-pane[hidden] {
  display: none;
}

/* Inspector demotes to an overlay <dialog> below xl. The same markup is
   used in both renditions: split.js moves the inspector content into the
   dialog when the pane is hidden, so there is exactly one copy in the DOM
   at a time (same single-source stance as the localnav disclosure). */
/* Same right-edge sheet geometry and entry motion as .nav-drawer (the
   shared block lives with .nav-drawer's rules), only the padding differs:
   the drawer's inner wrapper pads itself, the inspector dialog holds
   plain content. */
.split-inspector-dialog {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(20rem, 85vw);
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 1.25rem;
  border: none;
  border-left: 1px solid var(--border);
  background-color: var(--background);
  color: var(--foreground);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition:
    transform var(--dur-2) var(--ease-in-out),
    overlay var(--dur-2) allow-discrete,
    display var(--dur-2) allow-discrete;
}

.split-inspector-dialog[open] {
  transform: translateX(0);
}

@starting-style {
  .split-inspector-dialog[open] {
    transform: translateX(100%);
  }
}

.split-inspector-dialog::backdrop {
  background-color: var(--overlay-backdrop);
}

@media (max-width: 1279.98px) {
  .split-inspector {
    display: none;
  }
}

@media (min-width: 1280px) {
  .split-shell[data-inspector='closed'] .split-inspector {
    display: none;
  }
}

/* -- phone: navigation stack ------------------------------------------------ */

@media (max-width: 767.98px) {
  .split-list {
    width: 100%;
    border-right: none;
  }

  .split-shell[data-view='list'] .split-detail {
    display: none;
  }

  .split-shell[data-view='detail'] .split-list {
    display: none;
  }

  .split-back {
    display: inline-flex;
  }

  .split-detail-header {
    padding: 0.75rem 1rem;
  }

  .split-detail-body {
    padding: 1.25rem 1rem;
  }
}

/* ==========================================================================
   Wizard (wizard.html + wizard.js) — a multi-step form, Apple setup-flow
   anatomy: single centered column, one question group per step, generous
   spacing, one primary action, quiet back link. Native HTML validation
   gates each step (wizard.js calls checkValidity(), the browser paints
   :user-invalid), the review step summarizes everything with per-step
   edit links, then a success state. Prefer fewer steps over more chrome:
   if the whole thing fits one screen comfortably, use a plain form, not
   this.
   ========================================================================== */

.wizard-shell {
  max-width: 34rem;
  margin: 0 auto;
  padding: 3rem 1rem 4.5rem;
}

.wizard-steps {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  margin: 0 0 2rem;
  padding: 0;
}

.wizard-steps li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}

.wizard-step-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 1px solid var(--border);
  background-color: var(--card);
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted-foreground);
  flex: none;
  transition:
    background-color var(--dur-2) var(--ease-in-out),
    color var(--dur-2) var(--ease-in-out),
    border-color var(--dur-2) var(--ease-in-out);
}

.wizard-steps li[aria-current='step'] {
  color: var(--foreground);
  font-weight: 500;
}

.wizard-steps li[aria-current='step'] .wizard-step-dot {
  background-color: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
}

.wizard-steps li[data-done='true'] .wizard-step-dot {
  border-color: var(--border-violet);
  color: var(--foreground);
}

/* Connector line between step labels. */
.wizard-steps li + li::before {
  content: '';
  width: 1.25rem;
  height: 1px;
  background-color: var(--border);
  margin-right: 0.4rem;
}

/* Phone: dots-and-labels collapse to plain "Step 2 of 4" text. */
.wizard-steps-compact {
  display: none;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  margin: 0 0 1.5rem;
}

@media (max-width: 767.98px) {
  .wizard-steps {
    display: none;
  }

  .wizard-steps-compact {
    display: block;
  }
}

.wizard-step {
  /* steps are <fieldset>s for semantics, drop the browser chrome */
  border: none;
  padding: 0;
  margin: 0;
  min-width: 0;
}

.wizard-step[hidden] {
  display: none;
}

.wizard-step h1:focus {
  outline: none;
}

.wizard-step h1 {
  font-size: 1.35rem;
  font-weight: 500;
  margin: 0 0 0.35rem;
}

.wizard-step-lead {
  color: var(--muted-foreground);
  margin: 0 0 1.5rem;
}

.wizard-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2rem;
}

.wizard-back {
  background: none;
  border: none;
  padding: 0.5rem 0.25rem;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--muted-foreground);
  cursor: pointer;
  transition: color var(--dur-2) var(--ease-in-out);
}

.wizard-back:hover {
  color: var(--foreground);
}

.wizard-back:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}

/* Review step: definition list of everything entered, one block per step
   with a quiet edit link back to it. */
.wizard-review-block {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
  margin-bottom: 1rem;
}

.wizard-review-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

.wizard-review-title {
  font-size: 0.875rem;
  font-weight: 500;
  margin: 0;
}

.wizard-review dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 1.25rem;
  font-size: 0.875rem;
  margin: 0;
}

.wizard-review dt {
  color: var(--muted-foreground);
}

.wizard-review dd {
  margin: 0;
}

/* Success state. */
.wizard-done {
  text-align: center;
  padding: 3rem 0;
}

.wizard-done-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background-color: var(--ba-commercial-surface);
  color: var(--ba-commercial-text);
  margin-bottom: 1rem;
}

.wizard-done-icon svg {
  width: 22px;
  height: 22px;
}

/* Priority+ nav overflow (rule 10): the More trigger reads exactly like an
   inline .nav-links link, its chevron marks it as a menu. When the current
   page lives inside the overflow, the trigger carries .active the same way
   an inline link would. */
.nav-more {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.5rem 0.75rem;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--muted-foreground);
  font: inherit;
  font-size: 0.9375rem;
  cursor: pointer;
  transition:
    color var(--dur-2) var(--ease-in-out),
    background-color var(--dur-2) var(--ease-in-out);
}

.nav-more:hover {
  color: var(--foreground);
  background-color: var(--accent);
}

.nav-more:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.nav-more[aria-current] {
  color: var(--foreground);
  font-weight: 500;
}

.nav-more svg {
  width: 14px;
  height: 14px;
}

.menu-item[aria-current='page'] {
  color: var(--foreground);
  font-weight: 500;
}

/* ==========================================================================
   Table page (table.html + table.js) — composes the P6 data-display
   pieces into a full CRUD page: toolbar (search + filters + demo state
   chips), selectable rows with a bulk bar, working sort on the sortable
   headers, pagination, and the loading/empty states inline. The template
   is the markup/CSS contract, a real app owns data behavior server-side,
   table.js only demonstrates the client contract.
   ========================================================================== */

.table-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin: 2rem 0 1rem;
}

.table-search {
  max-width: 16rem;
}

.table-toolbar .select-wrapper {
  width: auto;
}

.table-toolbar .select-wrapper .input {
  width: auto;
  min-width: 9rem;
}

.table-states {
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
}

/* Bulk bar: appears between toolbar and table once anything is selected. */
.bulk-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.75rem;
  background-color: var(--accent);
  border: 1px solid var(--border-violet);
  border-radius: var(--radius-md);
}

.bulk-bar[hidden] {
  display: none;
}

.bulk-count {
  font-size: 0.875rem;
  font-weight: 500;
  margin-right: auto;
}

.data-table .check-cell {
  width: 2.25rem;
  padding-right: 0;
}

.data-table .check-cell input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--primary);
}

/* Sortable headers are real buttons (keyboard, focus ring), inheriting
   the th's type treatment. aria-sort on the th drives the icon. */
.sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

.sort-btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-xs);
}

.data-table th[aria-sort='ascending'] .sort-icon {
  transform: rotate(180deg);
}

.data-table th[aria-sort='ascending'],
.data-table th[aria-sort='descending'] {
  color: var(--foreground);
}

/* Demo states: the wrap swaps between table, loading skeleton, empty. */
.table-loading {
  padding: 1.25rem;
}

.data-table-wrap[data-state='loading'] .data-table,
.data-table-wrap[data-state='empty'] .data-table {
  display: none;
}

.data-table-wrap[data-state='default'] .table-loading,
.data-table-wrap[data-state='empty'] .table-loading {
  display: none;
}

.data-table-wrap[data-state='default'] .table-empty,
.data-table-wrap[data-state='loading'] .table-empty {
  display: none;
}

/* Priority-collapse columns (section 7 rule 6's order: reflow > collapse >
   scroll): the least important columns drop first as width shrinks, the
   wrap's overflow-x scroll only has to carry what survives. */
@media (max-width: 1023.98px) {
  .data-table .priority-3 {
    display: none;
  }
}

@media (max-width: 767.98px) {
  .data-table .priority-2 {
    display: none;
  }

  .table-search {
    max-width: none;
    flex: 1 1 100%;
  }

  .table-states {
    margin-left: 0;
  }
}

/* ==========================================================================
   Dashboard (dashboard.html) — KPI stat cards, hand-written inline SVG
   charts (no chart library, the zero-build bar applies to charts too),
   and a recent-activity table reusing .data-table. Chart series colors
   follow the documented order: violet first, then the seven business-area
   accents (section 9's chart rule applies on the web too). Single-series
   charts are violet only.
   ========================================================================== */

.dashboard-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1rem;
  margin: 2rem 0 1.5rem;
}

.stat-card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
}

.stat-value {
  font-size: 1.75rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.stat-label {
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  margin-top: 0.15rem;
}

.stat-delta {
  margin-top: 0.5rem;
}

.dashboard-charts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

@media (min-width: 768px) {
  .dashboard-charts {
    grid-template-columns: 1fr 1fr;
  }
}

.chart-card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
}

.chart-title {
  font-size: 0.9375rem;
  font-weight: 500;
  margin: 0 0 0.15rem;
}

.chart-caption {
  font-size: 0.8125rem;
  color: var(--muted-foreground);
  margin: 0 0 1rem;
}

.chart-card svg {
  display: block;
  width: 100%;
  height: auto;
}

.chart-card svg text {
  font-family: var(--font-sans);
  font-size: 10px;
  fill: var(--muted-foreground);
}

.chart-legend {
  display: flex;
  gap: 1rem;
  margin-top: 0.75rem;
  font-size: 0.75rem;
  color: var(--muted-foreground);
}

.chart-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.chart-legend i {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 2px;
  font-style: normal;
}
