/* Single source of truth for design tokens.
   All primitive names mirror the Pluralx × Alfadocs brand guide
   (April 2026, "Test color palette su Schermate esemplificative"):
     Brand:    blue-*, violet-*, purple-*, magenta-*, grey-*, fuchsia-*
     Semantic: green-*, red-*, yellow-*, indigo-*, orange-*
   `fuchsia-*` is the dark-mode Accent Violet; `magenta-*` is the
   light-mode Accent Violet — both back the brand "Accent Violet"
   family but at different brightness for contrast reasons.
   See src/brand/colors.mdx for the authoritative rendering.

   All tokens live in the `ui-kit-tokens` cascade layer so consuming apps
   can deterministically order legacy CSS (e.g. a host Bootstrap / Material
   sheet) against the kit. See docs/04-theming.mdx §Coexistence with
   legacy CSS for the recommended @import incantation. */

@layer ui-kit-tokens {

/* ------------------------------------------------------------------ */
/* Self-hosted brand font — Space Grotesk variable, Latin subset.      */
/* Shipped with the package so consumers don't have to wire a <link>   */
/* to Google Fonts in their own template. Non-Latin scripts fall       */
/* through to Noto Sans SC / JP / Arabic via the --font-sans stack.    */
/* ------------------------------------------------------------------ */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('./fonts/SpaceGrotesk-VF.woff2') format('woff2-variations'),
       url('./fonts/SpaceGrotesk-VF.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root,
.theme-light {
  /* ------------------------------------------------------------------ */
  /* Primitive — Dark Blue ramp (blue-500 = Main, Pantone 2119 C)        */
  /* ------------------------------------------------------------------ */
  --color-blue-100: #bdbeca;
  --color-blue-200: #9d9fb0;
  --color-blue-300: #70738c;
  --color-blue-400: #545875;
  --color-blue-500: #292e53;
  --color-blue-600: #252a4c;
  --color-blue-700: #1d213b;
  --color-blue-800: #17192e;
  --color-blue-900: #111323;

  /* ------------------------------------------------------------------ */
  /* Primitive — Brand Violet ramp (violet-500 = Main, Pantone 2125 C)   */
  /* ------------------------------------------------------------------ */
  --color-violet-500: #6761e5;
  --color-violet-700: #4945a3;
  --color-violet-800: #39357e;

  /* ------------------------------------------------------------------ */
  /* Primitive — Accent Purple ramp (purple-500 = Main, Pantone 2645 C)  */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Primitive — Magenta ramp (light-mode Accent Violet, magenta-500 =   */
  /* light-bg Main). Paired with fuchsia-* for the brighter dark-mode    */
  /* Accent Violet defined below. No Pantone — the brand guide assigns   */
  /* Pantone 2385 C to the fuchsia ramp, not this one.                   */
  /* ------------------------------------------------------------------ */
  --color-magenta-300: #ea7ffc;
  --color-magenta-500: #e040fb;
  --color-magenta-700: #9f2db2;
  --color-magenta-800: #7b238a;

  /* ------------------------------------------------------------------ */
  /* Primitive — Warm Grey ramp (grey-500 = Main, Pantone P 179-1 C)     */
  /* ------------------------------------------------------------------ */
  --color-grey-100: #fcfbf9;
  --color-grey-200: #fbf9f7;
  --color-grey-500: #f6f1ed;
  --color-grey-600: #e0dbd8;
  --color-grey-700: #afaba8;

  /* ------------------------------------------------------------------ */
  /* Primitive — Fuchsia ramp (fuchsia-500 = dark-mode Accent Violet,    */
  /* Pantone 2385 C). Apr 2026 brand guide introduces this as the dark   */
  /* pair of the Accent Violet family. It is NOT auto-wired to --primary */
  /* / --accent (that over-saturates every stat tile, active pill, and   */
  /* unread badge); reach for it explicitly — e.g. --scrollbar-thumb,    */
  /* --now-indicator, hover-glow shadow, per-element CTAs. Fails AA on   */
  /* white so never used directly on light surfaces.                    */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Semantic primitive — Green ramp (green-500 = Main). Backs           */
  /* --success and related tokens. Main-500 fails AA on white; semantic  */
  /* aliases pick the step (usually 600–800) that meets contrast.        */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Semantic primitive — Red ramp (red-500 = Main). Backs --destructive */
  /* and --error. Main-500 fails AA on white for normal text; solid      */
  /* destructive surfaces use red-700 so white text passes AA.           */
  /* ------------------------------------------------------------------ */
  --color-red-100: #fec3cc;
  --color-red-400: #fc657d;
  --color-red-700: #b22d41;
  --color-red-800: #8a2333;
  --color-red-900: #691a27;

  /* ------------------------------------------------------------------ */
  /* Semantic primitive — Yellow ramp (yellow-500 = Main). Backs         */
  /* --warning. Yellow is a light hue; solid warning surfaces use        */
  /* yellow-500 with --color-blue-900 text, not white.                   */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Semantic primitive — Indigo ramp (indigo-500 = Main). Backs --info. */
  /* Distinct from brand Dark Blue (--color-blue-*): Indigo is a bright  */
  /* saturated informational hue, not a near-black text surface.         */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Semantic primitive — Orange ramp (orange-500 = Main). Reserved for  */
  /* caution / accent scenarios (e.g. loud badges, highlight dots).      */
  /* Not currently wired to a semantic alias.                            */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Spacing
     Base unit is 0.25rem (4px). Tailwind multiplies `var(--spacing) * N`
     to reach the full 4/8/12/16/24/32/48/64/96 scale.                    */
  /* ------------------------------------------------------------------ */
  --spacing-xs: 0.25rem;   /*   4px */
  --spacing-sm: 0.5rem;    /*   8px */
  --spacing-md: 1rem;      /*  16px */
  --spacing-lg: 1.5rem;    /*  24px */

  /* Skeleton placeholder dimensions — used by loading states. */

  /* Minimum touch target — WCAG 2.5.5 (AA) / 2.5.8 (AAA). */
  --min-target-size: 44px;

  /* DataTable compact row height — intentionally below min-target-size (32px).
     Only valid for dense data views; must not be applied in accessible themes. */

  /* DataTable expanded density — roomy rows for complex cells (balance breakdowns,
     tag lists, nested key/value content). */

  /* Icon sizes */

  /* Logo block-size scale — inline-size flows via aspect ratio. */
  --logo-size-md: 24px;

  /* Resizable handle thickness */

  /* Popover / tooltip max-widths */

  /* Horizontal-oriented FormField label column. Consuming apps override
     per locale when translated labels overflow (German, Polish, etc.). */

  /* ------------------------------------------------------------------ */
  /* Radius                                                              */
  /* ------------------------------------------------------------------ */
  --radius-sm: 4px;
  --radius-md: 8px;

  /* ------------------------------------------------------------------ */
  /* Borders & Focus rings
     Focus-ring width / offset are overridden in `.theme-accessible` to
     raise ring thickness and offset for WCAG 2.4.11 / 2.4.13.           */
  /* ------------------------------------------------------------------ */
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;

  /* ------------------------------------------------------------------ */
  /* Shadows — brand-tinted
     `sm → 2xl` is the elevation scale; `card` / `hover` are semantic
     aliases used by Card + hover affordances. Dark theme re-tints the
     hover glow to Accent Violet (fuchsia) so elevation reads distinct. */
  /* ------------------------------------------------------------------ */
  --shadow-md: 0 4px 12px #00000014;
  --shadow-lg: 0 10px 30px #0000000f;
  /* Chrome dividers — directional shadows that separate a sticky header or
     footer from a scrolling body region. Kept deliberately light so they
     read as an edge cue rather than a floating card. */

  /* Focus halo — two-layer box-shadow for rounded elements where `outline`
     looks too sharp. Inner layer matches the surface (--background) to
     create the offset gap; outer layer is the ring colour. Consumers combine
     with their own base shadow, e.g.
       box-shadow: var(--shadow-focus), var(--shadow-sm);                 */
  --shadow-focus: 0 0 0 var(--focus-ring-offset) var(--background),
    0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) var(--ring);

  /* ------------------------------------------------------------------ */
  /* Typography
     In-app surfaces use Space Grotesk only. Fraunces is a marketing-only
     display face — exposed as --font-serif for marketing pages to opt
     into explicitly. Semantic aliases (--font-main, --font-label) both
     resolve to Space Grotesk so no in-app component can reach for the
     serif accidentally.                                                  */
  /* ------------------------------------------------------------------ */
  --font-sans: 'Space Grotesk', 'Noto Sans SC', 'Noto Sans JP',
    'Noto Sans Arabic', 'Noto Sans Devanagari', ui-sans-serif, system-ui,
    sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular,
    Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;

  /* Font weights — Space Grotesk ships 300/400/500/600/700; Fraunces 600 (marketing) */

  /* Line-height — tuned for CJK glyph ascenders without walls-of-text feel. */

  /* Font sizes */
  --font-size-xs:   0.75rem;   /*  12px */
  --font-size-sm:   0.875rem;  /*  14px */
  --font-size-base: 1rem;      /*  16px */

  /* Letter spacing */

  /* ------------------------------------------------------------------ */
  /* Type roles — semantic bundles on top of the primitives above.       */
  /* Call sites use the `.type-<role>` component classes at the bottom   */
  /* of this file, not the raw `--font-size-*` / `--font-weight-*`       */
  /* primitives. Each role exposes the same 7-var fingerprint            */
  /* (family / size / weight / line-height / tracking / transform /      */
  /* features) so themes can override any axis uniformly —               */
  /* .theme-accessible below lifts the size of every heading, body,      */
  /* label, and meta role one step up.                                    */
  /* ------------------------------------------------------------------ */

  /* Page title — H1, once per view. */

  /* Section title — H2 of a headed region inside a page
     (WarningStack, NotificationsPanel, settings sections). */

  /* Card title — H3, a single card's own heading. */

  /* Item title — H4, a row/card inside a list or stack
     (Alert title, NotificationCard title, WarningStack item title). */

  /* Body — primary reading text, dialog copy. */

  /* Body-small — descriptions under titles, help text, alert body,
     notification body, dense body runs. */

  /* Label — form labels, stat labels, trend/delta, inline field labels. */

  /* Eyebrow — uppercase micro-caption over a section, chip captions,
     tray eyebrow labels. Weight is medium (500), not light (300): at 12px
     with expanded tracking, uppercase + light combines to the weakest
     readable variant (worst for dyslexia / low vision). Medium keeps the
     eyebrow legible while still visually quieter than adjacent labels. */

  /* Meta — timestamps, metadata rows, secondary descriptors
     ("2 minutes ago", "16 settings to complete"). */

  /* Metric — Stat / gauge numeric values. Size defaults to 3xl; Stat's
     CVA size variants override `--type-metric-size` per-variant.
     `tabular` keeps digits column-aligned across rows. */

  /* Badge */

  /* ------------------------------------------------------------------ */
  /* Component-scoped dimension tokens — added for token-coverage-gaps.
     Each one absorbs a recurring hardcoded value from the components     */
  /* batch so future changes land in one place.                           */
  /* ------------------------------------------------------------------ */

  /* 1px hairlines (stepper separator, pdf-viewer toolbar divider,
     command-palette separator). Bumps to 2px in accessible themes below. */

  /* Dialog / Popover width scale — swap the hardcoded `max-w-[…px]`
     in command-palette (640px), rich-text-editor shortcuts (480px),
     freemium-paywall (36rem) for these tokens. */

  /* Chart density heights — absorbs chart.tsx's hardcoded min-h values. */

  /* Header — app chrome density tokens. */
  --header-height-lg: 64px;

  /* Sidebar — modern variant tokens. The classic variant keeps its
     w-16 / w-64 Tailwind sizes to avoid churn; the modern variant opts
     into a tighter 64px icon rail, 248px expanded rail, and 40px icon
     tile for the filled-pill active state. See sidebar-modern-refresh. */

  /* Signature pad default dimensions — absorbs the 400 / 200 numeric
     fallbacks in signature-capture. */

  /* Arrow glyph size shared across overlay primitives (Tooltip,
     Popover, DropdownMenu, NavigationMenu). */

  /* AudioVisualiser + TypingIndicator spec. */

  /* Recording-pulse / typing-indicator slow cadence (independent of
     `--animation-duration` so reduced-motion simply disables animation
     rather than shortening it to zero). */

  /* Rich-text-editor minimums. */

  /* Now-indicator (FullCalendar). Magenta-700 passes AA on light in a
     way magenta-500 doesn't; dark mode uses the brighter value for
     perceptual parity. */

  /* ------------------------------------------------------------------ */
  /* TranscriptPanel — speaker diarization palette
     Colour alone never conveys identity (WCAG 1.4.1); paired with a
     visible label in TranscriptPanel. Backgrounds meet 3:1 against
     the default text colour.                                            */
  /* ------------------------------------------------------------------ */

  /* TypingIndicator dot loop — independent of --animation-duration
     so the typing cadence stays consistent even under reduced motion,
     where this keyframe simply does not run. */

  /* ------------------------------------------------------------------ */
  /* Motion
     `--animation-duration` is the single global knob every component
     reads; it drops to 0ms in `.theme-accessible` and under
     `prefers-reduced-motion: reduce` (see @media rule at end of file). */
  /* ------------------------------------------------------------------ */
  --duration-normal: 200ms;
  --animation-duration: var(--duration-normal);

  /* ------------------------------------------------------------------ */
  /* Opacity ladder — the only alpha values shipped components may use. */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Scrollbar                                                           */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Z-index — named layer scale with deliberate 10-unit gaps.           */
  /* ------------------------------------------------------------------ */
  --z-dropdown: 10;
  --z-sticky: 20;

  /* ------------------------------------------------------------------ */
  /* Breakpoints — reference tokens mirrored by Tailwind's screens.
     CSS media queries can't read custom properties, so these exist for
     docs parity; in code use the `sm:` / `md:` / `lg:` / `xl:` / `2xl:`
     Tailwind variants which resolve to the same pixel widths.           */
  /* ------------------------------------------------------------------ */

  /* ------------------------------------------------------------------ */
  /* Semantic — light theme                                              */
  /* ------------------------------------------------------------------ */
  --primary: var(--color-violet-500);
  --primary-foreground: #ffffff;

  --accent: var(--color-magenta-700);

  --destructive: var(--color-red-700);
  --destructive-foreground: #ffffff;

  --background: #ffffff;
  --foreground: var(--color-blue-500);
  --muted: var(--color-blue-300);
  --muted-foreground: var(--color-blue-400);
  --border: var(--color-grey-600);
  --ring: var(--primary);

  /* Progress / meter track. A primary-tinted surface that stays visible
     against the page and gives the filled stripe real hue-contrast against
     the track. color-mix resolves at computed-time, so this automatically
     re-tints when --primary shifts between theme-light / theme-dark / the
     accessible variants — no per-theme override needed. */

  --popover: var(--background);
  --popover-foreground: var(--foreground);

}

.theme-dark {
  /* Dark-mode primary / accent stay on magenta-500. The Apr 2026 brand
     guide introduces a brighter "Accent Violet" (fuchsia-*) ramp for
     dark surfaces, but used as the semantic --primary it saturates the
     whole UI (every stat tile, active sidebar pill, and unread-badge
     becomes hot pink). Fuchsia stays in the palette as an opt-in
     primitive — per-element `var(--color-fuchsia-500)` — and lights up
     a handful of intentional accent spots (now-indicator, hover glow,
     scrollbar thumb). Matches the PDF mockups, which reserve fuchsia
     for CTAs rather than blanketing every surface. */
  --primary: var(--color-magenta-500);
  --primary-foreground: #ffffff;

  --accent: var(--color-magenta-500);

  --destructive: var(--color-red-400);
  --destructive-foreground: #ffffff;

  --background: var(--color-blue-500);
  --foreground: var(--color-grey-500);
  --muted: var(--color-blue-200);
  --muted-foreground: var(--color-blue-100);
  --border: var(--color-violet-700);
  --ring: var(--accent);

  --popover: var(--color-blue-600);
  --popover-foreground: var(--color-grey-100);

  /* Dark-mode shadows: deeper card, fuchsia-tinted hover glow (brand
     guide dark-mode accent). */
  --shadow-md: 0 4px 12px #00000066;
  --shadow-lg: 0 10px 30px #00000066;

  /* Now-indicator — dark mode uses fuchsia-500 so the indicator lands
     on the same brighter magenta the brand guide picks for dark. */

  /* Dark-mode transcript speaker palette — the 700 steps hit 3:1 against
     the lighter dark-mode foreground; the 200 steps used on light do not.
     Magenta keeps its speaker slot so identity is stable across themes. */
}

.theme-accessible {
  /* AAA-leaning overrides on a light surface: darker primary, darker text. */
  --primary: var(--color-violet-800);
  --primary-foreground: #ffffff;

  --accent: var(--color-magenta-800);

  --destructive: var(--color-red-800);
  --destructive-foreground: #ffffff;

  --foreground: var(--color-blue-900);
  --muted: var(--color-blue-700);
  --muted-foreground: var(--color-blue-800);
  --border: var(--color-blue-400);
  --ring: var(--primary);

  /* Raised focus-ring + larger touch target per WCAG 2.4.11 / 2.5.8. */
  --focus-ring-width: 3px;
  --focus-ring-offset: 3px;
  --min-target-size: 48px;

  /* Logo scale bumps in accessible mode for low-vision reinforcement. */
  --logo-size-md: 32px;

  /* Motion off — static UI for vestibular sensitivity (WCAG 2.3.3). */
  --animation-duration: 0ms;

  /* Type roles — lift size one step across every role (except metric,
     whose sizing belongs to Stat's CVA size variants, and the gauge
     center which is set inline per-component). This matches the
     --focus-ring-width / --min-target-size lifts and delivers the
     "16px → 18px base body" shift documented in docs/04-theming.mdx. */
}

/* Dark + Accessible composes: apply .theme-dark first, then .theme-accessible
   re-overrides the variables that need to move in the OPPOSITE direction on
   a dark surface (lift primary, lift muted, lift border, max foreground). */
.theme-dark.theme-accessible {
  /* Dark-accessible primary keeps the lifted magenta pairing from before
     the Apr 2026 brand refresh. Fuchsia-300 would overshoot saturation
     across every --primary surface — see the .theme-dark comment. */
  --primary: var(--color-magenta-300);
  --primary-foreground: var(--color-blue-900);

  --accent: var(--color-magenta-300);

  --destructive: var(--color-red-100);
  --destructive-foreground: var(--color-red-900);

  --foreground: #ffffff;
  --background: var(--color-blue-800);
  --muted: var(--color-grey-700);
  --muted-foreground: var(--color-grey-600);
  --border: var(--color-blue-200);
  --ring: var(--primary);

}

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-main);
}

/* -------------------------------------------------------------------- */
/* Type-role component classes.                                         */
/*                                                                      */
/* Each class composes a role's 7-var fingerprint into one utility so   */
/* call sites never re-specify size + weight + line-height + tracking   */
/* + transform + features at the point of use. Colour is intentionally  */
/* NOT set here — roles control typography, surfaces control colour     */
/* (pair with `text-[var(--foreground)]` / `text-[var(--muted-         */
/* foreground)]` / semantic tones at the call site).                    */
/*                                                                      */
/* Any override — accessible theme, dark theme, locale-specific         */
/* tightening — happens by re-declaring the underlying                  */
/* `--type-<role>-<axis>` variable, never by re-authoring these rules.  */
/* -------------------------------------------------------------------- */
@layer components {
  .type-title-page {
    font-family: var(--type-title-page-family);
    font-size: var(--type-title-page-size);
    font-weight: var(--type-title-page-weight);
    line-height: var(--type-title-page-line-height);
    letter-spacing: var(--type-title-page-tracking);
    text-transform: var(--type-title-page-transform);
    font-feature-settings: var(--type-title-page-features);
  }

  .type-title-section {
    font-family: var(--type-title-section-family);
    font-size: var(--type-title-section-size);
    font-weight: var(--type-title-section-weight);
    line-height: var(--type-title-section-line-height);
    letter-spacing: var(--type-title-section-tracking);
    text-transform: var(--type-title-section-transform);
    font-feature-settings: var(--type-title-section-features);
  }

  .type-title-card {
    font-family: var(--type-title-card-family);
    font-size: var(--type-title-card-size);
    font-weight: var(--type-title-card-weight);
    line-height: var(--type-title-card-line-height);
    letter-spacing: var(--type-title-card-tracking);
    text-transform: var(--type-title-card-transform);
    font-feature-settings: var(--type-title-card-features);
  }

  .type-title-item {
    font-family: var(--type-title-item-family);
    font-size: var(--type-title-item-size);
    font-weight: var(--type-title-item-weight);
    line-height: var(--type-title-item-line-height);
    letter-spacing: var(--type-title-item-tracking);
    text-transform: var(--type-title-item-transform);
    font-feature-settings: var(--type-title-item-features);
  }

  .type-body {
    font-family: var(--type-body-family);
    font-size: var(--type-body-size);
    font-weight: var(--type-body-weight);
    line-height: var(--type-body-line-height);
    letter-spacing: var(--type-body-tracking);
    text-transform: var(--type-body-transform);
    font-feature-settings: var(--type-body-features);
  }

  .type-body-sm {
    font-family: var(--type-body-sm-family);
    font-size: var(--type-body-sm-size);
    font-weight: var(--type-body-sm-weight);
    line-height: var(--type-body-sm-line-height);
    letter-spacing: var(--type-body-sm-tracking);
    text-transform: var(--type-body-sm-transform);
    font-feature-settings: var(--type-body-sm-features);
  }

  .type-label {
    font-family: var(--type-label-family);
    font-size: var(--type-label-size);
    font-weight: var(--type-label-weight);
    line-height: var(--type-label-line-height);
    letter-spacing: var(--type-label-tracking);
    text-transform: var(--type-label-transform);
    font-feature-settings: var(--type-label-features);
  }

  .type-eyebrow {
    font-family: var(--type-eyebrow-family);
    font-size: var(--type-eyebrow-size);
    font-weight: var(--type-eyebrow-weight);
    line-height: var(--type-eyebrow-line-height);
    letter-spacing: var(--type-eyebrow-tracking);
    text-transform: var(--type-eyebrow-transform);
    font-feature-settings: var(--type-eyebrow-features);
  }

  /* CJK + Korean scripts have built-in optical spacing and no case; the
     uppercase eyebrow's expanded letter-spacing reads awkwardly against
     ideographic and Hangul glyphs. Drop tracking to normal when the active
     language descends from an ancestor marked ja/zh/ko (matches :lang on
     the element OR any ancestor per CSS Selectors L4). */
  .type-eyebrow:lang(ja),
  .type-eyebrow:lang(zh),
  .type-eyebrow:lang(ko) {
    letter-spacing: normal;
  }

  .type-meta {
    font-family: var(--type-meta-family);
    font-size: var(--type-meta-size);
    font-weight: var(--type-meta-weight);
    line-height: var(--type-meta-line-height);
    letter-spacing: var(--type-meta-tracking);
    text-transform: var(--type-meta-transform);
    font-feature-settings: var(--type-meta-features);
  }

  .type-metric {
    font-family: var(--type-metric-family);
    font-size: var(--type-metric-size);
    font-weight: var(--type-metric-weight);
    line-height: var(--type-metric-line-height);
    letter-spacing: var(--type-metric-tracking);
    text-transform: var(--type-metric-transform);
    font-feature-settings: var(--type-metric-features);
  }
}

/* Suppress animations globally for users who opt out at the OS level. */
@media (prefers-reduced-motion: reduce) {
  :root,
  .theme-light,
  .theme-dark {
    --animation-duration: 0ms;
  }
}

/* -------------------------------------------------------------------- */
/* Spinner keyframes                                                    */
/* -------------------------------------------------------------------- */

/* Pure-scale wave — shapes pump in size with no opacity change. */
@keyframes spinner-pulse {
  0%, 100% { transform: scale(0.7);  }
  25%      { transform: scale(1.15); }
}

/* Trailing-tail fade — one shape "hot" at a time, others decay toward dim. */
@keyframes spinner-chase {
  0%, 100% { opacity: var(--opacity-20); }
  25%      { opacity: 1; }
}

/* Brand-palette drift — blue → violet → purple → magenta → blue. */
@keyframes spinner-prism {
  0%, 100% { fill: var(--color-blue-500);    }
  25%      { fill: var(--color-violet-500);  }
  50%      { fill: var(--color-purple-500);  }
  75%      { fill: var(--color-magenta-500); }
}

/* -------------------------------------------------------------------- */
/* Skeleton keyframes                                                    */
/* -------------------------------------------------------------------- */

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: var(--opacity-50); }
}

/* -------------------------------------------------------------------- */
/* TypingIndicator keyframes                                            */
/* -------------------------------------------------------------------- */

@keyframes typing-indicator-dot {
  0%, 60%, 100% { opacity: var(--opacity-20); }
  30%           { opacity: 1; }
}

/* -------------------------------------------------------------------- */
/* StreamingText cursor                                                 */
/* -------------------------------------------------------------------- */

@keyframes streaming-cursor-blink {
  0%, 50%  { opacity: 1; }
  51%, 100%{ opacity: 0; }
}

/* -------------------------------------------------------------------- */
/* AudioRecorder pulse ring                                             */
/* -------------------------------------------------------------------- */

@keyframes recorder-pulse {
  0%   { transform: scale(1);   opacity: var(--opacity-70); }
  100% { transform: scale(1.6); opacity: 0; }
}

/* -------------------------------------------------------------------- */
/* Checkbox indicator draw — relies on pathLength="1" on the target     */
/* path so the stroke dash values are unit-normalised. When             */
/* `--animation-duration` is 0 (accessible theme or prefers-reduced-    */
/* motion) the animation completes instantly and `forwards` pins the    */
/* glyph to its fully-drawn end state.                                  */
/* -------------------------------------------------------------------- */

@keyframes checkbox-indicator-draw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

/* -------------------------------------------------------------------- */
/* Stepper — same pathLength="1" draw-in for check / error glyphs and    */
/* a subtle one-shot bounce when an indicator enters the current or     */
/* completed state. Both collapse to 0ms under reduced motion.           */
/* -------------------------------------------------------------------- */

@keyframes stepper-icon-draw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

@keyframes stepper-indicator-pop {
  0%   { transform: scale(0.85); }
  60%  { transform: scale(1.06); }
  100% { transform: scale(1);    }
}

/* -------------------------------------------------------------------- */
/* Accordion keyframes — keyframe-based animation resolves the computed */
/* block-size from the Radix-populated CSS variable at start-of-play,   */
/* which a plain `transition-[block-size]` cannot (the variable isn't   */
/* populated when the transition begins, so it snaps). Duration and    */
/* easing are driven by `--animation-duration`, so the accessible theme */
/* and `prefers-reduced-motion` still collapse both to 0ms.             */
/* -------------------------------------------------------------------- */

@keyframes accordion-down {
  from { block-size: 0; }
  to   { block-size: var(--radix-accordion-content-height); }
}

@keyframes accordion-up {
  from { block-size: var(--radix-accordion-content-height); }
  to   { block-size: 0; }
}

} /* end @layer ui-kit-tokens */
