/*!
 * CubePilot UI v0.4.0
 * Bundled from src/styles/index.css.
 * https://github.com/CubePilot/cubepilot-ui
 */
/* ─────────────────────────────────────────────────────────────────────────
   CubePilot UI — main entry point.

   Import path (consumer side):
     @import "@cubepilot/ui/css";

   Order matters: tokens first, then reset, then anything that depends on
   the cascade order they establish.
   ───────────────────────────────────────────────────────────────────────── */

/* ── inlined from src/tokens/tokens.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   CubePilot design tokens · v0.4.0
   Generated by scripts/build-tokens.mjs from src/tokens/tokens.json.
   DO NOT EDIT by hand — re-run `pnpm tokens:build`.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* color.cube */
  --cp-color-cube-orange: #F26522;
  --cp-color-cube-red: #D6242D;
  --cp-color-cube-yellow: #F2B705;
  --cp-color-cube-blue: #1D8AC2;
  --cp-color-cube-green: #2FB271;
  --cp-color-cube-purple: #6F4FB0;
  --cp-color-cube-black: #1B1A18;
  --cp-color-cube-white: #FFFFFF;

  /* color.paper */
  --cp-color-paper-0: #FFFFFF;
  --cp-color-paper-50: #F6F8FB;
  --cp-color-paper-100: #EAEEF5;
  --cp-color-paper-200: #D5DEEC;

  /* color.ink */
  --cp-color-ink-0: #0E1219;
  --cp-color-ink-100: #2E3540;
  --cp-color-ink-200: #525C6B;
  --cp-color-ink-300: #8893A2;
  --cp-color-ink-400: #CCD3DE;

  /* color.void */
  --cp-color-void-0: #07101D;
  --cp-color-void-50: #0D1320;
  --cp-color-void-100: #161E2D;
  --cp-color-void-200: #232D3F;

  /* color.chalk */
  --cp-color-chalk-0: #DCE3EE;
  --cp-color-chalk-100: #B0B9C9;
  --cp-color-chalk-200: #9AA4B5;
  --cp-color-chalk-300: #5A6477;
  --cp-color-chalk-400: #1F2A3D;

  /* color.status */
  --cp-color-status-success: #2FB271;
  --cp-color-status-warning: #F2B705;
  --cp-color-status-danger: #D6242D;
  --cp-color-status-info: #1D8AC2;

  /* font.family */
  --cp-font-family-display: "CubePilot", "Saira", system-ui, sans-serif;
  --cp-font-family-body: "Saira", system-ui, -apple-system, "Segoe UI", sans-serif;
  --cp-font-family-mono: "IBM Plex Mono", "SF Mono", ui-monospace, monospace;

  /* font.weight */
  --cp-font-weight-regular: 400;
  --cp-font-weight-medium: 500;
  --cp-font-weight-semibold: 600;
  --cp-font-weight-bold: 700;

  /* font.size */
  --cp-font-size-10: 10px;
  --cp-font-size-11: 11px;
  --cp-font-size-12: 12px;
  --cp-font-size-14: 14px;
  --cp-font-size-16: 16px;
  --cp-font-size-18: 18px;
  --cp-font-size-20: 20px;
  --cp-font-size-24: 24px;
  --cp-font-size-32: 32px;
  --cp-font-size-40: 40px;
  --cp-font-size-56: 56px;
  --cp-font-size-72: 72px;

  /* font.line-height */
  --cp-line-height-tight: 1.10;
  --cp-line-height-snug: 1.25;
  --cp-line-height-normal: 1.45;
  --cp-line-height-loose: 1.65;

  /* font.letter-spacing */
  --cp-tracking-tight: -0.02em;
  --cp-tracking-normal: 0;
  --cp-tracking-wide: 0.04em;
  --cp-tracking-caps: 0.12em;

  /* space */
  --cp-space-0: 0;
  --cp-space-1: 2px;
  --cp-space-2: 4px;
  --cp-space-3: 6px;
  --cp-space-4: 8px;
  --cp-space-5: 10px;
  --cp-space-6: 12px;
  --cp-space-7: 14px;
  --cp-space-8: 16px;
  --cp-space-9: 20px;
  --cp-space-10: 24px;
  --cp-space-11: 32px;
  --cp-space-12: 40px;
  --cp-space-13: 48px;
  --cp-space-14: 64px;
  --cp-space-15: 80px;
  --cp-space-16: 96px;

  /* radius */
  --cp-radius-0: 0;
  --cp-radius-xs: 3px;
  --cp-radius-sm: 5px;
  --cp-radius-md: 8px;
  --cp-radius-lg: 12px;
  --cp-radius-xl: 20px;
  --cp-radius-pill: 999px;

  /* border */
  --cp-border-hairline: 1px;
  --cp-border-thin: 1.5px;
  --cp-border-thick: 2.5px;

  /* shadow */
  --cp-shadow-1: 0 1px 2px rgba(27,26,24,0.08), 0 1px 1px rgba(27,26,24,0.04);
  --cp-shadow-2: 0 4px 12px rgba(27,26,24,0.10), 0 2px 4px rgba(27,26,24,0.06);
  --cp-shadow-3: 0 8px 28px rgba(27,26,24,0.16), 0 4px 8px rgba(27,26,24,0.08);
  --cp-shadow-none: none;
  --cp-shadow-stamp: 4px 5px 0 var(--cp-color-border);
  --cp-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--cp-color-border-focus) 32%, transparent);

  /* motion.duration */
  --cp-duration-instant: 80ms;
  --cp-duration-fast: 120ms;
  --cp-duration-normal: 200ms;
  --cp-duration-slow: 320ms;

  /* motion.ease */
  --cp-ease-standard: cubic-bezier(0.2, 0.0, 0.0, 1.0);
  --cp-ease-out: cubic-bezier(0.0, 0.0, 0.0, 1.0);
  --cp-ease-in: cubic-bezier(0.4, 0.0, 1.0, 1.0);
  --cp-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1.0);

  /* z-index */
  --cp-z-base: 0;
  --cp-z-raised: 10;
  --cp-z-sticky: 30;
  --cp-z-overlay: 50;
  --cp-z-modal: 70;
  --cp-z-toast: 80;
  --cp-z-tooltip: 90;

  /* target */
  --cp-target-touch: 44px;
  --cp-target-mouse: 32px;
  --cp-target-compact: 24px;
}

:root,
[data-theme="light"] {
  --cp-color-bg: #F6F8FB;
  --cp-color-bg-elevated: #FFFFFF;
  --cp-color-bg-inset: #EAEEF5;
  --cp-color-bg-overlay: rgba(27, 26, 24, 0.42);
  --cp-color-fg: #0E1219;
  --cp-color-fg-muted: #525C6B;
  --cp-color-fg-faint: #8893A2;
  --cp-color-fg-disabled: #8893A2;
  --cp-color-fg-on-accent: #FFFFFF;
  --cp-color-border: #0E1219;
  --cp-color-border-subtle: #CCD3DE;
  --cp-color-border-focus: #F26522;
  --cp-color-accent: #F26522;
  color-scheme: light;
}

[data-theme="dark"] {
  --cp-color-bg: #0D1320;
  --cp-color-bg-elevated: #161E2D;
  --cp-color-bg-inset: #07101D;
  --cp-color-bg-overlay: rgba(0, 0, 0, 0.62);
  --cp-color-fg: #DCE3EE;
  --cp-color-fg-muted: #B0B9C9;
  --cp-color-fg-faint: #9AA4B5;
  --cp-color-fg-disabled: #5A6477;
  --cp-color-fg-on-accent: #FFFFFF;
  --cp-color-border: #DCE3EE;
  --cp-color-border-subtle: #1F2A3D;
  --cp-color-border-focus: #F26522;
  --cp-color-accent: #F26522;
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --cp-color-bg: #0D1320;
    --cp-color-bg-elevated: #161E2D;
    --cp-color-bg-inset: #07101D;
    --cp-color-bg-overlay: rgba(0, 0, 0, 0.62);
    --cp-color-fg: #DCE3EE;
    --cp-color-fg-muted: #B0B9C9;
    --cp-color-fg-faint: #9AA4B5;
    --cp-color-fg-disabled: #5A6477;
    --cp-color-fg-on-accent: #FFFFFF;
    --cp-color-border: #DCE3EE;
    --cp-color-border-subtle: #1F2A3D;
    --cp-color-border-focus: #F26522;
    --cp-color-accent: #F26522;
    color-scheme: dark;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --cp-duration-instant: 0ms;
    --cp-duration-fast:    0ms;
    --cp-duration-normal:  0ms;
    --cp-duration-slow:    0ms;
  }
}

/* ── end src/tokens/tokens.css ── */
/* ── inlined from src/styles/base/reset.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   Minimal modern reset. Just enough to make components predictable across
   browsers. Intentionally NOT a full normalize — we don't want to fight
   host pages that already have their own reset.
   ───────────────────────────────────────────────────────────────────────── */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  margin: 0;
  min-block-size: 100dvh;
  background: var(--cp-color-bg);
  color: var(--cp-color-fg);
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-16);
  line-height: var(--cp-line-height-normal);
}

/* Strip default list styles only when authors opt in by adding `[role=list]`
   — preserves screen-reader semantics and doesn't break host-page lists. */
[role="list"] {
  list-style: none;
  margin: 0;
  padding: 0;
}

img, svg, video, canvas, picture {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration-thickness: var(--cp-border-hairline);
  text-underline-offset: 0.18em;
}

hr {
  border: 0;
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  margin-block: var(--cp-space-8);
}

::selection {
  background: color-mix(in srgb, var(--cp-color-accent) 32%, transparent);
  color: var(--cp-color-fg);
}

/* ── end src/styles/base/reset.css ── */
/* ── inlined from src/styles/base/typography.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   Typography: font loading, type scale, helper classes.

   Display face: CubePilot (foundry-fixed v1.10). Reserved for wordmarks and
   headlines ≥ 24px until the foundry ships additional weights — see
   scratch/fonts_update.md.

   Body face: Saira (400/500/600/700, bundled woff2, latin subset).

   Mono: system stacks (no bundled font); IBM Plex Mono if the host has it.
   ───────────────────────────────────────────────────────────────────────── */

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

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

/* ── Headings: CubePilot display face when size ≥ 24px ─────────────────── */

.cp-display,
h1.cp-h1, h2.cp-h2 {
  font-family: var(--cp-font-family-display);
  font-feature-settings: "kern" 1;
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-tight);
}

.cp-h1 { font-size: var(--cp-font-size-40); font-weight: var(--cp-font-weight-regular); margin: 0; }
.cp-h2 { font-size: var(--cp-font-size-32); font-weight: var(--cp-font-weight-regular); margin: 0; }
.cp-h3 {
  font-size: var(--cp-font-size-24);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-snug);
  margin: 0;
}
.cp-h4 {
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-snug);
  margin: 0;
}
.cp-h5 {
  font-size: var(--cp-font-size-16);
  font-weight: var(--cp-font-weight-semibold);
  margin: 0;
}

/* Hero / page titles — only place where we let CubePilot fly free */
.cp-hero {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-56);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-tight);
}
.cp-display-xl {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-72);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-tight);
}

/* ── Body text ─────────────────────────────────────────────────────────── */

.cp-body  { font-size: var(--cp-font-size-16); line-height: var(--cp-line-height-normal); }
.cp-body-lg { font-size: var(--cp-font-size-18); line-height: var(--cp-line-height-normal); }
.cp-body-sm { font-size: var(--cp-font-size-14); line-height: var(--cp-line-height-normal); }
.cp-caption { font-size: var(--cp-font-size-12); line-height: var(--cp-line-height-snug); color: var(--cp-color-fg-muted); }

/* Eyebrow / all-caps micro label */
.cp-eyebrow {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}

/* Mono / spec text. Tabular figures for legibility against aligned columns */
.cp-mono {
  font-family: var(--cp-font-family-mono);
  font-variant-numeric: tabular-nums;
}

/* Numeric / tabular-figures hint for any element that displays figures
   (battery readouts, speed, version strings, RTK fix counts) */
.cp-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ── end src/styles/base/typography.css ── */
/* ── inlined from src/styles/base/focus.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   Focus rings. Always visible on keyboard navigation, never on mouse click.

   Rule of thumb: every interactive element in the library inherits this by
   default. Components that need a different focus treatment (e.g. embedded
   OLED green-on-black) override locally.
   ───────────────────────────────────────────────────────────────────────── */

:where(button, [role="button"], a, input, select, textarea, [tabindex]):focus {
  outline: none;
}

:where(button, [role="button"], a, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--cp-border-thin) solid var(--cp-color-border-focus);
  outline-offset: 2px;
  box-shadow: var(--cp-shadow-focus);
}

/* Skip the offset on flush-edge controls (e.g. table-row buttons) by
   adding `.cp-focus-flush` */
.cp-focus-flush:focus-visible {
  outline-offset: 0;
}

/* ── end src/styles/base/focus.css ── */
/* ── Components (alphabetical to keep merge conflicts boring) ───────────── */
/* ── inlined from src/styles/components/button.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-button — the canonical reference component for CubePilot UI.

   Architecture mirrors the conventions in SPEC.md:
   - Single root class `.cp-button` carries shared layout/typography.
   - Modifiers `--solid` `--accent` `--ghost` `--link` `--icon` `--stamped`
     swap visual treatment.
   - Sizes `--sm` (default) `--lg`. Default is the desktop 32px target.
   - `data-surface` reshapes for mobile/embedded without changing markup.
   - States flow from real ARIA: `:disabled`, `aria-disabled`, `aria-busy`,
     `:focus-visible` (inherits the base ring from base/focus.css).
   ───────────────────────────────────────────────────────────────────────── */

.cp-button {
  /* Component-local custom properties so size/surface modifiers can override
     them without rewriting the layout rules below. */
  --_cp-button-bg: transparent;
  --_cp-button-fg: var(--cp-color-fg);
  --_cp-button-border: var(--cp-color-border);
  --_cp-button-border-width: var(--cp-border-thin);
  --_cp-button-border-style: solid;
  --_cp-button-radius: var(--cp-radius-sm);
  --_cp-button-pad-x: var(--cp-space-7);
  --_cp-button-pad-y: var(--cp-space-3);
  --_cp-button-gap: var(--cp-space-4);
  --_cp-button-min-size: var(--cp-target-mouse);
  --_cp-button-font-size: var(--cp-font-size-14);
  --_cp-button-font-weight: var(--cp-font-weight-semibold);
  --_cp-button-shadow: none;
  --_cp-button-translate: 0;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--_cp-button-gap);
  vertical-align: middle;

  min-block-size: var(--_cp-button-min-size);
  padding: var(--_cp-button-pad-y) var(--_cp-button-pad-x);

  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-button-font-size);
  font-weight: var(--_cp-button-font-weight);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-normal);
  text-decoration: none;
  white-space: nowrap;

  background: var(--_cp-button-bg);
  color: var(--_cp-button-fg);
  border: var(--_cp-button-border-width) var(--_cp-button-border-style)
          var(--_cp-button-border);
  border-radius: var(--_cp-button-radius);
  box-shadow: var(--_cp-button-shadow);

  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  appearance: none;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard),
    transform var(--cp-duration-instant) var(--cp-ease-standard);

  transform: translateY(var(--_cp-button-translate));
}

.cp-button > svg,
.cp-button > .cp-button__icon {
  inline-size: 1em;
  block-size: 1em;
  flex: 0 0 auto;
}

/* Keyboard shortcut tag inside a button (e.g. ↵ on a primary action) */
.cp-button__kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 1.4em;
  padding-inline: var(--cp-space-2);
  margin-inline-start: var(--cp-space-3);
  font-family: var(--cp-font-family-mono);
  font-size: 0.85em;
  font-weight: var(--cp-font-weight-medium);
  border: var(--cp-border-hairline) solid currentColor;
  border-radius: var(--cp-radius-xs);
  opacity: 0.7;
}

/* ── Variants ─────────────────────────────────────────────────────────── */

/* Outline (default) — bordered, transparent. Hover fills with ink. */
.cp-button:hover,
.cp-button.cp-is-hover {
  --_cp-button-bg: var(--cp-color-bg-inset);
  --_cp-button-border: var(--cp-color-fg);
}

.cp-button:active,
.cp-button.cp-is-active {
  --_cp-button-bg: var(--cp-color-fg);
  --_cp-button-fg: var(--cp-color-bg);
  --_cp-button-translate: 1px;
}

/* Solid — filled with ink, inverted text. The workhorse confirming action. */
.cp-button--solid {
  --_cp-button-bg: var(--cp-color-fg);
  --_cp-button-fg: var(--cp-color-bg);
  --_cp-button-border: var(--cp-color-fg);
}
.cp-button--solid:hover,
.cp-button--solid.cp-is-hover {
  --_cp-button-bg: var(--cp-color-fg-muted);
  --_cp-button-border: var(--cp-color-fg-muted);
}
.cp-button--solid:active,
.cp-button--solid.cp-is-active {
  --_cp-button-bg: var(--cp-color-fg);
  --_cp-button-fg: var(--cp-color-bg);
  --_cp-button-translate: 1px;
}

/* Accent — primary CTA. Reserve for ONE per screen. */
.cp-button--accent {
  --_cp-button-bg: var(--cp-color-accent);
  --_cp-button-fg: var(--cp-color-fg-on-accent);
  --_cp-button-border: var(--cp-color-accent);
}
.cp-button--accent:hover,
.cp-button--accent.cp-is-hover {
  --_cp-button-bg: color-mix(in srgb, var(--cp-color-accent) 88%, black);
  --_cp-button-border: color-mix(in srgb, var(--cp-color-accent) 88%, black);
}
.cp-button--accent:active,
.cp-button--accent.cp-is-active {
  --_cp-button-bg: color-mix(in srgb, var(--cp-color-accent) 70%, black);
  --_cp-button-border: color-mix(in srgb, var(--cp-color-accent) 70%, black);
  --_cp-button-fg: var(--cp-color-fg-on-accent);
  --_cp-button-translate: 1px;
}

/* Ghost — borderless. Hover surfaces. */
.cp-button--ghost {
  --_cp-button-bg: transparent;
  --_cp-button-border: transparent;
}
.cp-button--ghost:hover,
.cp-button--ghost.cp-is-hover {
  --_cp-button-bg: var(--cp-color-bg-inset);
  --_cp-button-border: transparent;
  --_cp-button-fg: var(--cp-color-fg);
}
.cp-button--ghost:active,
.cp-button--ghost.cp-is-active {
  --_cp-button-bg: color-mix(in srgb, var(--cp-color-fg) 12%, transparent);
  --_cp-button-border: transparent;
  --_cp-button-translate: 1px;
}

/* Link — text-only with a straight underline. */
.cp-button--link {
  --_cp-button-bg: transparent;
  --_cp-button-border: transparent;
  --_cp-button-border-width: 0;
  --_cp-button-fg: var(--cp-color-accent);
  --_cp-button-pad-x: var(--cp-space-2);
  --_cp-button-pad-y: var(--cp-space-2);
  --_cp-button-min-size: auto;
  text-decoration: underline;
  text-decoration-style: solid;
  text-decoration-thickness: var(--cp-border-hairline);
  text-underline-offset: 0.22em;
}
.cp-button--link:hover,
.cp-button--link.cp-is-hover {
  --_cp-button-bg: transparent;
  --_cp-button-fg: color-mix(in srgb, var(--cp-color-accent) 80%, black);
  /* Thicken the straight underline on hover for affordance. */
  text-decoration-thickness: var(--cp-border-thin);
}
.cp-button--link:active,
.cp-button--link.cp-is-active {
  --_cp-button-bg: transparent;
  --_cp-button-fg: var(--cp-color-accent);
  --_cp-button-translate: 1px;
}

/* Icon-only — square. Pass an aria-label so screen readers know what it does. */
.cp-button--icon {
  --_cp-button-pad-x: var(--cp-space-3);
  --_cp-button-pad-y: var(--cp-space-3);
  --_cp-button-gap: 0;
  aspect-ratio: 1 / 1;
  min-inline-size: var(--_cp-button-min-size);
}
.cp-button--icon > svg,
.cp-button--icon > .cp-button__icon {
  inline-size: 1.15em;
  block-size: 1.15em;
}

/* Stamped — cube-mark cue. Notch at the bottom-right for a state-committing
   action (Arm Vehicle, Save Mission). The notch size scales with the size
   modifier via the local custom property. */
.cp-button--stamped {
  --_cp-button-notch: var(--cp-space-4);
  --_cp-button-pad-x: var(--cp-space-9);
  clip-path: polygon(
    0 0,
    100% 0,
    100% calc(100% - var(--_cp-button-notch)),
    calc(100% - var(--_cp-button-notch)) 100%,
    0 100%
  );
  /* Border-radius can't survive clip-path on the notched corner; flatten. */
  --_cp-button-radius: 0;
}
.cp-button--sm.cp-button--stamped { --_cp-button-notch: var(--cp-space-3); }
.cp-button--lg.cp-button--stamped { --_cp-button-notch: var(--cp-space-5); }

/* ── Sizes ────────────────────────────────────────────────────────────── */

.cp-button--sm {
  --_cp-button-min-size: var(--cp-target-compact);
  --_cp-button-font-size: var(--cp-font-size-12);
  --_cp-button-pad-x: var(--cp-space-5);
  --_cp-button-pad-y: var(--cp-space-2);
  --_cp-button-gap: var(--cp-space-3);
}

.cp-button--lg {
  --_cp-button-min-size: var(--cp-target-touch);
  --_cp-button-font-size: var(--cp-font-size-16);
  --_cp-button-pad-x: var(--cp-space-9);
  --_cp-button-pad-y: var(--cp-space-5);
  --_cp-button-gap: var(--cp-space-5);
}

/* ── Semantic state borders (paired with iconography in real apps) ─────── */

.cp-button.cp-is-error {
  --_cp-button-border: var(--cp-color-status-danger);
  --_cp-button-fg: var(--cp-color-status-danger);
}
.cp-button.cp-is-error:hover,
.cp-button.cp-is-error.cp-is-hover {
  --_cp-button-bg: color-mix(in srgb, var(--cp-color-status-danger) 12%, transparent);
  --_cp-button-border: var(--cp-color-status-danger);
}

.cp-button.cp-is-success {
  --_cp-button-border: var(--cp-color-status-success);
  --_cp-button-fg: var(--cp-color-status-success);
}
.cp-button.cp-is-success:hover,
.cp-button.cp-is-success.cp-is-hover {
  --_cp-button-bg: color-mix(in srgb, var(--cp-color-status-success) 12%, transparent);
  --_cp-button-border: var(--cp-color-status-success);
}

/* ── Disabled — real disabled (no pointer events, no focus) ─────────────── */

.cp-button:disabled,
.cp-button.cp-is-disabled {
  --_cp-button-bg: transparent;
  --_cp-button-fg: var(--cp-color-fg-disabled);
  --_cp-button-border: var(--cp-color-fg-disabled);
  --_cp-button-border-style: dashed;
  --_cp-button-translate: 0;
  cursor: not-allowed;
  box-shadow: none;
}
.cp-button--solid:disabled,
.cp-button--accent:disabled,
.cp-button--solid.cp-is-disabled,
.cp-button--accent.cp-is-disabled {
  --_cp-button-bg: transparent;
}

/* aria-disabled — focusable but inert. Use this when you need to explain
   why the action isn't available right now (tooltip, helper text). */
.cp-button[aria-disabled="true"] {
  --_cp-button-bg: transparent;
  --_cp-button-fg: var(--cp-color-fg-disabled);
  --_cp-button-border: var(--cp-color-fg-disabled);
  --_cp-button-border-style: dashed;
  --_cp-button-translate: 0;
  cursor: not-allowed;
}
.cp-button[aria-disabled="true"]:hover {
  /* Override hover — aria-disabled must not animate as if alive. */
  --_cp-button-bg: transparent;
}

/* ── Loading — aria-busy="true". A spinner pill appears after the label. */

.cp-button[aria-busy="true"],
.cp-button.cp-is-loading {
  cursor: progress;
  position: relative;
}
.cp-button[aria-busy="true"]::after,
.cp-button.cp-is-loading::after {
  content: "";
  inline-size: 0.9em;
  block-size: 0.9em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  margin-inline-start: var(--cp-space-3);
  animation: cp-button-spin 0.7s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .cp-button[aria-busy="true"]::after,
  .cp-button.cp-is-loading::after {
    animation-duration: 2s;
  }
}
@keyframes cp-button-spin {
  to { transform: rotate(360deg); }
}

/* The base focus ring (src/styles/base/focus.css) already covers
   :focus-visible with the orange ring + box-shadow. We only adjust here
   when the surface needs a different feel. */

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-button[data-surface="mobile"] {
  --_cp-button-min-size: var(--cp-target-touch);
  --_cp-button-pad-x: var(--cp-space-9);
  --_cp-button-pad-y: var(--cp-space-5);
  --_cp-button-font-size: var(--cp-font-size-16);
  --_cp-button-radius: var(--cp-radius-md);
}

.cp-button[data-surface="embedded"] {
  --_cp-button-min-size: var(--cp-target-compact);
  --_cp-button-pad-x: var(--cp-space-4);
  --_cp-button-pad-y: var(--cp-space-2);
  --_cp-button-font-size: var(--cp-font-size-11);
  --_cp-button-font-weight: var(--cp-font-weight-medium);
  --_cp-button-border-width: var(--cp-border-hairline);
  --_cp-button-radius: var(--cp-radius-xs);
  --_cp-button-shadow: none;
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}

/* ── Groups — segmented row of buttons that share edges ──────────────── */

.cp-button-group {
  display: inline-flex;
  align-items: stretch;
  isolation: isolate;
}
.cp-button-group > .cp-button {
  border-radius: 0;
  margin-inline-start: calc(-1 * var(--cp-border-thin));
}
.cp-button-group > .cp-button:first-child {
  border-start-start-radius: var(--cp-radius-sm);
  border-end-start-radius: var(--cp-radius-sm);
  margin-inline-start: 0;
}
.cp-button-group > .cp-button:last-child {
  border-start-end-radius: var(--cp-radius-sm);
  border-end-end-radius: var(--cp-radius-sm);
}
.cp-button-group > .cp-button:hover,
.cp-button-group > .cp-button:focus-visible,
.cp-button-group > .cp-button[aria-pressed="true"] {
  z-index: 1;
}

/* Pressed/toggled (used for segmented controls) */
.cp-button[aria-pressed="true"] {
  --_cp-button-bg: var(--cp-color-fg);
  --_cp-button-fg: var(--cp-color-bg);
  --_cp-button-border: var(--cp-color-fg);
}

/* ── end src/styles/components/button.css ── */
/* ── inlined from src/styles/components/callout.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-callout — inline, page-level notice / banner.

   NOT a transient toast. This is the workhorse for important on-page
   messages that live in the document flow: a pinned critical operations
   notice, a dismissable info tip, a validation summary at the top of a
   form. Toasts come and go; callouts stay until the content stops being
   relevant (or the user dismisses an optional one).

   Architecture mirrors button.css (the canonical reference):
   - `.cp-callout` is the root; `__icon` `__body` `__title` `__message`
     `__actions` `__dismiss` are the parts.
   - Severity modifiers `--info` (default) `--success` `--warning`
     `--danger` set the accent color + the icon tint.
   - Treatment modifiers `--soft` (the calm default look: subtle tinted
     fill + hairline accent border), `--solid` (filled accent surface,
     fg-on-accent text), `--outline` (transparent bg, accent left rail).
   - `--critical` is the deliberate, high-emphasis PINNED treatment for
     must-not-miss operational messages (bank-account changes, safety
     recalls). Strong danger left rail, elevated surface, larger, and
     NOT dismissable by default — the most prominent callout there is.
   - `--banner` is the full-width, square-cornered top-of-page strip.
   - Size `--sm` for tight inline placements.
   - Surface variation via `data-surface` (mobile / embedded).

   Accessibility:
   - Default severities (info / success / warning) are informational:
     pair with `role="status"` + `aria-live="polite"`.
   - `--danger` and `--critical` are urgent: pair with `role="alert"` +
     `aria-live="assertive"`.
   - Colour is never the only signal — every callout carries a leading
     `__icon` whose glyph matches the severity.
   - `__dismiss` is a real `<button type="button">` with
     `aria-label="Dismiss"`.

   States flow from real ARIA per SPEC.md §4. The container is not
   interactive; the actions and dismiss button inside it are real
   `<button>` / `<a>` elements carrying their own focus + hover.
   ───────────────────────────────────────────────────────────────────────── */

.cp-callout {
  /* Component-local custom properties so severity / treatment / size /
     surface modifiers can override them without rewriting layout. */
  --_cp-callout-accent: var(--cp-color-status-info);
  --_cp-callout-bg: color-mix(in srgb, var(--_cp-callout-accent) 8%, var(--cp-color-bg));
  --_cp-callout-fg: var(--cp-color-fg);
  --_cp-callout-msg-fg: var(--cp-color-fg-muted);
  --_cp-callout-icon-fg: var(--_cp-callout-accent);
  --_cp-callout-border: color-mix(in srgb, var(--_cp-callout-accent) 32%, transparent);
  --_cp-callout-border-width: var(--cp-border-hairline);
  --_cp-callout-rail: var(--_cp-callout-accent);
  --_cp-callout-rail-width: 0;
  --_cp-callout-radius: var(--cp-radius-md);
  --_cp-callout-pad-x: var(--cp-space-8);
  --_cp-callout-pad-y: var(--cp-space-7);
  --_cp-callout-gap: var(--cp-space-6);
  --_cp-callout-shadow: var(--cp-shadow-none);
  --_cp-callout-icon-size: var(--cp-space-9);          /* 20px box per spec */
  --_cp-callout-title-size: var(--cp-font-size-14);
  --_cp-callout-msg-size: var(--cp-font-size-12);

  position: relative;
  display: flex;
  gap: var(--_cp-callout-gap);
  align-items: flex-start;

  padding: var(--_cp-callout-pad-y) var(--_cp-callout-pad-x);

  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-callout-msg-size);
  line-height: var(--cp-line-height-snug);
  color: var(--_cp-callout-fg);

  background: var(--_cp-callout-bg);
  border: var(--_cp-callout-border-width) solid var(--_cp-callout-border);
  /* The accent left rail — width is 0 by default and raised by the
     --critical / --outline treatments that lean on it for emphasis. */
  border-inline-start: var(--_cp-callout-rail-width) solid var(--_cp-callout-rail);
  border-radius: var(--_cp-callout-radius);
  box-shadow: var(--_cp-callout-shadow);

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

/* ── Parts ────────────────────────────────────────────────────────────── */

/* Leading status icon — a 20px box holding a stroke-currentColor SVG.
   Tinted by the severity via --_cp-callout-icon-fg. Nudged down a sliver
   so it aligns with the title's cap-height rather than its ascender line. */
.cp-callout__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--_cp-callout-icon-size);
  block-size: var(--_cp-callout-icon-size);
  color: var(--_cp-callout-icon-fg);
  margin-block-start: 1px; /* optical: drop to title cap-height */
}

.cp-callout__icon > svg {
  inline-size: 100%;
  block-size: 100%;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Text column — grows to fill the row, allowing the message to wrap. */
.cp-callout__body {
  flex: 1 1 auto;
  min-inline-size: 0; /* allow text wrapping inside the flex row */
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-2);
}

.cp-callout__title {
  margin: 0;
  font-size: var(--_cp-callout-title-size);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-tight);
  color: var(--_cp-callout-fg);
  /* Account numbers, dates and amounts commonly appear in these titles. */
  font-variant-numeric: tabular-nums;
}

.cp-callout__message {
  margin: 0;
  font-size: var(--_cp-callout-msg-size);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-snug);
  color: var(--_cp-callout-msg-fg);
  font-variant-numeric: tabular-nums;
}

/* Trailing action row. On wide callouts it sits to the right of the body;
   it wraps under the body on narrow widths via the flex layout. */
.cp-callout__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--cp-space-4);
  flex-wrap: wrap;
  /* Pull level with the title so a single-line callout reads as one row. */
  margin-block-start: 0;
}

/* When actions live inside the body (stacked layout), give them top air. */
.cp-callout__body > .cp-callout__actions {
  margin-block-start: var(--cp-space-5);
}

/* Optional dismiss — a real <button>. Sits flush to the top-right. */
.cp-callout__dismiss {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-space-9);
  block-size: var(--cp-space-9);
  padding: 0;
  margin-block-start: -1px;                       /* optical: align with title cap-height */
  margin-inline-end: calc(-1 * var(--cp-space-3)); /* optical: tuck toward the edge */
  appearance: none;
  background: transparent;
  color: var(--cp-color-fg-muted);
  border: 0;
  border-radius: var(--cp-radius-sm);
  cursor: pointer;
  font-size: var(--cp-font-size-16);
  line-height: 1;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-callout__dismiss > svg {
  inline-size: var(--cp-space-7);
  block-size: var(--cp-space-7);
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
}

.cp-callout__dismiss:hover,
.cp-callout__dismiss.cp-is-hover {
  background: color-mix(in srgb, currentColor 14%, transparent);
  color: var(--_cp-callout-fg);
}

.cp-callout__dismiss:active,
.cp-callout__dismiss.cp-is-active {
  background: color-mix(in srgb, currentColor 22%, transparent);
}

/* ── Severity variants — set the accent; icon tint follows ─────────────── */

.cp-callout--info {
  --_cp-callout-accent: var(--cp-color-status-info);
}
.cp-callout--success {
  --_cp-callout-accent: var(--cp-color-status-success);
}
.cp-callout--warning {
  --_cp-callout-accent: var(--cp-color-status-warning);
}
.cp-callout--danger {
  --_cp-callout-accent: var(--cp-color-status-danger);
}

/* ── Treatment variants ───────────────────────────────────────────────── */

/* Soft (the calm default look) — subtle tinted fill, hairline accent
   border, icon in the accent color. This is what a bare .cp-callout
   already renders, so --soft is mostly a semantic hook; it re-asserts the
   values so it survives even if a future default changes. */
.cp-callout--soft {
  --_cp-callout-bg: color-mix(in srgb, var(--_cp-callout-accent) 8%, var(--cp-color-bg));
  --_cp-callout-fg: var(--cp-color-fg);
  --_cp-callout-msg-fg: var(--cp-color-fg-muted);
  --_cp-callout-icon-fg: var(--_cp-callout-accent);
  --_cp-callout-border: color-mix(in srgb, var(--_cp-callout-accent) 32%, transparent);
  --_cp-callout-rail-width: 0;
}

/* Solid — filled accent surface, on-accent foreground. Everything that
   reads as "muted" must lift to a translucent white so it stays legible
   on the strong fill. */
.cp-callout--solid {
  --_cp-callout-bg: var(--_cp-callout-accent);
  --_cp-callout-fg: var(--cp-color-fg-on-accent);
  --_cp-callout-msg-fg: color-mix(in srgb, var(--cp-color-fg-on-accent) 82%, transparent);
  --_cp-callout-icon-fg: var(--cp-color-fg-on-accent);
  --_cp-callout-border: var(--_cp-callout-accent);
  --_cp-callout-rail-width: 0;
}
/* On the solid surface the dismiss must invert too. */
.cp-callout--solid .cp-callout__dismiss {
  color: color-mix(in srgb, var(--cp-color-fg-on-accent) 82%, transparent);
}
.cp-callout--solid .cp-callout__dismiss:hover,
.cp-callout--solid .cp-callout__dismiss.cp-is-hover {
  color: var(--cp-color-fg-on-accent);
}

/* Outline — transparent surface, accent left rail. The lightest-weight
   treatment: no fill, just a colored spine + bordered frame. */
.cp-callout--outline {
  --_cp-callout-bg: transparent;
  --_cp-callout-fg: var(--cp-color-fg);
  --_cp-callout-msg-fg: var(--cp-color-fg-muted);
  --_cp-callout-icon-fg: var(--_cp-callout-accent);
  --_cp-callout-border: var(--cp-color-border-subtle);
  --_cp-callout-rail: var(--_cp-callout-accent);
  --_cp-callout-rail-width: var(--cp-border-thick);
  /* The rail wants a square inner edge so it reads as a spine, not a
     rounded chip. Flatten the start corners. */
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

/* ── --critical — the deliberate, pinned, high-emphasis treatment ──────── */

/* Always danger-accented regardless of any severity modifier — a critical
   notice IS the alarm. Strong left rail, elevated surface, slightly larger
   type and padding, a tinted danger fill. Not dismissable by default
   (consumers may still add a __dismiss, but the visual language says
   "this stays"). */
.cp-callout--critical {
  --_cp-callout-accent: var(--cp-color-status-danger);
  --_cp-callout-bg: color-mix(in srgb, var(--cp-color-status-danger) 12%, var(--cp-color-bg-elevated));
  --_cp-callout-fg: var(--cp-color-fg);
  --_cp-callout-msg-fg: var(--cp-color-fg-muted);
  --_cp-callout-icon-fg: var(--cp-color-status-danger);
  --_cp-callout-border: color-mix(in srgb, var(--cp-color-status-danger) 40%, transparent);
  --_cp-callout-rail: var(--cp-color-status-danger);
  --_cp-callout-rail-width: var(--cp-space-3);          /* 6px spine — unmistakable */
  --_cp-callout-shadow: var(--cp-shadow-2);             /* lifts off the page */
  --_cp-callout-radius: var(--cp-radius-md);
  --_cp-callout-pad-x: var(--cp-space-9);
  --_cp-callout-pad-y: var(--cp-space-8);
  --_cp-callout-gap: var(--cp-space-7);
  --_cp-callout-icon-size: var(--cp-space-10);          /* 24px — bigger alert glyph */
  --_cp-callout-title-size: var(--cp-font-size-16);
  --_cp-callout-msg-size: var(--cp-font-size-14);
  /* Square inner edge so the spine reads as a pinned bar. */
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  align-items: flex-start;
}

/* The critical icon gets a tinted disc behind it so the alert glyph has
   real presence, not a thin red line. */
.cp-callout--critical .cp-callout__icon {
  border-radius: var(--cp-radius-pill);
  background: color-mix(in srgb, var(--cp-color-status-danger) 16%, transparent);
  padding: var(--cp-space-2);
  box-sizing: content-box;
  margin-block-start: 0;
}

/* The critical title carries a touch more weight than the default. */
.cp-callout--critical .cp-callout__title {
  font-weight: var(--cp-font-weight-bold);
  letter-spacing: var(--cp-tracking-tight);
}

/* ── --banner — full-width top-of-page strip ──────────────────────────── */

/* Square-ish corners, no side borders (only the rail keeps its edge), and
   it fills its container. Pair with --solid for a loud top strip or leave
   --soft for a quieter one. */
.cp-callout--banner {
  --_cp-callout-radius: var(--cp-radius-0);
  --_cp-callout-pad-x: var(--cp-space-10);
  display: flex;
  inline-size: 100%;
  align-items: center;
}
/* A banner's actions sit inline at the trailing edge, never stacked. */
.cp-callout--banner .cp-callout__actions {
  margin-block-start: 0;
  margin-inline-start: auto;
}
/* When a banner is also critical, keep its spine but square every corner. */
.cp-callout--banner.cp-callout--critical {
  border-radius: var(--cp-radius-0);
}

/* ── Size ─────────────────────────────────────────────────────────────── */

.cp-callout--sm {
  --_cp-callout-radius: var(--cp-radius-sm);
  --_cp-callout-pad-x: var(--cp-space-6);
  --_cp-callout-pad-y: var(--cp-space-5);
  --_cp-callout-gap: var(--cp-space-5);
  --_cp-callout-icon-size: var(--cp-space-8);          /* 16px */
  --_cp-callout-title-size: var(--cp-font-size-12);
  --_cp-callout-msg-size: var(--cp-font-size-11);
}
.cp-callout--sm .cp-callout__dismiss {
  inline-size: var(--cp-space-8);
  block-size: var(--cp-space-8);
  font-size: var(--cp-font-size-14);
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-callout[data-surface="mobile"] {
  --_cp-callout-pad-x: var(--cp-space-9);
  --_cp-callout-pad-y: var(--cp-space-8);
  --_cp-callout-gap: var(--cp-space-7);
  --_cp-callout-title-size: var(--cp-font-size-16);
  --_cp-callout-msg-size: var(--cp-font-size-14);
  --_cp-callout-icon-size: var(--cp-space-10);
}
.cp-callout[data-surface="mobile"] .cp-callout__dismiss {
  inline-size: var(--cp-target-touch);
  block-size: var(--cp-target-touch);
  margin-block-start: calc(-1 * var(--cp-space-3));
}

.cp-callout[data-surface="embedded"] {
  --_cp-callout-radius: var(--cp-radius-xs);
  --_cp-callout-border-width: var(--cp-border-hairline);
  --_cp-callout-shadow: var(--cp-shadow-none);
  --_cp-callout-pad-x: var(--cp-space-5);
  --_cp-callout-pad-y: var(--cp-space-4);
  --_cp-callout-gap: var(--cp-space-5);
  --_cp-callout-icon-size: var(--cp-space-8);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
}
.cp-callout[data-surface="embedded"] .cp-callout__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
}
.cp-callout[data-surface="embedded"] .cp-callout__message {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
}
/* Embedded surfaces drop the lift + spine for a flat scan-line look. */
.cp-callout[data-surface="embedded"].cp-callout--critical {
  --_cp-callout-shadow: var(--cp-shadow-none);
  --_cp-callout-rail-width: var(--cp-border-thick);
}

/* ── Reduced motion — nothing here animates layout, but keep the contract
   explicit so future enter/leave additions inherit it. ────────────────── */
@media (prefers-reduced-motion: reduce) {
  .cp-callout {
    transition: none;
  }
}

/* ── end src/styles/components/callout.css ── */
/* ── inlined from src/styles/components/card.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-card — the surface container family.

   Follows the architecture established by button.css (the canonical
   reference): a single root class, BEM elements for parts, modifier
   classes for variants and sizes, and component-local custom properties
   (`--_cp-card-*`) so modifiers can override the layout cleanly.

   Cards are the most-reused container in the design system. Modals,
   toasts, popovers, and empty-state panels all compose .cp-card under
   the hood — so this file owns the surface contract.

   Surface contract:
   - bg = --cp-color-bg-elevated (the "raised" plane above page bg)
   - border = thin solid border on subtle line
   - radius = medium (8px)
   - padding = space-9 (20px)
   - gap between stacked parts = space-7 (14px)

   States live on `.cp-card--clickable`: hover, focus-visible, active,
   aria-pressed (selectable cards). Static cards have no states.
   ───────────────────────────────────────────────────────────────────────── */

.cp-card {
  /* Component-local custom properties — modifiers override these without
     rewriting the layout rules below. */
  --_cp-card-bg: var(--cp-color-bg-elevated);
  --_cp-card-fg: var(--cp-color-fg);
  --_cp-card-border: var(--cp-color-border-subtle);
  --_cp-card-border-width: var(--cp-border-thin);
  --_cp-card-border-style: solid;
  --_cp-card-radius: var(--cp-radius-md);
  --_cp-card-pad: var(--cp-space-9);
  --_cp-card-gap: var(--cp-space-7);
  --_cp-card-shadow: none;
  --_cp-card-translate: 0;
  --_cp-card-accent-bar: 0;

  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--_cp-card-gap);
  min-inline-size: 0;

  padding: var(--_cp-card-pad);

  font-family: var(--cp-font-family-body);
  color: var(--_cp-card-fg);
  background: var(--_cp-card-bg);
  border: var(--_cp-card-border-width) var(--_cp-card-border-style)
          var(--_cp-card-border);
  border-radius: var(--_cp-card-radius);
  box-shadow: var(--_cp-card-shadow);

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard),
    transform var(--cp-duration-instant) var(--cp-ease-standard);

  transform: translateY(var(--_cp-card-translate));
}

/* ── Elements (BEM parts) ─────────────────────────────────────────────── */

/* Eyebrow — mono uppercase tag that sits above the title. Re-uses the
   .cp-eyebrow tokens from typography.css for consistency. */
.cp-card__eyebrow {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  line-height: var(--cp-line-height-snug);
  /* Override the column gap when the eyebrow leads — pull the title in
     closer than the default card gap. */
  margin-block-end: calc(var(--cp-space-3) - var(--_cp-card-gap));
}

/* Header — title block + optional actions on the trailing edge. */
.cp-card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--cp-space-7);
  min-inline-size: 0;
}
.cp-card__header > .cp-card__heading {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-2);
  min-inline-size: 0;
  flex: 1 1 auto;
}
.cp-card__header > .cp-card__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-4);
  flex: 0 0 auto;
}

/* Title — h-level heading. Picks up the display face above 24px via
   typography.css helpers when authors apply .cp-h3 / .cp-h4; the
   default here renders as a card-title at 20px. */
.cp-card__title {
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-snug);
  letter-spacing: var(--cp-tracking-normal);
  color: var(--cp-color-fg);
  margin: 0;
  /* Allow long titles to wrap inside narrow cards */
  overflow-wrap: anywhere;
}

/* Subtitle — caption style, sits directly below the title. */
.cp-card__subtitle {
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  margin: 0;
}

/* Body — main content area. The default `display: contents` would
   break the column flow, so we keep it as a column container with
   slightly looser gap. */
.cp-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-5);
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);
  color: var(--cp-color-fg);
  min-inline-size: 0;
}
.cp-card__body p { margin: 0; }
.cp-card__body p + p { margin-block-start: var(--cp-space-4); }

/* Footer — action bar at the bottom. Top-bordered divider extends to
   the card edges by absorbing the card's horizontal padding via
   negative margins. */
.cp-card__footer {
  display: flex;
  align-items: center;
  gap: var(--cp-space-5);
  flex-wrap: wrap;
  padding-block-start: var(--cp-space-7);
  margin-block-start: auto;
  margin-inline: calc(var(--_cp-card-pad) * -1);
  padding-inline: var(--_cp-card-pad);
  border-block-start: var(--cp-border-hairline) solid
                       var(--cp-color-border-subtle);
}
.cp-card__footer > .cp-card__footer-spacer { flex: 1 1 auto; }

/* Media — full-bleed image / video region. Sits at the top of the
   card with no padding around it: we cancel the card's padding via
   negative margins and round the top corners to match. The bottom
   corners stay square because content typically follows. */
.cp-card__media {
  position: relative;
  display: block;
  margin: calc(var(--_cp-card-pad) * -1);
  margin-block-end: 0;
  overflow: hidden;
  background: var(--cp-color-bg-inset);
  border-block-end: var(--cp-border-hairline) solid
                     var(--cp-color-border-subtle);
  /* Round only the top corners to mate with the card radius. We
     subtract the border width so the inner crop matches the outer
     edge perfectly. */
  border-start-start-radius: calc(var(--_cp-card-radius) -
                                  var(--_cp-card-border-width));
  border-start-end-radius:   calc(var(--_cp-card-radius) -
                                  var(--_cp-card-border-width));
  aspect-ratio: 16 / 9;
}
.cp-card__media > img,
.cp-card__media > video,
.cp-card__media > svg {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}
/* When the media is an iconographic placeholder (a single .cp-icon or
   inline SVG), center it instead of cropping. */
.cp-card__media--icon {
  display: grid;
  place-items: center;
  color: var(--cp-color-fg-muted);
  /* Diagonal-cross hatch placeholder. The 0.5px stops are the
     canonical gradient-line idiom for drawing a 1px diagonal — there
     is no token for sub-pixel gradient breakpoints and changing it
     would lose the hairline. (Optical-correction exception per
     SPEC §4.) */
  background:
    linear-gradient(135deg,
      transparent calc(50% - 0.5px),
      var(--cp-color-border-subtle) calc(50% - 0.5px),
      var(--cp-color-border-subtle) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)),
    linear-gradient(45deg,
      transparent calc(50% - 0.5px),
      var(--cp-color-border-subtle) calc(50% - 0.5px),
      var(--cp-color-border-subtle) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)),
    var(--cp-color-bg-inset);
}
.cp-card__media--icon > * {
  inline-size: auto;
  block-size: auto;
  object-fit: contain;
  position: relative;
  z-index: 1;
  padding: var(--cp-space-3) var(--cp-space-5);
  background: var(--_cp-card-bg);
  color: var(--cp-color-fg-muted);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
}
.cp-card__media--icon > svg {
  inline-size: var(--cp-space-13);
  block-size: var(--cp-space-13);
  padding: 0;
  background: transparent;
  color: inherit;
}

/* When media sits inside a card with non-default padding, the negative
   margin still works because we read --_cp-card-pad from the variable. */

/* Stats — convenience grid for tabular figures (battery/alt/speed
   rows in vehicle cards). Composable but optional. */
.cp-card__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(8rem, 100%), 1fr));
  gap: var(--cp-space-7) var(--cp-space-9);
}
.cp-card__stat {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-1);
  min-inline-size: 0;
}
.cp-card__stat-label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}
.cp-card__stat-value {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-24);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--cp-color-fg);
}
.cp-card__stat-unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-muted);
  margin-inline-start: var(--cp-space-1);
  font-variant-numeric: tabular-nums;
}

/* Accent rail — used by .cp-card--accent. Sits flush to the leading
   edge of the card; rendered with an absolutely-positioned bar so it
   adapts to any height without us recomputing padding. */
.cp-card::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: var(--_cp-card-accent-bar);
  background: var(--cp-color-accent);
  border-start-start-radius: calc(var(--_cp-card-radius) -
                                  var(--_cp-card-border-width));
  border-end-start-radius:   calc(var(--_cp-card-radius) -
                                  var(--_cp-card-border-width));
  pointer-events: none;
}

/* ── Variants ─────────────────────────────────────────────────────────── */

/* Elevated — the signature design-system look. Stamped offset shadow
   replaces a softer drop shadow; same as the stamped CTA's cube-mark
   cue, scaled for a surface. */
.cp-card--elevated {
  --_cp-card-shadow: var(--cp-shadow-stamp);
  --_cp-card-border: var(--cp-color-border);
  --_cp-card-border-width: var(--cp-border-thin);
}

/* Outlined — no fill, border only. Use for grouping content on an
   already-elevated host (e.g., inside another card) where stacking
   fills would muddy the hierarchy. */
.cp-card--outlined {
  --_cp-card-bg: transparent;
  --_cp-card-border: var(--cp-color-border);
}

/* Inset — sunken sub-surface (bg-inset). Use for callouts, code
   blocks, or sub-sections within a parent card. */
.cp-card--inset {
  --_cp-card-bg: var(--cp-color-bg-inset);
  --_cp-card-border: var(--cp-color-border-subtle);
}

/* Accent — left-edge orange rail. Use to draw the eye to a single
   card in a list (a featured product, the active vehicle, a primary
   recommendation). Pairs naturally with --elevated. */
.cp-card--accent {
  --_cp-card-accent-bar: var(--cp-space-2);
  /* Nudge content right so it doesn't sit on top of the rail. */
  padding-inline-start: calc(var(--_cp-card-pad) + var(--_cp-card-accent-bar));
}
.cp-card--accent .cp-card__footer,
.cp-card--accent .cp-card__media {
  /* Footer divider and media bleed cancel padding-inline-start by the
     same amount so the rail isn't cut by them. */
  margin-inline-start: calc((var(--_cp-card-pad) +
                             var(--_cp-card-accent-bar)) * -1);
  padding-inline-start: calc(var(--_cp-card-pad) + var(--_cp-card-accent-bar));
}
.cp-card--accent .cp-card__media {
  padding-inline-start: 0;
}

/* Compact — half the padding for dense lists. Stat grids, side-rail
   tiles, mobile carousels. */
.cp-card--compact {
  --_cp-card-pad: var(--cp-space-6);
  --_cp-card-gap: var(--cp-space-5);
}

/* ── Sizes ────────────────────────────────────────────────────────────── */

.cp-card--sm {
  --_cp-card-pad: var(--cp-space-7);
  --_cp-card-gap: var(--cp-space-5);
  --_cp-card-radius: var(--cp-radius-sm);
}
.cp-card--sm .cp-card__title { font-size: var(--cp-font-size-16); }
.cp-card--sm .cp-card__stat-value { font-size: var(--cp-font-size-20); }

.cp-card--lg {
  --_cp-card-pad: var(--cp-space-11);
  --_cp-card-gap: var(--cp-space-9);
  --_cp-card-radius: var(--cp-radius-lg);
}
.cp-card--lg .cp-card__title { font-size: var(--cp-font-size-24); }
.cp-card--lg .cp-card__stat-value { font-size: var(--cp-font-size-32); }

/* ── Clickable — the card becomes a button conceptually ────────────────── */

.cp-card--clickable {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  appearance: none;
  text-align: inherit;
  text-decoration: none;
  font: inherit;
  color: inherit;
}
/* If the author chose <button> or <a> as the root, the focus ring from
   base/focus.css already applies. If they used a <div role="button">,
   the [tabindex]:focus-visible rule in focus.css also covers it. */

.cp-card--clickable:hover,
.cp-card--clickable.cp-is-hover {
  --_cp-card-border: var(--cp-color-fg);
  /* -1px lift: a single-pixel optical nudge so the card "comes
     forward" without disturbing layout. Matches the +1px active
     press used by .cp-button. */
  --_cp-card-translate: -1px;
  --_cp-card-shadow: var(--cp-shadow-2);
}
.cp-card--clickable.cp-card--elevated:hover,
.cp-card--clickable.cp-card--elevated.cp-is-hover {
  /* Elevated cards already carry the stamped shadow — stack the
     normal shadow-2 over it so the card lifts AND the stamp stays. */
  --_cp-card-shadow: var(--cp-shadow-stamp), var(--cp-shadow-2);
}

.cp-card--clickable:active,
.cp-card--clickable.cp-is-active {
  /* +1px press, same idiom as .cp-button:active. */
  --_cp-card-translate: 1px;
  --_cp-card-shadow: none;
}
.cp-card--clickable.cp-card--elevated:active,
.cp-card--clickable.cp-card--elevated.cp-is-active {
  /* Pressed: keep the stamp but drop the lift shadow. */
  --_cp-card-shadow: var(--cp-shadow-stamp);
}

.cp-card--clickable:focus-visible {
  /* Let the global focus ring (base/focus.css) handle the outline; we
     pull the border in to match so the focused card reads as a single
     unit rather than a card-inside-a-ring. */
  --_cp-card-border: var(--cp-color-border-focus);
}

/* Selected / toggled — aria-pressed for selection grids
   (e.g. choose-your-vehicle, choose-your-frame). */
.cp-card--clickable[aria-pressed="true"],
.cp-card--clickable[aria-selected="true"] {
  --_cp-card-border: var(--cp-color-accent);
  --_cp-card-border-width: var(--cp-border-thick);
  --_cp-card-bg: color-mix(in srgb,
                           var(--cp-color-accent) 6%,
                           var(--cp-color-bg-elevated));
}
.cp-card--clickable[aria-pressed="true"]:hover,
.cp-card--clickable[aria-selected="true"]:hover {
  --_cp-card-bg: color-mix(in srgb,
                           var(--cp-color-accent) 10%,
                           var(--cp-color-bg-elevated));
}

/* Disabled — focusable + inert. The card still reads, but actions
   inside are explicitly off. */
.cp-card--clickable[aria-disabled="true"],
.cp-card--clickable:disabled {
  --_cp-card-border-style: dashed;
  --_cp-card-fg: var(--cp-color-fg-disabled);
  cursor: not-allowed;
  --_cp-card-translate: 0;
  --_cp-card-shadow: none;
}
.cp-card--clickable[aria-disabled="true"] .cp-card__title,
.cp-card--clickable:disabled .cp-card__title {
  color: var(--cp-color-fg-disabled);
}

/* ── Status modifiers — paired with iconography in real apps. Same
   semantic-state pattern as .cp-button. ──────────────────────────────── */

.cp-card.cp-is-error {
  --_cp-card-border: var(--cp-color-status-danger);
}
.cp-card.cp-is-success {
  --_cp-card-border: var(--cp-color-status-success);
}
.cp-card.cp-is-warning {
  --_cp-card-border: var(--cp-color-status-warning);
}

/* Loading — aria-busy. A subtle shimmer plays across the surface to
   signal "data on the way" without taking over the whole card. */
.cp-card[aria-busy="true"],
.cp-card.cp-is-loading {
  position: relative;
  overflow: hidden;
}
.cp-card[aria-busy="true"]::after,
.cp-card.cp-is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    105deg,
    transparent 30%,
    color-mix(in srgb, var(--cp-color-fg) 6%, transparent) 50%,
    transparent 70%
  );
  background-size: 200% 100%;
  animation: cp-card-shimmer 1.6s linear infinite;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .cp-card[aria-busy="true"]::after,
  .cp-card.cp-is-loading::after { animation-duration: 6s; }
}
@keyframes cp-card-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

/* Mobile — slightly larger touch padding so users on Herelink / phones
   don't accidentally press neighbouring cards in a list. */
.cp-card[data-surface="mobile"] {
  --_cp-card-pad: var(--cp-space-10);
  --_cp-card-gap: var(--cp-space-8);
  --_cp-card-radius: var(--cp-radius-md);
}

/* Embedded — hairline border, sharper radius, mono labels. Cards on a
   240×135 OLED need every pixel to read crisply; soft drop shadows
   blur into the panel. */
.cp-card[data-surface="embedded"] {
  --_cp-card-pad: var(--cp-space-5);
  --_cp-card-gap: var(--cp-space-4);
  --_cp-card-radius: var(--cp-radius-0);
  --_cp-card-border-width: var(--cp-border-hairline);
  --_cp-card-shadow: none;
  font-family: var(--cp-font-family-mono);
}
.cp-card[data-surface="embedded"] .cp-card__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}
.cp-card[data-surface="embedded"] .cp-card__subtitle {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
}
.cp-card[data-surface="embedded"] .cp-card__body {
  font-size: var(--cp-font-size-11);
}
.cp-card[data-surface="embedded"] .cp-card__stat-value {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-16);
  letter-spacing: var(--cp-tracking-normal);
}
.cp-card[data-surface="embedded"] .cp-card__footer {
  padding-block-start: var(--cp-space-4);
}

/* ── Card grid helper — a sensible default container for tiled cards.
   Authors can also use their own grid; this is a "just works" path. ──── */

.cp-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
  gap: var(--cp-space-9);
  align-items: stretch;
}
.cp-card-grid > .cp-card { block-size: 100%; }

/* ── end src/styles/components/card.css ── */
/* ── inlined from src/styles/components/disclosure.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-disclosure — a native <details>/<summary> collapsible.

   The progressive-disclosure primitive. Used for "Read more" on long
   announcement copy, "+N more" inline expansion (serial-number lists,
   HS codes), and grouped detail sub-sections inside an expandable table
   row. Built on real <details>/<summary> so all the open/close semantics,
   keyboard handling, and find-in-page behaviour come for free — no JS.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-disclosure` is the root, set on a <details> element. It carries
     the shared layout + the component-local custom properties.
   - `__summary` (on the <summary>) is the clickable header. We kill the
     native marker (::-webkit-details-marker + list-style:none) and draw
     our own `__marker` chevron that rotates 90° when the parent is [open].
   - `__title` is optional bold text; `__meta` is the trailing muted
     hint/count; `__content` is the revealed body.
   - Treatment modifiers (`--card`, `--ghost`, `--inline`) restyle without
     rewriting layout — they override the local custom props. Default is a
     simple bordered row.
   - States flow from the native element: [open] rotates the marker and may
     tint the summary; :hover / :focus-visible surface the summary; the
     base focus ring (base/focus.css) covers keyboard focus. The rotate
     respects prefers-reduced-motion.
   - `data-surface` reshapes for mobile/embedded without changing markup.
   ───────────────────────────────────────────────────────────────────────── */

.cp-disclosure {
  /* Component-local custom properties so treatment / surface modifiers can
     override them without rewriting the layout rules below. */
  --_cp-disclosure-bg: transparent;
  --_cp-disclosure-fg: var(--cp-color-fg);
  --_cp-disclosure-border: var(--cp-color-border);
  --_cp-disclosure-border-width: var(--cp-border-thin);
  --_cp-disclosure-radius: var(--cp-radius-sm);
  --_cp-disclosure-shadow: none;

  --_cp-disclosure-summary-bg: transparent;
  --_cp-disclosure-summary-bg-hover: var(--cp-color-bg-inset);
  --_cp-disclosure-summary-bg-open: transparent;
  --_cp-disclosure-summary-fg: var(--cp-color-fg);
  --_cp-disclosure-summary-pad-x: var(--cp-space-6);
  --_cp-disclosure-summary-pad-y: var(--cp-space-5);
  --_cp-disclosure-summary-gap: var(--cp-space-5);
  --_cp-disclosure-summary-min-size: var(--cp-target-mouse);
  --_cp-disclosure-summary-font-size: var(--cp-font-size-14);
  --_cp-disclosure-summary-font-weight: var(--cp-font-weight-medium);

  --_cp-disclosure-marker-size: var(--cp-font-size-14);
  --_cp-disclosure-marker-color: var(--cp-color-fg-muted);
  --_cp-disclosure-marker-rotate: 90deg; /* applied at [open] */

  --_cp-disclosure-content-pad-x: var(--cp-space-6);
  --_cp-disclosure-content-pad-y: var(--cp-space-5);
  --_cp-disclosure-content-fg: var(--cp-color-fg);
  --_cp-disclosure-divider: transparent;

  display: block;
  background: var(--_cp-disclosure-bg);
  color: var(--_cp-disclosure-fg);
  border: var(--_cp-disclosure-border-width) solid var(--_cp-disclosure-border);
  border-radius: var(--_cp-disclosure-radius);
  box-shadow: var(--_cp-disclosure-shadow);
  font-family: var(--cp-font-family-body);

  transition:
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

/* ── Summary — the clickable header (a real <summary>) ─────────────────── */

.cp-disclosure__summary {
  display: flex;
  align-items: center;
  gap: var(--_cp-disclosure-summary-gap);

  min-block-size: var(--_cp-disclosure-summary-min-size);
  padding: var(--_cp-disclosure-summary-pad-y) var(--_cp-disclosure-summary-pad-x);

  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-disclosure-summary-font-size);
  font-weight: var(--_cp-disclosure-summary-font-weight);
  line-height: var(--cp-line-height-snug);
  color: var(--_cp-disclosure-summary-fg);
  background: var(--_cp-disclosure-summary-bg);

  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  list-style: none; /* kill the native disclosure triangle (Firefox/standard) */

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}

/* Kill the WebKit/Blink native marker so only our __marker shows. */
.cp-disclosure__summary::-webkit-details-marker {
  display: none;
}
.cp-disclosure__summary::marker {
  content: "";
}

/* ── Marker — a chevron drawn via CSS mask, rotated on [open] ──────────── */

.cp-disclosure__marker {
  flex: 0 0 auto;
  inline-size: var(--_cp-disclosure-marker-size);
  block-size: var(--_cp-disclosure-marker-size);
  background: var(--_cp-disclosure-marker-color);

  /* A right-pointing chevron; the [open] state rotates it to point down.
     Same data-uri-mask approach as the table sort icon / form chevron. */
  -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M6 4l4 4-4 4"/></svg>');
          mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M6 4l4 4-4 4"/></svg>');
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;

  transform: rotate(0deg);
  transition: transform var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-disclosure[open] > .cp-disclosure__summary .cp-disclosure__marker {
  transform: rotate(var(--_cp-disclosure-marker-rotate));
  background: var(--cp-color-fg);
}

/* ── Title + meta — optional summary parts ────────────────────────────── */

/* Bold text inside the summary. Takes the available width so __meta is
   pushed to the trailing edge. */
.cp-disclosure__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  font-weight: var(--cp-font-weight-semibold);
  color: inherit;
}

/* Trailing muted hint / count. Numeric content gets tabular figures so
   "12 fields" / "+4 more" stay aligned across stacked rows. */
.cp-disclosure__meta {
  flex: 0 0 auto;
  margin-inline-start: auto;
  color: var(--cp-color-fg-muted);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-regular);
  font-variant-numeric: tabular-nums;
}

/* ── Content — the revealed body ──────────────────────────────────────── */

.cp-disclosure__content {
  padding: var(--_cp-disclosure-content-pad-y) var(--_cp-disclosure-content-pad-x);
  color: var(--_cp-disclosure-content-fg);
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);
  border-block-start: var(--cp-border-hairline) solid var(--_cp-disclosure-divider);
}
.cp-disclosure__content > :first-child { margin-block-start: 0; }
.cp-disclosure__content > :last-child  { margin-block-end: 0; }

/* ── States ───────────────────────────────────────────────────────────── */

.cp-disclosure__summary:hover,
.cp-disclosure__summary.cp-is-hover {
  background: var(--_cp-disclosure-summary-bg-hover);
}

/* The base focus ring (base/focus.css) covers :focus-visible. Pull it
   inside the rounded root so the ring tracks the summary, not the page. */
.cp-disclosure__summary:focus-visible {
  outline-offset: calc(-1 * var(--cp-space-1));
  border-radius: inherit;
}

/* Open — let the summary swap to its open bg (modifiers set this). */
.cp-disclosure[open] > .cp-disclosure__summary {
  background: var(--_cp-disclosure-summary-bg-open);
}

/* Disabled — a disclosure that can't be toggled right now. There is no
   native disabled <details>, so we express it with [aria-disabled] on the
   summary: inert pointer, faded chrome, no hover. */
.cp-disclosure__summary[aria-disabled="true"] {
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
  pointer-events: none;
}
.cp-disclosure__summary[aria-disabled="true"] .cp-disclosure__marker {
  background: var(--cp-color-fg-disabled);
}

/* ── Treatment: default (simple bordered row) ─────────────────────────── */
/* The base block already paints a bordered row with a transparent fill and
   a hairline divider above the content. Nothing extra to declare here. */

/* ── Treatment: --card ────────────────────────────────────────────────── */
/* Bordered, elevated card container. Larger radius, summary gets a roomier
   pad and a hover/open surface; content is separated by a visible hairline. */
.cp-disclosure--card {
  --_cp-disclosure-bg: var(--cp-color-bg-elevated);
  --_cp-disclosure-radius: var(--cp-radius-md);
  --_cp-disclosure-shadow: var(--cp-shadow-1);
  --_cp-disclosure-border: var(--cp-color-border-subtle);

  --_cp-disclosure-summary-pad-x: var(--cp-space-7);
  --_cp-disclosure-summary-pad-y: var(--cp-space-6);
  --_cp-disclosure-summary-font-size: var(--cp-font-size-16);
  --_cp-disclosure-summary-bg-hover: var(--cp-color-bg-inset);
  --_cp-disclosure-summary-bg-open: var(--cp-color-bg-inset);

  --_cp-disclosure-content-pad-x: var(--cp-space-7);
  --_cp-disclosure-content-pad-y: var(--cp-space-7);
  --_cp-disclosure-divider: var(--cp-color-border-subtle);
}
/* Keep the summary's rounded corners flush with the card while closed; when
   open the divider does the visual work. */
.cp-disclosure--card > .cp-disclosure__summary {
  border-radius: var(--_cp-disclosure-radius);
}
.cp-disclosure--card[open] > .cp-disclosure__summary {
  border-end-start-radius: 0;
  border-end-end-radius: 0;
}

/* ── Treatment: --ghost ───────────────────────────────────────────────── */
/* No border, minimal chrome — for inline "Read more" on long copy. The
   summary reads as an accent trigger; the content is flush-left, no divider. */
.cp-disclosure--ghost {
  --_cp-disclosure-border-width: 0;
  --_cp-disclosure-radius: var(--cp-radius-0);

  --_cp-disclosure-summary-pad-x: 0;
  --_cp-disclosure-summary-pad-y: var(--cp-space-2);
  --_cp-disclosure-summary-gap: var(--cp-space-3);
  --_cp-disclosure-summary-min-size: auto;
  --_cp-disclosure-summary-fg: var(--cp-color-accent);
  --_cp-disclosure-summary-font-weight: var(--cp-font-weight-semibold);
  --_cp-disclosure-summary-bg-hover: transparent;

  --_cp-disclosure-marker-color: var(--cp-color-accent);

  --_cp-disclosure-content-pad-x: 0;
  --_cp-disclosure-content-pad-y: var(--cp-space-4);
  --_cp-disclosure-content-fg: var(--cp-color-fg-muted);
}
.cp-disclosure--ghost > .cp-disclosure__content {
  border-block-start: 0;
}
.cp-disclosure--ghost .cp-disclosure__summary:hover,
.cp-disclosure--ghost .cp-disclosure__summary.cp-is-hover {
  --_cp-disclosure-summary-fg: color-mix(in srgb, var(--cp-color-accent) 80%, black);
  --_cp-disclosure-marker-color: color-mix(in srgb, var(--cp-color-accent) 80%, black);
}
/* Ghost keeps the chevron in the accent even when open. */
.cp-disclosure--ghost[open] > .cp-disclosure__summary .cp-disclosure__marker {
  background: var(--cp-color-accent);
}

/* ── Treatment: --inline ──────────────────────────────────────────────── */
/* Compact, for "+N more" — the summary is a small accent link-like trigger
   that sits inline after a list. Tighter type, square mini-chevron, content
   tucked right under the trigger. */
.cp-disclosure--inline {
  --_cp-disclosure-border-width: 0;
  --_cp-disclosure-radius: var(--cp-radius-0);

  --_cp-disclosure-summary-pad-x: 0;
  --_cp-disclosure-summary-pad-y: 0;
  --_cp-disclosure-summary-gap: var(--cp-space-2);
  --_cp-disclosure-summary-min-size: var(--cp-target-compact);
  --_cp-disclosure-summary-fg: var(--cp-color-accent);
  --_cp-disclosure-summary-font-size: var(--cp-font-size-12);
  --_cp-disclosure-summary-font-weight: var(--cp-font-weight-semibold);
  --_cp-disclosure-summary-bg-hover: transparent;

  --_cp-disclosure-marker-size: var(--cp-font-size-11);
  --_cp-disclosure-marker-color: var(--cp-color-accent);

  --_cp-disclosure-content-pad-x: 0;
  --_cp-disclosure-content-pad-y: var(--cp-space-3);
  --_cp-disclosure-content-fg: var(--cp-color-fg);

  display: inline-block;
}
.cp-disclosure--inline > .cp-disclosure__summary {
  display: inline-flex;
}
.cp-disclosure--inline > .cp-disclosure__content {
  border-block-start: 0;
  font-size: var(--cp-font-size-12);
}
.cp-disclosure--inline .cp-disclosure__summary:hover,
.cp-disclosure--inline .cp-disclosure__summary.cp-is-hover {
  --_cp-disclosure-summary-fg: color-mix(in srgb, var(--cp-color-accent) 80%, black);
  --_cp-disclosure-marker-color: color-mix(in srgb, var(--cp-color-accent) 80%, black);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.cp-disclosure--inline[open] > .cp-disclosure__summary .cp-disclosure__marker {
  background: var(--cp-color-accent);
}

/* ── Reduced motion — don't animate the rotate or surface fades ───────── */

@media (prefers-reduced-motion: reduce) {
  .cp-disclosure,
  .cp-disclosure__summary,
  .cp-disclosure__marker {
    transition: none;
  }
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-disclosure[data-surface="mobile"] {
  --_cp-disclosure-radius: var(--cp-radius-md);
  --_cp-disclosure-summary-min-size: var(--cp-target-touch);
  --_cp-disclosure-summary-pad-x: var(--cp-space-7);
  --_cp-disclosure-summary-pad-y: var(--cp-space-6);
  --_cp-disclosure-summary-font-size: var(--cp-font-size-16);
  --_cp-disclosure-marker-size: var(--cp-font-size-18);
  --_cp-disclosure-content-pad-x: var(--cp-space-7);
  --_cp-disclosure-content-pad-y: var(--cp-space-6);
}

.cp-disclosure[data-surface="embedded"] {
  --_cp-disclosure-radius: var(--cp-radius-xs);
  --_cp-disclosure-border-width: var(--cp-border-hairline);
  --_cp-disclosure-border: var(--cp-color-border-subtle);
  --_cp-disclosure-shadow: none;
  --_cp-disclosure-summary-min-size: var(--cp-target-compact);
  --_cp-disclosure-summary-pad-x: var(--cp-space-4);
  --_cp-disclosure-summary-pad-y: var(--cp-space-2);
  --_cp-disclosure-summary-font-size: var(--cp-font-size-11);
  --_cp-disclosure-summary-font-weight: var(--cp-font-weight-medium);
  --_cp-disclosure-marker-size: var(--cp-font-size-11);
  --_cp-disclosure-content-pad-x: var(--cp-space-4);
  --_cp-disclosure-content-pad-y: var(--cp-space-3);
}
.cp-disclosure[data-surface="embedded"] .cp-disclosure__summary {
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}
.cp-disclosure[data-surface="embedded"] .cp-disclosure__content {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
}
.cp-disclosure[data-surface="embedded"] .cp-disclosure__meta {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
}

/* ── end src/styles/components/disclosure.css ── */
/* ── inlined from src/styles/components/empty.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-empty — empty & error state container.

   First-impression of a feature, last-impression of a failure. The
   pattern is always the same shape — icon → title → message → actions
   → meta — so the user gets the same mental model whether they are
   looking at "no missions yet", "RTK base offline", or "404".

   Composes inside a .cp-card or .cp-card--outlined when a frame is
   wanted. Stands alone when the host already provides one (a page,
   a panel).

   Variants steer intent (filter / error / network / permission / 404)
   via a colored block-start border and a tinted icon frame — the icon
   is meaningful, not decorative, so the icon name in the consumer
   markup should match the intent.

   Sizes step up padding for full-page takeovers (--lg) and step down
   for inline-in-card situations (--sm).

   States follow SPEC.md §3. The container itself is not interactive;
   the actions inside it are real .cp-button elements that carry their
   own ARIA.
   ───────────────────────────────────────────────────────────────────────── */

.cp-empty {
  /* Component-local custom properties — variants and sizes override
     these without rewriting the layout rules below. */
  --_cp-empty-pad-block: var(--cp-space-12);
  --_cp-empty-pad-inline: var(--cp-space-10);
  --_cp-empty-gap: var(--cp-space-7);
  --_cp-empty-max-inline: 480px;       /* optical max-width; not a token-able value */
  --_cp-empty-tint: var(--cp-color-fg-muted);
  --_cp-empty-rule: transparent;
  --_cp-empty-rule-width: 0;

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--_cp-empty-gap);

  max-inline-size: var(--_cp-empty-max-inline);
  margin-inline: auto;
  padding: var(--_cp-empty-pad-block) var(--_cp-empty-pad-inline);

  text-align: center;
  color: var(--cp-color-fg);
  font-family: var(--cp-font-family-body);

  /* Variant rule: a colored block-start hairline that signals intent
     without dominating the surface. Pulled flush to the container
     edges via negative inline margins so it bleeds when nested in a
     card; cancelled when --rule-width is 0 (default / neutral). */
  border-block-start: var(--_cp-empty-rule-width) solid
                       var(--_cp-empty-rule);
}

/* ── Parts ────────────────────────────────────────────────────────────── */

/* Icon frame — circle/square holder that gives the glyph visual weight.
   The frame inherits its color from the variant (--_cp-empty-tint) and
   draws a subtle tinted disc behind the icon via color-mix so the
   color reads as "muted" not "loud". */
.cp-empty__icon-frame {
  --_cp-empty-frame-size: var(--cp-space-14);                  /* 64px default */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--_cp-empty-frame-size);
  block-size: var(--_cp-empty-frame-size);
  border-radius: var(--cp-radius-pill);
  background: color-mix(in srgb, var(--_cp-empty-tint) 10%, transparent);
  color: var(--_cp-empty-tint);
  border: var(--cp-border-hairline) solid
          color-mix(in srgb, var(--_cp-empty-tint) 28%, transparent);
  /* Tick the frame down a sliver so the icon doesn't pile against the
     headline; subsumed by the parent gap when the consumer omits the
     frame. */
  margin-block-end: calc(var(--cp-space-2) * -1);
  /* The icon inside the frame is intentionally bigger than the wrapper
     would normally allow; lock the inner SVG so it does not stretch. */
  flex: none;
}
.cp-empty__icon-frame > .cp-icon,
.cp-empty__icon-frame > svg {
  inline-size: calc(var(--_cp-empty-frame-size) * 0.5);
  block-size: calc(var(--_cp-empty-frame-size) * 0.5);
  color: inherit;
}

/* Standalone icon (no frame) — for the lightest treatments where a
   bare glyph reads cleaner than a tinted disc. */
.cp-empty__icon {
  color: var(--_cp-empty-tint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cp-empty__icon > .cp-icon,
.cp-empty__icon > svg {
  inline-size: var(--cp-space-13);
  block-size: var(--cp-space-13);
  color: inherit;
}

/* Title — display face at 24px+; falls back to Saira semibold under.
   Composes naturally with .cp-h3 / .cp-h4 if the author wants explicit
   sizing. */
.cp-empty__title {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-24);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
  margin: 0;
  text-wrap: balance;
}

/* Message — body text, muted. Capped at 40ch so long copy doesn't
   stretch into an unreadable line. */
.cp-empty__message {
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);
  color: var(--cp-color-fg-muted);
  margin: 0;
  max-inline-size: 40ch;
  text-wrap: pretty;
}

/* Actions — centered button row. Wrap on narrow widths instead of
   overflowing. Authors drop real .cp-button elements inside. */
.cp-empty__actions {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--cp-space-5);
  margin-block-start: var(--cp-space-3);
}

/* Meta — mono fine-print under the actions. Error codes, request IDs,
   firmware versions. Pair with tabular-nums via .cp-num if needed. */
.cp-empty__meta {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-fg-faint);
  margin: 0;
  margin-block-start: var(--cp-space-2);
  font-variant-numeric: tabular-nums;
  /* Allow long error strings to wrap on commas / slashes without
     pushing the layout wide. */
  overflow-wrap: anywhere;
}

/* ── Variants — intent by tint + rule colour ───────────────────────────── */

/* (default) — neutral / "nothing here yet". Muted tint, no rule. */

/* Filter — "no results match". Same neutral tint as default; the
   slightly different framing comes from the consumer's choice of
   icon (compass / magnifier-shaped). Provided as a class so authors
   can lean on it semantically. */
.cp-empty--filter {
  --_cp-empty-tint: var(--cp-color-fg-muted);
}

/* Error — generic failure. Danger-tinted icon + thin top rule. */
.cp-empty--error {
  --_cp-empty-tint: var(--cp-color-status-danger);
  --_cp-empty-rule: var(--cp-color-status-danger);
  --_cp-empty-rule-width: var(--cp-border-thick);
}

/* Network — connection lost. Warning tint (yellow); a lost link is
   recoverable, not fatal. */
.cp-empty--network {
  --_cp-empty-tint: var(--cp-color-status-warning);
  --_cp-empty-rule: var(--cp-color-status-warning);
  --_cp-empty-rule-width: var(--cp-border-thick);
}

/* Permission — access denied. Danger tint, slightly heavier rule
   because the action genuinely cannot proceed. */
.cp-empty--permission {
  --_cp-empty-tint: var(--cp-color-status-danger);
  --_cp-empty-rule: var(--cp-color-status-danger);
  --_cp-empty-rule-width: var(--cp-border-thick);
}

/* 404 — page not found. Accent tint (orange) so it reads as a
   "found nothing here" beacon rather than a hard error. The doc
   page pairs this with a big mono "404" headline instead of the
   icon frame, which is the canonical treatment. */
.cp-empty--404 {
  --_cp-empty-tint: var(--cp-color-accent);
  --_cp-empty-rule: var(--cp-color-accent);
  --_cp-empty-rule-width: var(--cp-border-thick);
}

/* ── Big numeric — the 404 hero. ───────────────────────────────────────── */

.cp-empty__numeric {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-72);
  font-weight: var(--cp-font-weight-regular);
  line-height: 1;
  letter-spacing: var(--cp-tracking-tight);
  color: var(--_cp-empty-tint);
  font-variant-numeric: tabular-nums;
  margin: 0;
  /* Pull the headline closer than the default gap so the number
     reads as a logotype rather than a paragraph. */
  margin-block-end: calc(var(--cp-space-2) * -1);
}

/* ── Sizes ────────────────────────────────────────────────────────────── */

/* Small — inline-in-card. Half the padding, tighter type. */
.cp-empty--sm {
  --_cp-empty-pad-block: var(--cp-space-9);
  --_cp-empty-pad-inline: var(--cp-space-8);
  --_cp-empty-gap: var(--cp-space-5);
}
.cp-empty--sm .cp-empty__icon-frame {
  --_cp-empty-frame-size: var(--cp-space-13);                  /* 48px */
}
.cp-empty--sm .cp-empty__icon > .cp-icon,
.cp-empty--sm .cp-empty__icon > svg {
  inline-size: var(--cp-space-12);                             /* 40px */
  block-size: var(--cp-space-12);
}
.cp-empty--sm .cp-empty__title {
  /* Step the title down to Saira semibold at 18px — below the 24px
     threshold the SPEC reserves for the display face. */
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-18);
  font-weight: var(--cp-font-weight-semibold);
  letter-spacing: var(--cp-tracking-normal);
}
.cp-empty--sm .cp-empty__message {
  font-size: var(--cp-font-size-12);
}
.cp-empty--sm .cp-empty__numeric {
  font-size: var(--cp-font-size-40);
}

/* Large — full-page takeover. Generous padding, larger icon frame,
   ampler max-inline so paragraphs don't read cramped on a 4K canvas. */
.cp-empty--lg {
  --_cp-empty-pad-block: var(--cp-space-15);
  --_cp-empty-pad-inline: var(--cp-space-12);
  --_cp-empty-gap: var(--cp-space-9);
  --_cp-empty-max-inline: 560px;
}
.cp-empty--lg .cp-empty__icon-frame {
  --_cp-empty-frame-size: var(--cp-space-15);                  /* 80px */
}
.cp-empty--lg .cp-empty__title {
  font-size: var(--cp-font-size-32);
}
.cp-empty--lg .cp-empty__message {
  font-size: var(--cp-font-size-16);
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

/* Mobile — slightly tighter inline padding so the message gets enough
   character width on narrow phone widths, larger touch targets handled
   by the buttons themselves. */
.cp-empty[data-surface="mobile"] {
  --_cp-empty-pad-block: var(--cp-space-11);
  --_cp-empty-pad-inline: var(--cp-space-9);
  --_cp-empty-gap: var(--cp-space-8);
}

/* Embedded — terminal-like. No icon frame, mono everything, hairline
   rules. Designed for the 240×135 / 320×240 OLED panels on flight
   controllers where pixels are precious and an iconographic empty
   state reads as decoration. */
.cp-empty[data-surface="embedded"] {
  --_cp-empty-pad-block: var(--cp-space-6);
  --_cp-empty-pad-inline: var(--cp-space-6);
  --_cp-empty-gap: var(--cp-space-4);
  font-family: var(--cp-font-family-mono);
  text-align: start;
  align-items: stretch;
}
.cp-empty[data-surface="embedded"] .cp-empty__icon-frame,
.cp-empty[data-surface="embedded"] .cp-empty__icon {
  /* Iconography is fine on a 720p phone; on a 240×135 OLED it just
     turns into a smear. Hide the big visuals on this surface. */
  display: none;
}
.cp-empty[data-surface="embedded"] .cp-empty__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}
.cp-empty[data-surface="embedded"] .cp-empty__message {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-normal);
  max-inline-size: none;
  text-align: start;
}
.cp-empty[data-surface="embedded"] .cp-empty__actions {
  justify-content: flex-start;
  margin-block-start: var(--cp-space-3);
}
.cp-empty[data-surface="embedded"] .cp-empty__meta {
  text-align: start;
}
.cp-empty[data-surface="embedded"] .cp-empty__numeric {
  font-size: var(--cp-font-size-24);
  text-align: start;
}

/* ── Composition niceties ─────────────────────────────────────────────── */

/* When nested in a .cp-card, drop the card's own padding contribution
   because .cp-empty already provides generous breathing room of its
   own. Authors who want both can override by adding their own padding. */
.cp-card > .cp-empty,
.cp-card--outlined > .cp-empty {
  margin: calc(var(--_cp-card-pad, var(--cp-space-9)) * -1);
  /* Re-bound the inline size to the card width so the auto-margin
     centering still works. */
  max-inline-size: none;
}
/* …unless this is the small variant, which is designed to live inside
   a card with the card's padding intact. */
.cp-card > .cp-empty--sm,
.cp-card--outlined > .cp-empty--sm {
  margin: 0;
  max-inline-size: var(--_cp-empty-max-inline);
}

/* Reduced motion — kill any pulse/spin on icons inside an empty
   state when the user has asked for less motion. The .cp-icon--spin
   class already honours this; documenting the rule here for clarity. */
@media (prefers-reduced-motion: reduce) {
  .cp-empty .cp-icon--spin > svg,
  .cp-empty .cp-icon--spin svg {
    animation: none;
  }
}

/* ── end src/styles/components/empty.css ── */
/* ── inlined from src/styles/components/footer.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-footer — page-level footer family.

   Architecture mirrors button.css (the canonical reference):
   - Single root class `.cp-footer` carries shared layout/typography.
   - Modifiers `--minimal` `--multi` `--app` `--inset` swap layout/feel.
   - Element parts (BEM) scope the multi-column anatomy:
       __container, __brand, __col, __col-title, __links, __link,
       __bottom, __copyright, __legal, __social
   - Component-local custom properties (`--_cp-footer-*`) so modifiers can
     override the layout without rewriting rules.

   Surface notes:
   - "embedded" (OLED) intentionally has NO footer. Embedded firmware has
     a status strip, not a page footer; that's handled by the app status
     bar pattern elsewhere. Documented in docs/components/footer.html.
   ───────────────────────────────────────────────────────────────────────── */

.cp-footer {
  --_cp-footer-bg: var(--cp-color-bg);
  --_cp-footer-fg: var(--cp-color-fg-muted);
  --_cp-footer-border: var(--cp-color-border-subtle);
  --_cp-footer-pad-block: var(--cp-space-11);
  --_cp-footer-pad-inline: var(--cp-space-10);
  --_cp-footer-max-w: 1280px;
  --_cp-footer-font-size: var(--cp-font-size-14);
  --_cp-footer-gap: var(--cp-space-9);

  display: block;
  padding: var(--_cp-footer-pad-block) var(--_cp-footer-pad-inline);
  background: var(--_cp-footer-bg);
  color: var(--_cp-footer-fg);
  border-block-start: var(--cp-border-hairline) solid var(--_cp-footer-border);

  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-footer-font-size);
  line-height: var(--cp-line-height-normal);
}

/* ── Anatomy parts ────────────────────────────────────────────────────── */

.cp-footer__container {
  max-inline-size: var(--_cp-footer-max-w);
  margin-inline: auto;
}

/* Brand column — logo + tagline + (optional) social row */
.cp-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-7);
  max-inline-size: 36ch;
}
.cp-footer__brand img,
.cp-footer__brand svg {
  block-size: var(--cp-space-12); /* ~40px wordmark height */
  inline-size: auto;
  display: block;
}
.cp-footer__tagline {
  margin: 0;
  font-size: var(--cp-font-size-14);
  color: var(--cp-color-fg-muted);
  line-height: var(--cp-line-height-normal);
  max-inline-size: 32ch;
}

/* Link column */
.cp-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-6);
  min-inline-size: 0;
}

/* Column header — mono uppercase eyebrow */
.cp-footer__col-title {
  margin: 0;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
}

/* Link list — ul reset */
.cp-footer__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-4);
  font-size: var(--cp-font-size-14);
}
.cp-footer__links li { min-inline-size: 0; }

/* Link */
.cp-footer__link {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  color: var(--cp-color-fg-muted);
  text-decoration: none;
  border-block-end: var(--cp-border-hairline) solid transparent;
  padding-block-end: 1px; /* optical — keeps baseline steady when underline appears */
  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-footer__link:hover,
.cp-footer__link.cp-is-hover {
  color: var(--cp-color-fg);
  border-block-end-color: var(--cp-color-fg);
}
.cp-footer__link:focus-visible {
  /* Base focus.css supplies the ring; we just ensure the text color flips. */
  color: var(--cp-color-fg);
}

/* Bottom row — copyright + legal + (optional) social */
.cp-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--cp-space-7);
  margin-block-start: var(--cp-space-9);
  padding-block-start: var(--cp-space-7);
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}

.cp-footer__copyright {
  margin: 0;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-fg-faint);
}

.cp-footer__legal {
  list-style: none;
  margin: 0;
  padding: 0;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cp-space-7);
  font-size: var(--cp-font-size-12);
}
.cp-footer__legal li {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-7);
}
.cp-footer__legal li + li::before {
  content: "·";
  color: var(--cp-color-fg-faint);
  /* Negative margin pulls the dot closer to the gap mid-point. Optical. */
  margin-inline-start: calc(-1 * var(--cp-space-4));
}

.cp-footer__social {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
}
.cp-footer__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-target-mouse);
  block-size: var(--cp-target-mouse);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
  color: var(--cp-color-fg-muted);
  text-decoration: none;
  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-footer__social a:hover {
  color: var(--cp-color-fg);
  border-color: var(--cp-color-fg);
  background: var(--cp-color-bg-inset);
}
.cp-footer__social a > svg,
.cp-footer__social a > .cp-icon {
  inline-size: var(--cp-space-8);
  block-size: var(--cp-space-8);
}

/* ── Variants ─────────────────────────────────────────────────────────── */

/* Minimal — single-row layout: brand left, copyright center, social right.
   The simplest possible page footer. */
.cp-footer--minimal .cp-footer__container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--cp-space-7);
}
.cp-footer--minimal .cp-footer__copyright {
  /* When the legal cluster sits beside it, allow it to wrap independently. */
  flex: 1 1 auto;
  text-align: center;
}
.cp-footer--minimal .cp-footer__brand {
  flex-direction: row;
  align-items: center;
  gap: var(--cp-space-7);
  max-inline-size: none;
}
.cp-footer--minimal .cp-footer__brand img,
.cp-footer--minimal .cp-footer__brand svg {
  block-size: var(--cp-space-11); /* ~32px for minimal contexts */
}
.cp-footer--minimal .cp-footer__bottom {
  /* In minimal, the row IS the bottom; suppress the duplicate border. */
  margin-block-start: 0;
  padding-block-start: 0;
  border-block-start: 0;
}

/* Multi — 4-column layout: brand | product | resources | legal */
.cp-footer--multi .cp-footer__container {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--cp-space-12) var(--cp-space-11);
}

/* App — for in-app contexts. Minimal, hairline, mono. */
.cp-footer--app {
  --_cp-footer-pad-block: var(--cp-space-5);
  --_cp-footer-pad-inline: var(--cp-space-9);
  --_cp-footer-font-size: var(--cp-font-size-11);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-fg-faint);
}
.cp-footer--app .cp-footer__container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cp-space-9);
  font-variant-numeric: tabular-nums;
}
.cp-footer--app .cp-footer__status {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
}
.cp-footer--app .cp-footer__status::before {
  content: "";
  inline-size: var(--cp-space-3);
  block-size: var(--cp-space-3);
  border-radius: 50%;
  background: var(--cp-color-status-success);
  box-shadow: 0 0 var(--cp-space-3)
              color-mix(in srgb, var(--cp-color-status-success) 60%, transparent);
}
.cp-footer--app .cp-footer__status[data-state="warning"]::before {
  background: var(--cp-color-status-warning);
  box-shadow: 0 0 var(--cp-space-3)
              color-mix(in srgb, var(--cp-color-status-warning) 60%, transparent);
}
.cp-footer--app .cp-footer__status[data-state="danger"]::before {
  background: var(--cp-color-status-danger);
  box-shadow: 0 0 var(--cp-space-3)
              color-mix(in srgb, var(--cp-color-status-danger) 60%, transparent);
}
.cp-footer--app .cp-footer__status[data-state="info"]::before {
  background: var(--cp-color-status-info);
}
.cp-footer--app .cp-footer__spacer { flex: 1 1 auto; }

/* Inset — stronger visual demarcation: filled background. Composes with
   any of the above. */
.cp-footer--inset {
  --_cp-footer-bg: var(--cp-color-bg-inset);
}

/* ── Responsive — for --multi ─────────────────────────────────────────── */

/* Tablet: collapse to 2 columns; brand spans the full width. */
@media (max-width: 900px) {
  .cp-footer--multi .cp-footer__container {
    grid-template-columns: 1fr 1fr;
    gap: var(--cp-space-11) var(--cp-space-10);
  }
  .cp-footer--multi .cp-footer__brand {
    grid-column: 1 / -1;
  }
  .cp-footer--multi .cp-footer__bottom {
    grid-column: 1 / -1;
  }
}

/* Mobile: single column. Link columns become <details> accordions so the
   page doesn't become a wall of links. The disclosure marker is the chevron
   from system defaults; we restyle the summary to look like a tappable row. */
@media (max-width: 600px) {
  .cp-footer {
    --_cp-footer-pad-inline: var(--cp-space-9);
  }
  .cp-footer--multi .cp-footer__container {
    grid-template-columns: 1fr;
    gap: var(--cp-space-9);
  }
  .cp-footer--multi .cp-footer__col {
    /* When a <details> is used as the column, drop the default flex stack
       (the <details>/summary handles its own layout). */
    gap: 0;
    border-block-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  }
  .cp-footer--multi .cp-footer__col:last-of-type {
    border-block-end: 0;
  }
  /* If the column IS a details element, treat the summary as the title. */
  details.cp-footer__col > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-block: var(--cp-space-6);
    font-family: var(--cp-font-family-mono);
    font-size: var(--cp-font-size-11);
    font-weight: var(--cp-font-weight-medium);
    letter-spacing: var(--cp-tracking-caps);
    text-transform: uppercase;
    color: var(--cp-color-fg);
    min-block-size: var(--cp-target-touch);
  }
  details.cp-footer__col > summary::-webkit-details-marker { display: none; }
  details.cp-footer__col > summary::after {
    content: "+";
    font-family: var(--cp-font-family-mono);
    font-size: var(--cp-font-size-18);
    color: var(--cp-color-fg-muted);
    line-height: 1;
    transition: transform var(--cp-duration-fast) var(--cp-ease-standard);
  }
  details.cp-footer__col[open] > summary::after {
    content: "−";
  }
  details.cp-footer__col > .cp-footer__links {
    padding-block: var(--cp-space-4) var(--cp-space-7);
    padding-inline-start: var(--cp-space-4);
  }
  details.cp-footer__col > .cp-footer__links .cp-footer__link {
    min-block-size: var(--cp-target-touch);
    align-items: center;
  }
}

/* Reduced-motion: don't slide the +/− glyph. */
@media (prefers-reduced-motion: reduce) {
  details.cp-footer__col > summary::after { transition: none; }
}

/* ── end src/styles/components/footer.css ── */
/* ── inlined from src/styles/components/form.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   Forms & Inputs — text input, textarea, select, checkbox, radio, switch,
   fieldset, plus the .cp-field wrapper that lays them out with eyebrow
   labels, hints, and error text.

   Design intent comes from scratch/categories/forms.html (translated from
   sketch to production). Naming, states, and tokens follow SPEC.md.

   States — prefer the native attribute (:focus-visible, :disabled,
   [readonly], :checked) and fall back to `.cp-is-error` / `.cp-is-success`
   only when the browser cannot infer state from markup.

   The check/radio/select indicators are inline SVG data-URIs painted with
   currentColor, so they inherit theme colour automatically and require no
   theme-specific overrides.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Field wrapper ─────────────────────────────────────────────────────── */

.cp-field {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-3);
  min-inline-size: 0;
}

.cp-field__label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  /* Keep label snug against the control */
  line-height: var(--cp-line-height-snug);
}

.cp-field__label .cp-field__required {
  color: var(--cp-color-status-danger);
  margin-inline-start: var(--cp-space-1);
}

.cp-field__hint {
  font-size: var(--cp-font-size-12);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
}

.cp-field__error {
  font-size: var(--cp-font-size-12);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-status-danger);
  display: none;
  /* Pair the colour cue with a leading glyph for a11y (SPEC §8: colour is
     never the only state signal). */
  font-weight: var(--cp-font-weight-medium);
}
.cp-field__error::before {
  content: "! ";
  font-family: var(--cp-font-family-mono);
}

.cp-field.cp-is-error .cp-field__error { display: block; }
.cp-field.cp-is-error .cp-field__hint  { display: none; }

/* Inline "label : control" layout, e.g. param row */
.cp-field-row {
  display: flex;
  align-items: center;
  gap: var(--cp-space-6);
  flex-wrap: wrap;
}
.cp-field-row .cp-field__label {
  flex: 0 0 auto;
}

/* Vertical stack of fields */
.cp-field-group {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-7);
}

/* ── Shared control surface ────────────────────────────────────────────── */

.cp-input,
.cp-textarea,
.cp-select {
  /* Layout */
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  inline-size: 100%;
  min-block-size: var(--cp-target-mouse);
  padding-block: var(--cp-space-3);
  padding-inline: var(--cp-space-5);

  /* Surface */
  background: var(--cp-color-bg-elevated);
  border: var(--cp-border-thin) solid var(--cp-color-border);
  border-radius: var(--cp-radius-xs);
  color: var(--cp-color-fg);

  /* Type */
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-snug);

  /* Reset native chrome */
  appearance: none;
  -webkit-appearance: none;
  margin: 0;

  transition:
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-input::placeholder,
.cp-textarea::placeholder {
  color: var(--cp-color-fg-faint);
  font-style: italic;
}

/* Focus — keyboard + click both get the accent treatment for inputs,
   since text fields are always operated by hand. (Buttons use
   focus-visible only.) */
.cp-input:focus,
.cp-textarea:focus,
.cp-select:focus {
  outline: none;
  border-color: var(--cp-color-border-focus);
  box-shadow: var(--cp-shadow-focus);
}

/* Disabled */
.cp-input:disabled,
.cp-textarea:disabled,
.cp-select:disabled,
.cp-input[aria-disabled="true"],
.cp-textarea[aria-disabled="true"],
.cp-select[aria-disabled="true"] {
  color: var(--cp-color-fg-disabled);
  border-style: dashed;
  background: var(--cp-color-bg-inset);
  cursor: not-allowed;
}

/* Readonly — looks "settled" rather than disabled */
.cp-input[readonly],
.cp-textarea[readonly] {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg-muted);
  cursor: default;
}
.cp-input[readonly]:focus,
.cp-textarea[readonly]:focus {
  /* Don't pretend the field is editable */
  box-shadow: none;
  border-color: var(--cp-color-border);
}

/* Error / Success — paired with adjacent helper text & glyphs for non-colour
   signal. */
.cp-input.cp-is-error,
.cp-textarea.cp-is-error,
.cp-select.cp-is-error,
.cp-field.cp-is-error .cp-input,
.cp-field.cp-is-error .cp-textarea,
.cp-field.cp-is-error .cp-select {
  border-color: var(--cp-color-status-danger);
}
.cp-input.cp-is-error:focus,
.cp-textarea.cp-is-error:focus,
.cp-select.cp-is-error:focus,
.cp-field.cp-is-error .cp-input:focus,
.cp-field.cp-is-error .cp-textarea:focus,
.cp-field.cp-is-error .cp-select:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cp-color-status-danger) 28%, transparent);
}

.cp-input.cp-is-success,
.cp-textarea.cp-is-success,
.cp-select.cp-is-success,
.cp-field.cp-is-success .cp-input,
.cp-field.cp-is-success .cp-textarea,
.cp-field.cp-is-success .cp-select {
  border-color: var(--cp-color-status-success);
}
.cp-input.cp-is-success:focus,
.cp-textarea.cp-is-success:focus,
.cp-select.cp-is-success:focus,
.cp-field.cp-is-success .cp-input:focus,
.cp-field.cp-is-success .cp-textarea:focus,
.cp-field.cp-is-success .cp-select:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cp-color-status-success) 28%, transparent);
}

/* ── Input sizes ───────────────────────────────────────────────────────── */

.cp-input--sm {
  min-block-size: var(--cp-target-compact);
  font-size: var(--cp-font-size-12);
  padding-block: var(--cp-space-2);
  padding-inline: var(--cp-space-4);
}

.cp-input--lg {
  min-block-size: var(--cp-target-touch);
  font-size: var(--cp-font-size-16);
  padding-block: var(--cp-space-5);
  padding-inline: var(--cp-space-6);
}

/* Mono variant — for param values, IDs, callsigns */
.cp-input--mono {
  font-family: var(--cp-font-family-mono);
  font-variant-numeric: tabular-nums;
}

/* ── Input with leading icon slot ──────────────────────────────────────── */

.cp-input--with-icon {
  /* Reserve space for the inline icon. The icon is positioned by its
     wrapper, so what we change here is just the start padding. */
  padding-inline-start: calc(var(--cp-space-9) + var(--cp-space-3));
}

/* Wrapper that lays an icon over the input. Use when an icon is required;
   keeps the real <input> intact for a11y. */
.cp-input-affix {
  position: relative;
  display: block;
  inline-size: 100%;
}

.cp-input__icon {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: var(--cp-space-5);
  transform: translateY(-50%);
  inline-size: var(--cp-space-8);
  block-size: var(--cp-space-8);
  color: var(--cp-color-fg-muted);
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cp-input__icon svg {
  inline-size: 100%;
  block-size: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

/* Trailing affix (units, etc.) */
.cp-input__suffix {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: var(--cp-space-5);
  transform: translateY(-50%);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-faint);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.cp-input--with-suffix {
  padding-inline-end: calc(var(--cp-space-11) + var(--cp-space-3));
}

/* ── Textarea ──────────────────────────────────────────────────────────── */

.cp-textarea {
  display: block; /* override the inline-flex from the shared rule */
  min-block-size: calc(var(--cp-target-mouse) * 2.5);
  resize: vertical;
  line-height: var(--cp-line-height-normal);
}

/* ── Select ────────────────────────────────────────────────────────────── */

.cp-select {
  /* Chevron — SVG with stroke='currentColor' is URL-encoded so consumer
     and theme `color` flow through automatically. */
  background-image:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--cp-space-5) center;
  background-size: var(--cp-space-8) var(--cp-space-8);
  padding-inline-end: calc(var(--cp-space-9) + var(--cp-space-3));
  cursor: pointer;
}

/* Multi-select drops the chevron */
.cp-select[multiple] {
  background-image: none;
  padding-inline-end: var(--cp-space-5);
  block-size: auto;
  align-items: stretch;
  padding-block: var(--cp-space-3);
}

.cp-select option {
  background: var(--cp-color-bg-elevated);
  color: var(--cp-color-fg);
}

/* ── Checkbox ──────────────────────────────────────────────────────────── */

.cp-checkbox {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;

  /* 18×18 is the canonical control-indicator size in the spec; it sits
     between space-9 (20) and space-8 (16) and is not on the spacing
     scale by design (these are micro-controls, not layout). */
  inline-size: 18px;
  block-size: 18px;
  flex: none;

  background: var(--cp-color-bg-elevated);
  border: var(--cp-border-thin) solid var(--cp-color-border);
  border-radius: var(--cp-radius-xs);

  display: inline-grid;
  place-items: center;
  cursor: pointer;
  color: var(--cp-color-fg-on-accent);
  vertical-align: middle;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

/* Checked: accent fill + white SVG check. The check is a mask so it
   inherits currentColor (which we force to fg-on-accent). */
.cp-checkbox::before {
  content: "";
  inline-size: 12px;
  block-size: 12px;
  background: currentColor;
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' d='M3 8.5l3.2 3.2L13 5'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' d='M3 8.5l3.2 3.2L13 5'/%3E%3C/svg%3E");
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  transform: scale(0);
  transition: transform var(--cp-duration-fast) var(--cp-ease-spring);
}

.cp-checkbox:checked {
  background: var(--cp-color-accent);
  border-color: var(--cp-color-accent);
}
.cp-checkbox:checked::before { transform: scale(1); }

/* Indeterminate — used for "some children checked" */
.cp-checkbox:indeterminate {
  background: var(--cp-color-accent);
  border-color: var(--cp-color-accent);
}
.cp-checkbox:indeterminate::before {
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='7' width='10' height='2' rx='1' fill='black'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='7' width='10' height='2' rx='1' fill='black'/%3E%3C/svg%3E");
  transform: scale(1);
}

.cp-checkbox:disabled,
.cp-checkbox[aria-disabled="true"] {
  border-style: dashed;
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
}
.cp-checkbox:disabled:checked,
.cp-checkbox[aria-disabled="true"]:checked {
  background: var(--cp-color-fg-disabled);
  border-color: var(--cp-color-fg-disabled);
  border-style: solid;
}

.cp-checkbox.cp-is-error,
.cp-field.cp-is-error .cp-checkbox {
  border-color: var(--cp-color-status-danger);
}

/* Inline check-row layout: <label><input><span>…</span></label> */
.cp-check-row {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-4);
  cursor: pointer;
  font-size: var(--cp-font-size-14);
  color: var(--cp-color-fg);
  line-height: var(--cp-line-height-snug);
}
.cp-check-row:has(.cp-checkbox:disabled),
.cp-check-row:has(.cp-radio:disabled),
.cp-check-row:has(.cp-switch:disabled) {
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
}

/* ── Radio ─────────────────────────────────────────────────────────────── */

.cp-radio {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;

  inline-size: 18px;
  block-size: 18px;
  flex: none;

  background: var(--cp-color-bg-elevated);
  border: var(--cp-border-thin) solid var(--cp-color-border);
  border-radius: var(--cp-radius-pill);

  display: inline-grid;
  place-items: center;
  cursor: pointer;
  color: var(--cp-color-accent);
  vertical-align: middle;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-radio::before {
  content: "";
  inline-size: 8px;
  block-size: 8px;
  border-radius: var(--cp-radius-pill);
  background: currentColor;
  transform: scale(0);
  transition: transform var(--cp-duration-fast) var(--cp-ease-spring);
}

.cp-radio:checked {
  border-color: var(--cp-color-accent);
}
.cp-radio:checked::before { transform: scale(1); }

.cp-radio:disabled,
.cp-radio[aria-disabled="true"] {
  border-style: dashed;
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
}
.cp-radio:disabled:checked,
.cp-radio[aria-disabled="true"]:checked {
  color: var(--cp-color-fg-disabled);
  border-color: var(--cp-color-fg-disabled);
  border-style: solid;
}

.cp-radio.cp-is-error,
.cp-field.cp-is-error .cp-radio {
  border-color: var(--cp-color-status-danger);
}

/* ── Switch ────────────────────────────────────────────────────────────── */

.cp-switch {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;

  inline-size: 36px;
  block-size: 20px;
  flex: none;

  background: transparent;
  border: var(--cp-border-thin) solid var(--cp-color-border);
  border-radius: var(--cp-radius-pill);

  position: relative;
  cursor: pointer;
  vertical-align: middle;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    box-shadow var(--cp-duration-fast) var(--cp-ease-standard);
}

/* Thumb */
.cp-switch::before {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 2px;
  inline-size: 12px;
  block-size: 12px;
  background: var(--cp-color-fg);
  border-radius: var(--cp-radius-pill);
  transform: translateY(-50%);
  transition: inset-inline-start var(--cp-duration-fast) var(--cp-ease-standard),
              background var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-switch:checked {
  background: var(--cp-color-accent);
  border-color: var(--cp-color-accent);
}
.cp-switch:checked::before {
  /* Right-aligned thumb on "on" state */
  inset-inline-start: calc(100% - 12px - 2px);
  background: var(--cp-color-fg-on-accent);
}

.cp-switch:disabled,
.cp-switch[aria-disabled="true"] {
  border-style: dashed;
  cursor: not-allowed;
}
.cp-switch:disabled::before,
.cp-switch[aria-disabled="true"]::before {
  background: var(--cp-color-fg-disabled);
}
.cp-switch:disabled:checked,
.cp-switch[aria-disabled="true"]:checked {
  background: var(--cp-color-fg-disabled);
  border-color: var(--cp-color-fg-disabled);
  border-style: solid;
}
.cp-switch:disabled:checked::before,
.cp-switch[aria-disabled="true"]:checked::before {
  background: var(--cp-color-bg-elevated);
}

.cp-switch.cp-is-error,
.cp-field.cp-is-error .cp-switch {
  border-color: var(--cp-color-status-danger);
}

/* ── Fieldset ──────────────────────────────────────────────────────────── */

.cp-fieldset {
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
  padding: var(--cp-space-6);
  padding-block-start: var(--cp-space-7);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-7);
  background: transparent;
  min-inline-size: 0;
}

.cp-fieldset__legend {
  /* legend is rendered floating in the top-left */
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  padding-inline: var(--cp-space-3);
  margin-inline-start: calc(var(--cp-space-3) * -1);
  /* Sit on top of the border line */
  background: var(--cp-color-bg);
}

.cp-fieldset.cp-is-error {
  border-color: var(--cp-color-status-danger);
}

/* ── Surface variants ──────────────────────────────────────────────────── */

/* Mobile: 44px controls, friendly for gloved hands */
[data-surface="mobile"] .cp-input,
[data-surface="mobile"] .cp-textarea,
[data-surface="mobile"] .cp-select,
.cp-input[data-surface="mobile"],
.cp-textarea[data-surface="mobile"],
.cp-select[data-surface="mobile"] {
  min-block-size: var(--cp-target-touch);
  font-size: var(--cp-font-size-16);
  padding-block: var(--cp-space-5);
  padding-inline: var(--cp-space-6);
}

[data-surface="mobile"] .cp-checkbox,
.cp-checkbox[data-surface="mobile"],
[data-surface="mobile"] .cp-radio,
.cp-radio[data-surface="mobile"] {
  inline-size: 22px;
  block-size: 22px;
}
[data-surface="mobile"] .cp-checkbox::before,
.cp-checkbox[data-surface="mobile"]::before {
  inline-size: 14px;
  block-size: 14px;
}
[data-surface="mobile"] .cp-radio::before,
.cp-radio[data-surface="mobile"]::before {
  inline-size: 10px;
  block-size: 10px;
}

[data-surface="mobile"] .cp-switch,
.cp-switch[data-surface="mobile"] {
  inline-size: 44px;
  block-size: 24px;
}
[data-surface="mobile"] .cp-switch::before,
.cp-switch[data-surface="mobile"]::before {
  inline-size: 16px;
  block-size: 16px;
}
[data-surface="mobile"] .cp-switch:checked::before,
.cp-switch[data-surface="mobile"]:checked::before {
  inset-inline-start: calc(100% - 16px - 2px);
}

/* Embedded: mono face, hairline borders, tighter padding. Targets OLED
   panels where physical buttons drive the UI so touch sizing doesn't
   apply. */
[data-surface="embedded"] .cp-input,
[data-surface="embedded"] .cp-textarea,
[data-surface="embedded"] .cp-select,
.cp-input[data-surface="embedded"],
.cp-textarea[data-surface="embedded"],
.cp-select[data-surface="embedded"] {
  font-family: var(--cp-font-family-mono);
  font-variant-numeric: tabular-nums;
  border-width: var(--cp-border-hairline);
  padding-block: var(--cp-space-2);
  padding-inline: var(--cp-space-4);
  min-block-size: var(--cp-target-compact);
  font-size: var(--cp-font-size-12);
  border-radius: var(--cp-radius-0);
}

[data-surface="embedded"] .cp-field__label,
.cp-field[data-surface="embedded"] .cp-field__label {
  font-size: var(--cp-font-size-10);
}

[data-surface="embedded"] .cp-checkbox,
.cp-checkbox[data-surface="embedded"],
[data-surface="embedded"] .cp-radio,
.cp-radio[data-surface="embedded"] {
  inline-size: 14px;
  block-size: 14px;
  border-width: var(--cp-border-hairline);
  border-radius: var(--cp-radius-0);
}
[data-surface="embedded"] .cp-radio,
.cp-radio[data-surface="embedded"] {
  border-radius: var(--cp-radius-pill);
}
[data-surface="embedded"] .cp-checkbox::before,
.cp-checkbox[data-surface="embedded"]::before {
  inline-size: 10px;
  block-size: 10px;
}
[data-surface="embedded"] .cp-radio::before,
.cp-radio[data-surface="embedded"]::before {
  inline-size: 6px;
  block-size: 6px;
}

[data-surface="embedded"] .cp-switch,
.cp-switch[data-surface="embedded"] {
  inline-size: 28px;
  block-size: 14px;
  border-width: var(--cp-border-hairline);
}
[data-surface="embedded"] .cp-switch::before,
.cp-switch[data-surface="embedded"]::before {
  inline-size: 8px;
  block-size: 8px;
}
[data-surface="embedded"] .cp-switch:checked::before,
.cp-switch[data-surface="embedded"]:checked::before {
  inset-inline-start: calc(100% - 8px - 2px);
}

[data-surface="embedded"] .cp-fieldset,
.cp-fieldset[data-surface="embedded"] {
  border-width: var(--cp-border-hairline);
  border-radius: var(--cp-radius-0);
  padding: var(--cp-space-5);
}

/* ── Forced-state hooks (for the docs page so we can show every state at
   once without spawning seventeen focused tabs). Wrap with care — these
   bypass real interaction state. */

.cp-input.cp-is-focus,
.cp-textarea.cp-is-focus,
.cp-select.cp-is-focus {
  border-color: var(--cp-color-border-focus);
  box-shadow: var(--cp-shadow-focus);
}

.cp-input.cp-is-hover:not(:disabled),
.cp-textarea.cp-is-hover:not(:disabled),
.cp-select.cp-is-hover:not(:disabled) {
  border-color: var(--cp-color-fg);
}
.cp-input:hover:not(:disabled):not(:focus):not(.cp-is-error):not(.cp-is-success),
.cp-textarea:hover:not(:disabled):not(:focus):not(.cp-is-error):not(.cp-is-success),
.cp-select:hover:not(:disabled):not(:focus):not(.cp-is-error):not(.cp-is-success) {
  /* default border is already fg; nudge accent on hover for affordance */
  border-color: var(--cp-color-accent);
}

/* ── end src/styles/components/form.css ── */
/* ── inlined from src/styles/components/gauge.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-gauge, .cp-bar-gauge, .cp-meter — telemetry vital-sign widgets.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-gauge` is the circular radial gauge. It positions and themes
     an INLINE SVG drawn per-instance (the SVG markup is page-side per
     SPEC §11 — direct presentation attributes, no class-based fills).
   - `.cp-bar-gauge` is the horizontal bar. `__fill`, `__segment`,
     `__threshold` are the parts.
   - `.cp-meter` is the composite (label row + bar) used everywhere
     in the wireframes.

   Status colors:
   - `--success` / `--warning` / `--danger` / `--info` / `--accent`
     drive a single component-local custom property
     `--_cp-gauge-color` (or `--_cp-bar-color`) that the inline SVG
     stroke inherits via `currentColor`.

   Thresholds convention (document only — no JS in this file):
   - <  20%  → danger
   - <  40%  → warning
   - >= 40%  → success / neutral
   Consumers set `data-value` and toggle the modifier class accordingly.

   Animation:
   - The ring "fills" by transitioning the SVG fill circle's
     `stroke-dashoffset` over `--cp-duration-slow`. Bars transition
     `inline-size` of the `__fill` element. Both respect
     `prefers-reduced-motion` via the token-level duration override.
   ───────────────────────────────────────────────────────────────────────── */


/* ── Circular gauge ───────────────────────────────────────────────────── */

.cp-gauge {
  /* Component-local custom properties so size/status modifiers can override
     them without rewriting layout rules. Consumers may also set
     `--cp-gauge-size` inline to override the default 120px target. */
  --cp-gauge-size: 120px;
  --_cp-gauge-color: var(--cp-color-fg);
  --_cp-gauge-track: color-mix(in srgb, currentColor 15%, transparent);
  --_cp-gauge-value-size: calc(var(--cp-gauge-size) / 4);
  --_cp-gauge-label-size: var(--cp-font-size-10);
  --_cp-gauge-unit-size: var(--cp-font-size-11);

  position: relative;
  display: inline-block;
  inline-size: var(--cp-gauge-size);
  block-size: var(--cp-gauge-size);
  color: var(--_cp-gauge-color);
  vertical-align: middle;
}

/* The SVG ring — drawn inline in markup, positioned here. */
.cp-gauge__ring {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
}

/* The "track" circle (full ring at low opacity) — selected by the
   `[data-cp-gauge-part="track"]` attribute on the inline <circle>.
   Page-side authors can either use this attribute OR write the
   stroke directly; the styles set safe defaults either way. */
.cp-gauge__ring [data-cp-gauge-part="track"] {
  stroke: var(--_cp-gauge-track);
  fill: none;
}

/* The "fill" circle — animates dashoffset on load. */
.cp-gauge__ring [data-cp-gauge-part="fill"] {
  stroke: currentColor;
  fill: none;
  transition: stroke-dashoffset var(--cp-duration-slow) var(--cp-ease-out);
}

/* Centered content (value + label + unit) */
.cp-gauge__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cp-space-2);
  text-align: center;
  pointer-events: none;
}

.cp-gauge__value {
  font-family: var(--cp-font-family-display);
  font-size: var(--_cp-gauge-value-size);
  font-weight: var(--cp-font-weight-bold);
  line-height: 1;
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
}

.cp-gauge__unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-gauge-unit-size);
  color: var(--cp-color-fg-muted);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.cp-gauge__label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-gauge-label-size);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
  line-height: 1;
}

/* ── Gauge sizes ──────────────────────────────────────────────────────── */

.cp-gauge--xs {
  --cp-gauge-size: 56px;
  --_cp-gauge-value-size: var(--cp-font-size-14);
  --_cp-gauge-label-size: 8px; /* below the type scale's floor; sparkline-only */
  --_cp-gauge-unit-size: 9px;
}

.cp-gauge--sm {
  --cp-gauge-size: 80px;
  --_cp-gauge-value-size: var(--cp-font-size-18);
  --_cp-gauge-label-size: 9px;
}

.cp-gauge--lg {
  --cp-gauge-size: 160px;
  --_cp-gauge-value-size: var(--cp-font-size-40);
  --_cp-gauge-label-size: var(--cp-font-size-11);
  --_cp-gauge-unit-size: var(--cp-font-size-12);
}

/* ── Gauge status colors ──────────────────────────────────────────────── */

.cp-gauge--accent  { --_cp-gauge-color: var(--cp-color-accent); }
.cp-gauge--success { --_cp-gauge-color: var(--cp-color-status-success); }
.cp-gauge--warning { --_cp-gauge-color: var(--cp-color-status-warning); }
.cp-gauge--danger  { --_cp-gauge-color: var(--cp-color-status-danger); }
.cp-gauge--info    { --_cp-gauge-color: var(--cp-color-status-info); }

/* ── Arc variant — semicircle / speedometer ───────────────────────────────
   The arc geometry lives in the inline SVG (different stroke-dasharray
   + rotation). All this rule needs to do is collapse the block-size
   so the widget doesn't reserve the empty bottom half. */
.cp-gauge--arc {
  block-size: calc(var(--cp-gauge-size) / 1.6);
  overflow: hidden;
}
.cp-gauge--arc .cp-gauge__center {
  /* Pull the readout up to the center of the visible arc. */
  inset-block-end: auto;
  block-size: var(--cp-gauge-size);
  padding-block-end: calc(var(--cp-gauge-size) / 8);
  justify-content: flex-end;
}


/* ── Bar gauge ────────────────────────────────────────────────────────── */

.cp-bar-gauge {
  --_cp-bar-block-size: 12px;
  --_cp-bar-color: var(--cp-color-fg);
  --_cp-bar-track: var(--cp-color-bg-inset);
  --_cp-bar-radius: var(--cp-radius-pill);
  --_cp-bar-value: 0%;

  position: relative;
  display: block;
  inline-size: 100%;
  block-size: var(--_cp-bar-block-size);
  background: var(--_cp-bar-track);
  border-radius: var(--_cp-bar-radius);
  overflow: hidden;
  color: var(--_cp-bar-color);
}

.cp-bar-gauge__fill {
  display: block;
  block-size: 100%;
  inline-size: var(--_cp-bar-value);
  background: currentColor;
  border-radius: inherit;
  transition: inline-size var(--cp-duration-slow) var(--cp-ease-out);
}

/* Segmented — cells side-by-side, gapped. The track shows through gaps. */
.cp-bar-gauge--segmented {
  display: flex;
  gap: var(--cp-space-2);
  background: transparent;
  overflow: visible;
  border-radius: 0;
}
.cp-bar-gauge__segment {
  flex: 1 1 0;
  block-size: 100%;
  background: var(--_cp-bar-track);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-xs);
  transition: background var(--cp-duration-normal) var(--cp-ease-standard);
}
.cp-bar-gauge__segment.cp-is-active,
.cp-bar-gauge__segment[aria-current="true"] {
  background: currentColor;
  border-color: currentColor;
}

/* Striped — hatched fill, evokes the wireframe pencil texture. */
.cp-bar-gauge--striped .cp-bar-gauge__fill {
  background-image: repeating-linear-gradient(
    -45deg,
    currentColor 0,
    currentColor 1.5px,
    transparent 1.5px,
    transparent 5px
  );
  background-color: color-mix(in srgb, currentColor 10%, transparent);
}

/* Inverted — fills from end → start (fuel remaining, negative values) */
.cp-bar-gauge--inverted {
  direction: rtl;
}
.cp-bar-gauge--inverted > .cp-bar-gauge__fill,
.cp-bar-gauge--inverted > .cp-bar-gauge__threshold {
  direction: ltr;
}

/* Threshold marker — vertical hairline at `--cp-bar-gauge-threshold` (a percentage) */
.cp-bar-gauge__threshold {
  --cp-bar-gauge-threshold: 80%;
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--cp-bar-gauge-threshold);
  inline-size: var(--cp-border-thin);
  background: var(--cp-color-fg);
  opacity: 0.6;
  pointer-events: none;
}
.cp-bar-gauge__threshold--warning { background: var(--cp-color-status-warning); }
.cp-bar-gauge__threshold--danger  { background: var(--cp-color-status-danger); }
.cp-bar-gauge__threshold--success { background: var(--cp-color-status-success); }

/* ── Bar sizes ────────────────────────────────────────────────────────── */

.cp-bar-gauge--xs { --_cp-bar-block-size: 4px; }
.cp-bar-gauge--sm { --_cp-bar-block-size: 8px; }
.cp-bar-gauge--lg { --_cp-bar-block-size: 16px; }

/* ── Bar status colors ────────────────────────────────────────────────── */

.cp-bar-gauge--accent  { --_cp-bar-color: var(--cp-color-accent); }
.cp-bar-gauge--success { --_cp-bar-color: var(--cp-color-status-success); }
.cp-bar-gauge--warning { --_cp-bar-color: var(--cp-color-status-warning); }
.cp-bar-gauge--danger  { --_cp-bar-color: var(--cp-color-status-danger); }
.cp-bar-gauge--info    { --_cp-bar-color: var(--cp-color-status-info); }


/* ── .cp-meter — composite label-row + bar ────────────────────────────── */

.cp-meter {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--cp-space-3) var(--cp-space-6);
  align-items: baseline;
  font-family: var(--cp-font-family-body);
}

.cp-meter__label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  line-height: 1.2;
}

.cp-meter__value {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-medium);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
  text-align: end;
  line-height: 1.2;
}

.cp-meter__bar {
  grid-column: 1 / -1;
}

.cp-meter__caption {
  grid-column: 1 / -1;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  color: var(--cp-color-fg-faint);
  line-height: 1.2;
  margin-block-start: calc(-1 * var(--cp-space-1)); /* optical: tighten to bar */
}


/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-gauge[data-surface="mobile"] {
  --cp-gauge-size: 144px;
  --_cp-gauge-value-size: var(--cp-font-size-32);
}
.cp-gauge[data-surface="mobile"].cp-gauge--sm  { --cp-gauge-size: 96px; }
.cp-gauge[data-surface="mobile"].cp-gauge--lg  { --cp-gauge-size: 192px; }
.cp-gauge[data-surface="mobile"].cp-gauge--xs  { --cp-gauge-size: 64px; }

.cp-gauge[data-surface="embedded"] {
  --cp-gauge-size: 72px;
  --_cp-gauge-value-size: var(--cp-font-size-14);
  --_cp-gauge-label-size: 8px;
  --_cp-gauge-unit-size: 9px;
  font-family: var(--cp-font-family-mono);
}
.cp-gauge[data-surface="embedded"] .cp-gauge__value {
  font-family: var(--cp-font-family-mono);
  font-weight: var(--cp-font-weight-semibold);
  letter-spacing: var(--cp-tracking-wide);
}

.cp-bar-gauge[data-surface="mobile"] { --_cp-bar-block-size: 16px; }
.cp-bar-gauge[data-surface="mobile"].cp-bar-gauge--sm { --_cp-bar-block-size: 10px; }
.cp-bar-gauge[data-surface="mobile"].cp-bar-gauge--xs { --_cp-bar-block-size: 6px; }
.cp-bar-gauge[data-surface="mobile"].cp-bar-gauge--lg { --_cp-bar-block-size: 22px; }

.cp-bar-gauge[data-surface="embedded"] {
  --_cp-bar-block-size: 6px;
  --_cp-bar-radius: var(--cp-radius-xs);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}
.cp-bar-gauge[data-surface="embedded"] .cp-bar-gauge__fill {
  border-radius: 0;
}


/* ── Reduced motion — token durations already collapse, but suppress the
   ring's initial dashoffset transition too so it doesn't flash. */
@media (prefers-reduced-motion: reduce) {
  .cp-gauge__ring [data-cp-gauge-part="fill"],
  .cp-bar-gauge__fill {
    transition: none;
  }
}

/* ── end src/styles/components/gauge.css ── */
/* ── inlined from src/styles/components/hero.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-hero / .cp-page-head / .cp-section-head — the headline surfaces.

   Three sibling headers, one file, because they are the same idea
   expressed at three scales: a marketing/landing hero, an in-product page
   header, and a minor section divider. They share an aesthetic vocabulary
   (mono eyebrow, display-face title, muted Saira subtitle) so authors can
   move between them without re-learning the API.

   Architecture mirrors button.css / card.css:
   - Component-local custom properties on the root.
   - BEM elements (`__eyebrow`, `__title`, `__subtitle`, `__actions`, …).
   - Modifier classes for variants (`--split`, `--centered`, `--accent`, …).

   Note on the `.cp-hero` typography rule in base/typography.css: that
   declaration sets the font-family/size/letter-spacing/line-height as if
   `.cp-hero` were applied directly to a heading element. We now use it
   as a container class, so we restore body defaults on the container and
   let `.cp-hero__title` take ownership of the display face.
   ───────────────────────────────────────────────────────────────────────── */

.cp-hero {
  /* Component-local custom properties — modifiers override these without
     rewriting the layout rules below. */
  --_cp-hero-pad-block: var(--cp-space-13);  /* 48px */
  --_cp-hero-pad-inline: var(--cp-space-10); /* 24px */
  --_cp-hero-gap: var(--cp-space-7);         /* 14px between parts */
  --_cp-hero-bg: var(--cp-color-bg);
  --_cp-hero-fg: var(--cp-color-fg);
  --_cp-hero-text-max: 64ch;
  /* 320px is the spec-mandated minimum hero height — taller than any
     token in the space scale, kept literal so the contract is obvious
     at the call-site. Tokens stop at space-16 (96px). */
  --_cp-hero-min-block: 320px;
  --_cp-hero-accent-bar: 0;
  /* Title scale — clamp from 40px to 56px between 20rem and 80rem
     viewports. Tokens cover the endpoints; the in-between is fluid. */
  --_cp-hero-title-size: clamp(
    var(--cp-font-size-40),
    5vw,
    var(--cp-font-size-56)
  );

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--_cp-hero-gap);

  min-block-size: var(--_cp-hero-min-block);
  padding: var(--_cp-hero-pad-block) var(--_cp-hero-pad-inline);

  /* Reset the typography.css `.cp-hero` rule (which existed before this
     became a container class). The container itself is body-styled; the
     title element claims the display face. */
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-16);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-normal);
  line-height: var(--cp-line-height-normal);

  background: var(--_cp-hero-bg);
  color: var(--_cp-hero-fg);
  border-radius: 0; /* heroes are full-bleed by default */

  isolation: isolate; /* so ::before rails sit above bg, below content */
}

/* All immediate text children share the same text-column width so a long
   subtitle wraps in lockstep with the title. */
.cp-hero > .cp-hero__eyebrow,
.cp-hero > .cp-hero__title,
.cp-hero > .cp-hero__subtitle,
.cp-hero > .cp-hero__actions {
  max-inline-size: var(--_cp-hero-text-max);
}

/* ── Elements ─────────────────────────────────────────────────────────── */

.cp-hero__eyebrow {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  line-height: var(--cp-line-height-snug);
  /* Pull the title closer than the default container gap. */
  margin-block-end: calc(var(--cp-space-2) - var(--_cp-hero-gap));
}

.cp-hero__title {
  font-family: var(--cp-font-family-display);
  font-size: var(--_cp-hero-title-size);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-tight);
  color: var(--cp-color-fg);
  margin: 0;
  overflow-wrap: anywhere;
}

.cp-hero__subtitle {
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-18);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-normal);
  color: var(--cp-color-fg-muted);
  margin: 0;
}

.cp-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cp-space-5);
  margin-block-start: var(--cp-space-3);
}

/* Media slot — sized to balance the text column in --split, or sit
   below it in the default vertical flow. */
.cp-hero__media {
  display: flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 0;
  color: var(--cp-color-fg-muted);
}
.cp-hero__media > img,
.cp-hero__media > svg {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
}

/* Accent rail — left-edge orange bar, same idiom as .cp-card--accent.
   Driven by --_cp-hero-accent-bar so only variants that set it render. */
.cp-hero::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: var(--_cp-hero-accent-bar);
  background: var(--cp-color-accent);
  pointer-events: none;
  z-index: -1;
}

/* ── Variants ─────────────────────────────────────────────────────────── */

/* Split — text column on the leading edge, media on the trailing edge.
   Both columns can shrink. Stacks vertically below 720px so the media
   doesn't crush the text. */
.cp-hero--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--cp-space-12);
}
.cp-hero--split > .cp-hero__eyebrow,
.cp-hero--split > .cp-hero__title,
.cp-hero--split > .cp-hero__subtitle,
.cp-hero--split > .cp-hero__actions {
  grid-column: 1;
}
.cp-hero--split > .cp-hero__media {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: stretch;
  justify-self: stretch;
}

/* Centered — marketing pitch. Title block centers in the surface; actions
   row centers horizontally too. */
.cp-hero--centered {
  align-items: center;
  text-align: center;
}
.cp-hero--centered > .cp-hero__eyebrow,
.cp-hero--centered > .cp-hero__title,
.cp-hero--centered > .cp-hero__subtitle {
  margin-inline: auto;
}
.cp-hero--centered > .cp-hero__actions {
  justify-content: center;
  margin-inline: auto;
}

/* Compact — used at the top of feature pages. Half the vertical padding,
   no required min-height, smaller title clamp. */
.cp-hero--compact {
  --_cp-hero-pad-block: var(--cp-space-11);  /* 32px */
  --_cp-hero-min-block: auto;
  --_cp-hero-title-size: clamp(
    var(--cp-font-size-32),
    3.5vw,
    var(--cp-font-size-40)
  );
  --_cp-hero-gap: var(--cp-space-6);
}

/* Accent — subtle accent wash + left rail. Same color as .cp-card--accent
   so consumers see a consistent "this is the featured surface" cue. */
.cp-hero--accent {
  --_cp-hero-accent-bar: var(--cp-space-2);
  --_cp-hero-bg: color-mix(
    in srgb,
    var(--cp-color-accent) 6%,
    var(--cp-color-bg)
  );
  padding-inline-start: calc(
    var(--_cp-hero-pad-inline) + var(--_cp-hero-accent-bar)
  );
}

/* Dark — forces dark tokens onto this single block regardless of host
   theme. Useful for inverted product showcases on otherwise-light pages.
   We declare the dark theme tokens directly on the container so children
   inherit them via `var(--cp-color-fg)` etc. without a [data-theme] flip
   on the document root. */
.cp-hero--dark {
  --cp-color-bg: var(--cp-color-void-50);
  --cp-color-bg-elevated: var(--cp-color-void-100);
  --cp-color-bg-inset: var(--cp-color-void-0);
  --cp-color-fg: var(--cp-color-chalk-0);
  --cp-color-fg-muted: var(--cp-color-chalk-100);
  --cp-color-fg-faint: var(--cp-color-chalk-200);
  --cp-color-fg-disabled: var(--cp-color-chalk-300);
  --cp-color-border: var(--cp-color-chalk-0);
  --cp-color-border-subtle: var(--cp-color-chalk-400);
  color-scheme: dark;
  --_cp-hero-bg: var(--cp-color-bg);
  --_cp-hero-fg: var(--cp-color-fg);
}
.cp-hero--dark.cp-hero--accent {
  --_cp-hero-bg: color-mix(
    in srgb,
    var(--cp-color-accent) 10%,
    var(--cp-color-void-50)
  );
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-page-head — internal page header, between top nav and content.

   Two-column layout: titles on the leading edge, meta + actions on the
   trailing edge. A bottom hairline separates the head from page body.
   Collapses to a single column below 720px so the meta stack doesn't
   compete with the title for a too-narrow leading column.
   ───────────────────────────────────────────────────────────────────────── */

.cp-page-head {
  --_cp-page-head-pad-block: var(--cp-space-9) var(--cp-space-7);
  --_cp-page-head-gap: var(--cp-space-8);
  --_cp-page-head-margin-end: var(--cp-space-9);

  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--_cp-page-head-gap);

  padding-block: var(--_cp-page-head-pad-block);
  margin-block-end: var(--_cp-page-head-margin-end);
  border-block-end: var(--cp-border-hairline) solid
                    var(--cp-color-border-subtle);

  font-family: var(--cp-font-family-body);
  color: var(--cp-color-fg);
}

.cp-page-head__lead {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--cp-space-3);
  min-inline-size: 0;
  flex: 1 1 auto;
}

.cp-page-head__breadcrumbs {
  /* Cosmetic wrapper — `.cp-breadcrumbs` carries its own typography.
     We only adjust the margin so the title pulls in closer. */
  margin-block-end: var(--cp-space-2);
}

.cp-page-head__title {
  font-family: var(--cp-font-family-display);
  font-size: clamp(
    var(--cp-font-size-24),
    3vw,
    var(--cp-font-size-40)
  );
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-tight);
  color: var(--cp-color-fg);
  margin: 0;
  overflow-wrap: anywhere;
}

.cp-page-head__subtitle {
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  margin: 0;
}

.cp-page-head__trail {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--cp-space-5);
  flex: 0 0 auto;
}

.cp-page-head__meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--cp-space-5);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-fg-muted);
  font-variant-numeric: tabular-nums;
}

.cp-page-head__actions {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--cp-space-4);
}

/* Tabs row — optional, sits at the bottom of the head, flush with the
   hairline border-block-end. We replace that border because the tabs row
   carries its own visible boundary. */
.cp-page-head:has(.cp-page-head__tabs) {
  border-block-end: 0;
  padding-block-end: 0;
}
.cp-page-head__tabs {
  display: flex;
  align-items: stretch;
  gap: var(--cp-space-3);
  /* Tabs span the full width of the head, not just the trailing column.
     We pull them out of the flex row and put them below by giving them
     100% width and order: 99. */
  inline-size: 100%;
  order: 99;
  margin-block-start: var(--cp-space-8);
  border-block-end: var(--cp-border-hairline) solid
                    var(--cp-color-border-subtle);
  overflow-x: auto;
}
.cp-page-head__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  padding: var(--cp-space-4) var(--cp-space-7);
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  background: transparent;
  border: 0;
  border-block-end: var(--cp-border-thick) solid transparent;
  /* Optical: shift the bottom border down 1px so it sits flush with the
     tabs strip hairline rather than above it. */
  margin-block-end: -1px;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-page-head__tab:hover,
.cp-page-head__tab.cp-is-hover {
  color: var(--cp-color-fg);
}
.cp-page-head__tab[aria-current="page"],
.cp-page-head__tab[aria-selected="true"],
.cp-page-head__tab.cp-is-current {
  color: var(--cp-color-fg);
  border-block-end-color: var(--cp-color-accent);
}
.cp-page-head__tab:focus-visible {
  outline: 0;
  /* Inherit ring tone from base/focus.css via box-shadow */
  border-radius: var(--cp-radius-xs);
  box-shadow: var(--cp-shadow-focus);
}

/* Responsive — collapse the two-column rows to a single stacked column
   below 720px so the meta block doesn't crush the title and the split
   hero's media doesn't squeeze the text. The breakpoint is a literal
   px value because media queries don't yet honour CSS custom
   properties; mirrors the responsive cutoffs in form.css. */
@media (max-width: 720px) {
  .cp-page-head {
    flex-direction: column;
    align-items: flex-start;
  }
  .cp-page-head__trail {
    align-items: flex-start;
    inline-size: 100%;
  }
  .cp-page-head__meta,
  .cp-page-head__actions {
    justify-content: flex-start;
  }
  .cp-hero--split {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--cp-space-9);
  }
  .cp-hero--split > .cp-hero__media {
    grid-column: 1;
    grid-row: auto;
  }
  .cp-section-head {
    flex-wrap: wrap;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-section-head — minor section header.

   Title + optional subtitle on the leading edge, optional action (ghost
   link, "View all →") on the trailing edge. Baseline-aligned so the
   title's baseline sits with the subtitle and the action text.
   ───────────────────────────────────────────────────────────────────────── */

.cp-section-head {
  display: flex;
  align-items: baseline;
  gap: var(--cp-space-6);
  margin-block-end: var(--cp-space-7);
}

.cp-section-head__lead {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--cp-space-5);
  min-inline-size: 0;
  flex: 1 1 auto;
}

.cp-section-head__title {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-24);
  font-weight: var(--cp-font-weight-semibold);
  letter-spacing: var(--cp-tracking-tight);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg);
  margin: 0;
}

.cp-section-head__subtitle {
  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-12);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  margin: 0;
}

.cp-section-head__action {
  margin-inline-start: auto;
  flex: 0 0 auto;
}

/* Surface variants — keep them lightweight: mono titles on embedded,
   touch-friendly tabs on mobile. */
.cp-hero[data-surface="mobile"] {
  --_cp-hero-pad-block: var(--cp-space-11);
  --_cp-hero-pad-inline: var(--cp-space-9);
  /* Match the 320px desktop minimum scaled to a phone aspect ratio.
     No token covers this; kept literal alongside the 320px default. */
  --_cp-hero-min-block: 240px;
  --_cp-hero-title-size: clamp(
    var(--cp-font-size-24),
    7vw,
    var(--cp-font-size-32)
  );
}

.cp-hero[data-surface="embedded"] {
  --_cp-hero-pad-block: var(--cp-space-7);
  --_cp-hero-pad-inline: var(--cp-space-5);
  --_cp-hero-min-block: auto;
  --_cp-hero-gap: var(--cp-space-3);
  font-family: var(--cp-font-family-mono);
}
.cp-hero[data-surface="embedded"] .cp-hero__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-16);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}
.cp-hero[data-surface="embedded"] .cp-hero__subtitle {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
}

.cp-page-head[data-surface="mobile"] .cp-page-head__title {
  font-size: var(--cp-font-size-24);
}
.cp-page-head[data-surface="embedded"] {
  --_cp-page-head-pad-block: var(--cp-space-5) var(--cp-space-4);
  --_cp-page-head-margin-end: var(--cp-space-5);
  font-family: var(--cp-font-family-mono);
}
.cp-page-head[data-surface="embedded"] .cp-page-head__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}

.cp-section-head[data-surface="embedded"] .cp-section-head__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}

/* ── end src/styles/components/hero.css ── */
/* ── inlined from src/styles/components/icon.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   CubePilot UI — Iconography

   Wrapper utilities for the 47-icon domain set in src/assets/icons/.
   The SVGs themselves are 24×24, fill="none", stroke="currentColor",
   stroke-width=1.5. The classes here only size the box and steer color;
   the SVG provides the geometry.

   Two consumption patterns:
     1. Inline SVG  (preferred — inherits color via currentColor)
        <svg class="cp-icon cp-icon--lg" aria-hidden="true">…</svg>

     2. <img src="…">  (no currentColor inheritance — color modifiers are
        no-ops; use this only when inlining is impractical, e.g. CMS)
        <img class="cp-icon cp-icon--lg" src="…/gps.svg" alt="">

   Decorative icons MUST set aria-hidden="true". Meaningful icons should
   carry an accessible name via <title>, aria-label, or sr-only text.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Base wrapper ────────────────────────────────────────────────────────── */

.cp-icon {
  /* Box: a square that follows surrounding text size by default (16px). */
  --cp-icon-size: var(--cp-font-size-16);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;                          /* never compress inside flex rows */
  inline-size: var(--cp-icon-size);
  block-size: var(--cp-icon-size);

  /* Color: SVGs draw with currentColor; consumer steers via `color`. */
  color: currentColor;
  fill: none;
  stroke: currentColor;

  /* Optical: descend the baseline a touch so the icon sits with x-height,
     not the baseline. Helps with inline-with-text use. */
  vertical-align: -0.125em;            /* -2px at 16px; ICOMOON convention */

  /* Make sure raw <svg> children always fill the wrapper precisely. */
  line-height: 0;
  user-select: none;
}

.cp-icon > svg,
.cp-icon svg {
  inline-size: 100%;
  block-size: 100%;
  display: block;
}

/* When used as <img>, the reset rule already gives `display: block`; we
   keep the wrapper's inline-flex layout intact and let the image fill it. */
img.cp-icon {
  /* The src is a raster of paths drawn with currentColor — but <img>
     sandboxes the SVG, so color cannot inherit. Document via the
     muted/accent modifiers being no-ops on <img>. */
  object-fit: contain;
}

/* ── Size scale ─────────────────────────────────────────────────────────── */

.cp-icon--xs { --cp-icon-size: var(--cp-font-size-12); }   /* 12px · body-sm leading */
.cp-icon--sm { --cp-icon-size: var(--cp-font-size-14); }   /* 14px · body-sm */
/* (default)                                                  16px · body    */
.cp-icon--md { --cp-icon-size: var(--cp-font-size-20); }   /* 20px · h4      */
.cp-icon--lg { --cp-icon-size: var(--cp-font-size-24); }   /* 24px · h3      */
.cp-icon--xl { --cp-icon-size: var(--cp-font-size-32); }   /* 32px · h2      */

/* ── Color modifiers ─────────────────────────────────────────────────────── */

.cp-icon--muted   { color: var(--cp-color-fg-muted); }
.cp-icon--faint   { color: var(--cp-color-fg-faint); }
.cp-icon--accent  { color: var(--cp-color-accent); }
.cp-icon--success { color: var(--cp-color-status-success); }
.cp-icon--warning { color: var(--cp-color-status-warning); }
.cp-icon--danger  { color: var(--cp-color-status-danger); }
.cp-icon--info    { color: var(--cp-color-status-info); }

/* Spin animation for "calibrating", "loading", "syncing" surfaces.
   Honours prefers-reduced-motion via the token (set to 0 globally). */
@keyframes cp-icon-spin {
  to { transform: rotate(360deg); }
}
.cp-icon--spin > svg,
.cp-icon--spin svg {
  animation: cp-icon-spin var(--cp-duration-slow) linear infinite;
  /* The slow duration token is 320ms — too fast for a perpetual spin —
     so we deliberately re-scale here (one rev per ~1.6s). The motion
     token is still the source for the reduced-motion override. */
  animation-duration: calc(var(--cp-duration-slow) * 5);
}

@media (prefers-reduced-motion: reduce) {
  .cp-icon--spin > svg,
  .cp-icon--spin svg { animation: none; }
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-icon-badge — a circle / square frame around an icon.
   Used in status pills, list rows, and dashboard tiles. The frame inherits
   color from the parent via currentColor for the border; fills are token
   driven so the icon stays legible.
   ───────────────────────────────────────────────────────────────────────── */

.cp-icon-badge {
  --cp-icon-badge-size: var(--cp-space-11);            /* 32px default */
  --cp-icon-badge-icon-ratio: 0.55;                    /* glyph fills ~55% */
  --cp-icon-badge-border: var(--cp-border-hairline);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;

  inline-size: var(--cp-icon-badge-size);
  block-size: var(--cp-icon-badge-size);
  border: var(--cp-icon-badge-border) solid currentColor;
  border-radius: var(--cp-radius-sm);                  /* default: square */
  background: transparent;
  color: var(--cp-color-fg);
  transition: background var(--cp-duration-fast) var(--cp-ease-out),
              color var(--cp-duration-fast) var(--cp-ease-out);
}

.cp-icon-badge > .cp-icon,
.cp-icon-badge > svg {
  --cp-icon-size: calc(var(--cp-icon-badge-size) * var(--cp-icon-badge-icon-ratio));
  inline-size: var(--cp-icon-size);
  block-size: var(--cp-icon-size);
  color: inherit;
}

/* Shape modifiers */
.cp-icon-badge--circle  { border-radius: var(--cp-radius-pill); }
.cp-icon-badge--square  { border-radius: var(--cp-radius-sm); }
.cp-icon-badge--soft    { border-radius: var(--cp-radius-md); }

/* Sizes — track .cp-icon scale so consumers can match wrapper-to-badge. */
.cp-icon-badge--xs { --cp-icon-badge-size: var(--cp-space-9); }    /* 20px */
.cp-icon-badge--sm { --cp-icon-badge-size: var(--cp-space-10); }   /* 24px */
/* (default)                                                          32px */
.cp-icon-badge--md { --cp-icon-badge-size: var(--cp-space-12); }   /* 40px */
.cp-icon-badge--lg { --cp-icon-badge-size: var(--cp-space-13); }   /* 48px */
.cp-icon-badge--xl { --cp-icon-badge-size: var(--cp-space-14); }   /* 64px */

/* Filled variant — inverts to a solid disc with the icon on bg. */
.cp-icon-badge--filled {
  background: currentColor;
  border-color: transparent;
}
.cp-icon-badge--filled > .cp-icon,
.cp-icon-badge--filled > svg {
  /* Invert glyph to the bg colour. Works for any tint because the
     wrapper's currentColor is the fill, and we manually flip the
     child to the elevated surface. */
  color: var(--cp-color-bg-elevated);
}

/* Soft tinted variant — translucent backdrop in the icon colour, with the
   stroke remaining the full colour. Uses color-mix so it tracks the theme
   automatically. */
.cp-icon-badge--tint {
  background: color-mix(in srgb, currentColor 14%, transparent);
  border-color: color-mix(in srgb, currentColor 28%, transparent);
}

/* Status colorings cascade to both badge stroke + icon. */
.cp-icon-badge--muted   { color: var(--cp-color-fg-muted); }
.cp-icon-badge--accent  { color: var(--cp-color-accent); }
.cp-icon-badge--success { color: var(--cp-color-status-success); }
.cp-icon-badge--warning { color: var(--cp-color-status-warning); }
.cp-icon-badge--danger  { color: var(--cp-color-status-danger); }
.cp-icon-badge--info    { color: var(--cp-color-status-info); }

/* ─────────────────────────────────────────────────────────────────────────
   .cp-dot — solid circular status indicator.
   Used standalone or prefixed onto a list item / status pill.
   ───────────────────────────────────────────────────────────────────────── */

.cp-dot {
  --cp-dot-size: var(--cp-space-3);                    /* 6px default */
  display: inline-block;
  inline-size: var(--cp-dot-size);
  block-size: var(--cp-dot-size);
  border-radius: var(--cp-radius-pill);
  background: currentColor;
  flex: none;
  vertical-align: middle;
}

.cp-dot--sm { --cp-dot-size: var(--cp-space-2); }      /* 4px */
.cp-dot--lg { --cp-dot-size: var(--cp-space-4); }      /* 8px */
.cp-dot--xl { --cp-dot-size: var(--cp-space-5); }      /* 10px */

.cp-dot--muted   { color: var(--cp-color-fg-muted); }
.cp-dot--accent  { color: var(--cp-color-accent); }
.cp-dot--success { color: var(--cp-color-status-success); }
.cp-dot--warning { color: var(--cp-color-status-warning); }
.cp-dot--danger  { color: var(--cp-color-status-danger); }
.cp-dot--info    { color: var(--cp-color-status-info); }

/* A "pulsing" dot — telemetry-style live indicator. Two layers: the solid
   core and a softer halo that pulses outward. We use a pseudo-element so
   no extra markup is required. */
.cp-dot--pulse {
  position: relative;
}
.cp-dot--pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: currentColor;
  opacity: 0.45;
  animation: cp-dot-pulse calc(var(--cp-duration-slow) * 5) var(--cp-ease-out) infinite;
}
@keyframes cp-dot-pulse {
  0%   { transform: scale(1);   opacity: 0.45; }
  70%  { transform: scale(2.6); opacity: 0;    }
  100% { transform: scale(2.6); opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
  .cp-dot--pulse::after { animation: none; }
}

/* ── end src/styles/components/icon.css ── */
/* ── inlined from src/styles/components/menu.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-menu — dropdown / overflow (kebab) action-menu PANEL.

   Styling only. This is the floating *panel* that a dropdown, popover, or
   overflow control reveals — positioning, open/close, and the kebab trigger
   are the consumer's job (an ng-zorro dropdown, a CDK overlay, a popover…).
   The kebab trigger itself is a `.cp-button--icon`, not part of this file.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-menu` is the root panel: elevated bg, hairline border, radius,
     shadow-2, overlay z-index, padding, sensible min-inline-size.
   - Parts:
       `__item`      — a real <button> or <a>; full-width, left-aligned,
                       with its own local-property cascade for the states.
       `__item-icon` — leading 1em glyph slot.
       `__item-shortcut` — trailing mono, faint keyboard hint (tabular).
       `__label`     — small mono-caps non-interactive group label.
       `__separator` — hairline divider (<hr> or role="separator").
   - Item modifiers: `--danger` (destructive). Selected via
     `aria-checked="true"` / `aria-selected="true"` (accent tint + trailing
     check, drawn by `__item::after`). Disabled via `:disabled` /
     `[aria-disabled="true"]`.
   - Sizes: default + `--compact`.
   - Surfaces: `data-surface="mobile"` (bigger touch items),
     `data-surface="embedded"` (mono, hairline, tighter).

   Accessibility:
   - Panel carries `role="menu"`; each item `role="menuitem"` (or
     `menuitemradio` / `menuitemcheckbox` for selectable items).
   - Items are real interactive elements, so the global :focus-visible ring
     (base/focus.css) applies for keyboard nav. `aria-expanded` lives on the
     external trigger — not our concern.
   ───────────────────────────────────────────────────────────────────────── */

.cp-menu {
  /* Component-local custom properties so size / surface modifiers can
     override them without rewriting the layout rules below. */
  --_cp-menu-bg: var(--cp-color-bg-elevated);
  --_cp-menu-fg: var(--cp-color-fg);
  --_cp-menu-border: var(--cp-color-border-subtle);
  --_cp-menu-border-width: var(--cp-border-hairline);
  --_cp-menu-radius: var(--cp-radius-md);
  --_cp-menu-shadow: var(--cp-shadow-2);
  --_cp-menu-pad: var(--cp-space-2);
  --_cp-menu-gap: var(--cp-space-1);
  --_cp-menu-min-inline: 200px; /* optical: wide enough for label + shortcut */

  /* Knobs shared down into the item parts via inheritance. */
  --_cp-menu-item-min-size: var(--cp-target-mouse);
  --_cp-menu-item-pad-x: var(--cp-space-5);
  --_cp-menu-item-pad-y: var(--cp-space-3);
  --_cp-menu-item-gap: var(--cp-space-5);
  --_cp-menu-item-radius: var(--cp-radius-sm);
  --_cp-menu-item-font-size: var(--cp-font-size-14);
  --_cp-menu-item-font-family: var(--cp-font-family-body);
  --_cp-menu-item-font-weight: var(--cp-font-weight-medium);
  --_cp-menu-label-font-size: var(--cp-font-size-10);

  display: flex;
  flex-direction: column;
  gap: var(--_cp-menu-gap);

  min-inline-size: var(--_cp-menu-min-inline);
  max-inline-size: 320px;
  padding: var(--_cp-menu-pad);

  color: var(--_cp-menu-fg);
  background: var(--_cp-menu-bg);
  border: var(--_cp-menu-border-width) solid var(--_cp-menu-border);
  border-radius: var(--_cp-menu-radius);
  box-shadow: var(--_cp-menu-shadow);

  /* Floating panel. The consumer positions it; we just claim the layer. */
  z-index: var(--cp-z-overlay);

  font-family: var(--cp-font-family-body);
  text-align: start;
}

/* ── Item — the interactive row ───────────────────────────────────────── */

.cp-menu__item {
  /* Item-local cascade. Modifiers and states flip these. */
  --_cp-item-bg: transparent;
  --_cp-item-fg: var(--_cp-menu-fg);
  --_cp-item-accent: var(--cp-color-accent);

  display: flex;
  align-items: center;
  gap: var(--_cp-menu-item-gap);
  inline-size: 100%;

  min-block-size: var(--_cp-menu-item-min-size);
  padding: var(--_cp-menu-item-pad-y) var(--_cp-menu-item-pad-x);

  font-family: var(--_cp-menu-item-font-family);
  font-size: var(--_cp-menu-item-font-size);
  font-weight: var(--_cp-menu-item-font-weight);
  line-height: var(--cp-line-height-snug);
  letter-spacing: var(--cp-tracking-normal);
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;

  color: var(--_cp-item-fg);
  background: var(--_cp-item-bg);
  border: 0;
  border-radius: var(--_cp-menu-item-radius);

  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  appearance: none;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}

/* Leading icon slot — 1em square, faint until the row is hot. */
.cp-menu__item-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1em;
  block-size: 1em;
  color: var(--cp-color-fg-muted);
}
.cp-menu__item-icon > svg {
  inline-size: 100%;
  block-size: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

/* Trailing shortcut hint — pushed to the far edge, mono, faint. */
.cp-menu__item-shortcut {
  flex: 0 0 auto;
  margin-inline-start: auto;
  padding-inline-start: var(--cp-space-7);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-wide);
  color: var(--cp-color-fg-faint);
  font-variant-numeric: tabular-nums;
}

/* Trailing selected check — drawn for aria-checked / aria-selected items.
   Hidden by default; revealed by the selected-state rules below. The
   ::after sits after the shortcut, so it never collides with it. */
.cp-menu__item::after {
  content: "";
  flex: 0 0 auto;
  margin-inline-start: var(--cp-space-3);
  opacity: 0;
  /* A check mark rendered as a rotated, two-sided border box — no asset,
     no font dependency, picks up currentColor. */
  inline-size: 0.42em;  /* optical: tick proportions */
  block-size: 0.72em;   /* optical */
  margin-block-start: -0.18em; /* optical: lift the tick onto the cap line */
  border-inline-end: var(--cp-border-thin) solid currentColor;
  border-block-end: var(--cp-border-thin) solid currentColor;
  transform: rotate(45deg);
  transition: opacity var(--cp-duration-fast) var(--cp-ease-standard);
}
/* When there's no shortcut, the check still needs to hug the right edge. */
.cp-menu__item:not(:has(.cp-menu__item-shortcut))::after {
  margin-inline-start: auto;
}

/* ── Item states ──────────────────────────────────────────────────────── */

.cp-menu__item:hover,
.cp-menu__item.cp-is-hover {
  --_cp-item-bg: var(--cp-color-bg-inset);
  --_cp-item-fg: var(--cp-color-fg);
}
.cp-menu__item:hover .cp-menu__item-icon,
.cp-menu__item.cp-is-hover .cp-menu__item-icon {
  color: var(--cp-color-fg);
}

.cp-menu__item:active,
.cp-menu__item.cp-is-active {
  --_cp-item-bg: color-mix(in srgb, var(--cp-color-fg) 12%, transparent);
}

/* Selected — subtle accent tint + revealed trailing check. Works for both
   aria-checked (menuitemcheckbox) and aria-selected (menuitemradio / lists). */
.cp-menu__item[aria-checked="true"],
.cp-menu__item[aria-selected="true"],
.cp-menu__item.cp-is-selected {
  --_cp-item-bg: color-mix(in srgb, var(--_cp-item-accent) 12%, transparent);
  --_cp-item-fg: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
}
.cp-menu__item[aria-checked="true"]:hover,
.cp-menu__item[aria-selected="true"]:hover,
.cp-menu__item.cp-is-selected:hover {
  --_cp-item-bg: color-mix(in srgb, var(--_cp-item-accent) 20%, transparent);
}
.cp-menu__item[aria-checked="true"]::after,
.cp-menu__item[aria-selected="true"]::after,
.cp-menu__item.cp-is-selected::after {
  opacity: 1;
  color: var(--_cp-item-accent);
}

/* Danger — destructive action (Delete, Revoke). Danger fg, danger-tinted
   hover. The tint stays subtle so it reads as "caution" not "alarm". */
.cp-menu__item--danger {
  --_cp-item-fg: var(--cp-color-status-danger);
  --_cp-item-accent: var(--cp-color-status-danger);
}
.cp-menu__item--danger .cp-menu__item-icon {
  color: var(--cp-color-status-danger);
}
.cp-menu__item--danger:hover,
.cp-menu__item--danger.cp-is-hover {
  --_cp-item-bg: color-mix(in srgb, var(--cp-color-status-danger) 14%, transparent);
  --_cp-item-fg: var(--cp-color-status-danger);
}
.cp-menu__item--danger:hover .cp-menu__item-icon,
.cp-menu__item--danger.cp-is-hover .cp-menu__item-icon {
  color: var(--cp-color-status-danger);
}
.cp-menu__item--danger:active,
.cp-menu__item--danger.cp-is-active {
  --_cp-item-bg: color-mix(in srgb, var(--cp-color-status-danger) 22%, transparent);
}

/* Disabled — inert. Real <button> uses :disabled; links / aria-driven rows
   use [aria-disabled="true"]. Both kill hover and dim to fg-disabled. */
.cp-menu__item:disabled,
.cp-menu__item[aria-disabled="true"],
.cp-menu__item.cp-is-disabled {
  --_cp-item-bg: transparent;
  --_cp-item-fg: var(--cp-color-fg-disabled);
  cursor: not-allowed;
}
.cp-menu__item:disabled .cp-menu__item-icon,
.cp-menu__item[aria-disabled="true"] .cp-menu__item-icon,
.cp-menu__item.cp-is-disabled .cp-menu__item-icon {
  color: var(--cp-color-fg-disabled);
}
.cp-menu__item:disabled:hover,
.cp-menu__item[aria-disabled="true"]:hover,
.cp-menu__item.cp-is-disabled:hover {
  /* aria-disabled stays focusable but must not animate as if alive. */
  --_cp-item-bg: transparent;
  --_cp-item-fg: var(--cp-color-fg-disabled);
}

/* ── Label — non-interactive group heading ────────────────────────────── */

.cp-menu__label {
  padding: var(--cp-space-3) var(--_cp-menu-item-pad-x) var(--cp-space-2);
  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-menu-label-font-size);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
  user-select: none;
}
/* A label that isn't the first child gets breathing room above it. */
.cp-menu__label:not(:first-child) {
  margin-block-start: var(--cp-space-3);
}

/* ── Separator — hairline divider ─────────────────────────────────────── */

.cp-menu__separator {
  block-size: 0;
  margin: var(--cp-space-2) calc(-1 * var(--_cp-menu-pad)); /* bleed to panel edges */
  border: 0;
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}

/* ── Sizes ────────────────────────────────────────────────────────────── */

.cp-menu--compact {
  --_cp-menu-pad: var(--cp-space-1);
  --_cp-menu-min-inline: 168px; /* optical: tighter dense menus still fit a shortcut */
  --_cp-menu-item-min-size: var(--cp-target-compact);
  --_cp-menu-item-pad-x: var(--cp-space-4);
  --_cp-menu-item-pad-y: var(--cp-space-2);
  --_cp-menu-item-gap: var(--cp-space-4);
  --_cp-menu-item-radius: var(--cp-radius-xs);
  --_cp-menu-item-font-size: var(--cp-font-size-12);
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

/* Mobile — bigger touch items, roomier panel. */
.cp-menu[data-surface="mobile"] {
  --_cp-menu-pad: var(--cp-space-3);
  --_cp-menu-radius: var(--cp-radius-lg);
  --_cp-menu-min-inline: 240px;
  --_cp-menu-item-min-size: var(--cp-target-touch);
  --_cp-menu-item-pad-x: var(--cp-space-7);
  --_cp-menu-item-pad-y: var(--cp-space-5);
  --_cp-menu-item-font-size: var(--cp-font-size-16);
  --_cp-menu-item-radius: var(--cp-radius-md);
}

/* Embedded — mono type, hairline chrome, tighter. The OLED / terminal feel. */
.cp-menu[data-surface="embedded"] {
  --_cp-menu-bg: var(--cp-color-bg);
  --_cp-menu-border-width: var(--cp-border-hairline);
  --_cp-menu-radius: var(--cp-radius-xs);
  --_cp-menu-shadow: var(--cp-shadow-none);
  --_cp-menu-pad: var(--cp-space-1);
  --_cp-menu-min-inline: 160px;
  --_cp-menu-item-min-size: var(--cp-target-compact);
  --_cp-menu-item-pad-x: var(--cp-space-4);
  --_cp-menu-item-pad-y: var(--cp-space-2);
  --_cp-menu-item-radius: var(--cp-radius-0);
  --_cp-menu-item-font-size: var(--cp-font-size-11);
  --_cp-menu-item-font-family: var(--cp-font-family-mono);
  --_cp-menu-item-font-weight: var(--cp-font-weight-medium);
}
.cp-menu[data-surface="embedded"] .cp-menu__item {
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}
.cp-menu[data-surface="embedded"] .cp-menu__item-shortcut {
  font-size: var(--cp-font-size-10);
}

/* ── end src/styles/components/menu.css ── */
/* ── inlined from src/styles/components/modal.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-modal — modals, confirm dialogs, drawers, sheets.

   Native <dialog>-based element. The same class set is
   safe to apply to a <div role="dialog" aria-modal="true">, but
   consumers should prefer <dialog>:
     • Browser-managed focus trap.
     • ESC closes for free.
     • ::backdrop pseudo-element for the scrim.
     • inert content behind the modal.
     • Promoted to the top-layer (above everything, no z-index battles).

   Architecture mirrors button.css:
     • Single root class .cp-modal carries surface + layout.
     • BEM elements for parts (__header, __title, __close, __body,
       __footer, __icon).
     • Modifier classes for size (--sm/--lg/--xl/--fullscreen) and
       severity (--danger/--warning/--info).
     • The drawer is a sibling root .cp-drawer that shares the surface
       tokens but swaps placement and motion.
     • Component-local custom properties (--_cp-modal-*) so size and
       severity tweaks don't have to rewrite layout rules.

   A11y note for consumers:
     • Move focus to the dialog when it opens (most browsers do this
       automatically for <dialog>; for div-fallback wire it yourself).
     • Return focus to the trigger on close.
     • Set aria-modal="true" on the div-fallback root.
     • Set aria-labelledby on the dialog pointing at __title id.
     • Set aria-describedby pointing at __body id for screen readers.
   ───────────────────────────────────────────────────────────────────────── */

.cp-modal {
  /* Component-local properties so modifiers can re-tune without
     rewriting layout below.

     Modal inline-sizes (360 / 480 / 640 / 800) are deliberately not
     drawn from the spacing scale: they're modal-content-width design
     decisions sized by reading-rhythm/content density rather than
     UI rhythm. Same justification used in form.css for the 18px
     checkbox indicator. */
  --_cp-modal-inline-size: 480px;
  --_cp-modal-pad: var(--cp-space-10);
  --_cp-modal-gap: var(--cp-space-8);
  --_cp-modal-radius: var(--cp-radius-md);
  --_cp-modal-border-color: var(--cp-color-border-subtle);
  --_cp-modal-border-width: var(--cp-border-thin);
  --_cp-modal-shadow: var(--cp-shadow-3);
  --_cp-modal-accent: transparent;

  /* Reset native <dialog> chrome. The native rules vary across browsers
     and we want a single surface that works for both <dialog> and a
     <div role="dialog"> fallback. */
  padding: 0;
  margin: auto;
  border: 0;

  inline-size: min(100% - var(--cp-space-9), var(--_cp-modal-inline-size));
  max-inline-size: min(100% - var(--cp-space-9), var(--_cp-modal-inline-size));
  max-block-size: calc(100dvh - var(--cp-space-12));

  /* Centred via inset + margin auto for both <dialog> (top-layer) and
     div-fallback rendered inside a fixed-position overlay container. */
  inset-block: 0;
  inset-inline: 0;

  background: var(--cp-color-bg-elevated);
  color: var(--cp-color-fg);
  border: var(--_cp-modal-border-width) solid var(--_cp-modal-border-color);
  border-inline-start: 0;
  border-radius: var(--_cp-modal-radius);
  box-shadow: var(--_cp-modal-shadow);

  /* When a severity modifier sets --_cp-modal-accent, the inline-start
     edge becomes a wide accent rail. Default is transparent — same
     border thickness on every side. */
  box-sizing: border-box;
  background-image: linear-gradient(
    to right,
    var(--_cp-modal-accent) 0,
    var(--_cp-modal-accent) var(--cp-space-2),
    transparent var(--cp-space-2)
  );

  /* Content layout: a vertical stack of parts (header, body, footer)
     each separated by --_cp-modal-gap. The body grows; header+footer
     stay snug.

     CRITICAL: don't set `display` here — the user-agent rule
     `dialog:not([open]) { display: none }` must keep the dialog
     hidden when closed. We set the layout `display:flex` only when
     the dialog is open or when used as a non-dialog element.
     Other layout properties (gap, flex-direction) are inert when
     display is none, so they're safe to set on the base rule. */
  flex-direction: column;
  gap: var(--_cp-modal-gap);

  /* Inner padding is on the dialog so the gradient rail can sit flush
     against the inline-start edge without bleeding under content. */
  padding: var(--_cp-modal-pad);
  padding-inline-start: calc(var(--_cp-modal-pad) + var(--cp-space-2));

  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);

  /* Open/close animation. The dialog's `open` attribute toggles the
     entry; transition-behavior:allow-discrete is required so the
     display:none → display:flex transition runs at all. */
  opacity: 0;
  transform: scale(0.96) translateY(var(--cp-space-3));
  transition:
    opacity var(--cp-duration-normal) var(--cp-ease-standard),
    transform var(--cp-duration-normal) var(--cp-ease-standard),
    overlay var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete,
    display var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete;
}

/* `display: flex` is applied only when the dialog is open OR when the
   .cp-modal class is used on a non-dialog element (div-fallback). */
.cp-modal[open],
.cp-modal:not(dialog):not([hidden]) {
  display: flex;
  opacity: 1;
  transform: scale(1) translateY(0);
}

/* Starting state for the entry animation — required for the
   @starting-style block to drive the open transition. */
@starting-style {
  .cp-modal[open] {
    opacity: 0;
    transform: scale(0.96) translateY(var(--cp-space-3));
  }
}

/* ── Backdrop ─────────────────────────────────────────────────────────── */

.cp-modal::backdrop {
  background: var(--cp-color-bg-overlay);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  transition:
    opacity var(--cp-duration-normal) var(--cp-ease-standard),
    overlay var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete,
    display var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete;
}

.cp-modal[open]::backdrop {
  opacity: 1;
}

@starting-style {
  .cp-modal[open]::backdrop { opacity: 0; }
}

/* Companion class for div-fallback. Use this when consumers can't use
   <dialog>: it provides the same scrim look but as a real element,
   so the consumer wires the click-to-close + focus-trap themselves. */
.cp-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--cp-z-overlay);
  background: var(--cp-color-bg-overlay);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: grid;
  place-items: center;
  padding: var(--cp-space-9);
}
.cp-modal-backdrop[hidden] { display: none; }
.cp-modal-backdrop > .cp-modal {
  /* div-fallback modal sits inside the overlay container instead of
     in the top-layer. Reset inset so flex centring takes over. */
  position: static;
  z-index: var(--cp-z-modal);
}

/* ── Header / Title / Close ──────────────────────────────────────────── */

.cp-modal__header {
  display: flex;
  align-items: flex-start;
  gap: var(--cp-space-7);
  /* No bottom border by default — the gap between parts is enough.
     Consumers can add .cp-modal__header--ruled if they want a divider. */
}

.cp-modal__header--ruled {
  padding-block-end: var(--cp-space-7);
  border-block-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}

.cp-modal__title {
  flex: 1 1 auto;
  min-inline-size: 0;
  margin: 0;
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-snug);
  letter-spacing: var(--cp-tracking-normal);
  color: var(--cp-color-fg);
}

.cp-modal__subtitle {
  display: block;
  margin-block-start: var(--cp-space-2);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}

/* Close button — bare X icon button. Square, ghost-style, sits at the
   end of the header. Composed in markup as a real <button>. */
.cp-modal__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  inline-size: var(--cp-target-mouse);
  block-size: var(--cp-target-mouse);
  padding: 0;
  background: transparent;
  color: var(--cp-color-fg-muted);
  border: 0;
  border-radius: var(--cp-radius-sm);
  cursor: pointer;
  appearance: none;
  font-size: var(--cp-font-size-18);
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-modal__close:hover,
.cp-modal__close.cp-is-hover {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
}
.cp-modal__close:active,
.cp-modal__close.cp-is-active {
  background: color-mix(in srgb, var(--cp-color-fg) 12%, transparent);
}

/* The X glyph: a CSS-drawn cross so consumers don't need an icon
   asset. 1em sizing keeps it scalable. Two pseudo-element bars
   rotated 45° around the centre. */
.cp-modal__close::before,
.cp-modal__close::after {
  content: "";
  position: absolute;
  inline-size: 0.9em;
  block-size: var(--cp-border-thin);
  background: currentColor;
  border-radius: var(--cp-border-thin);
}
.cp-modal__close::before { transform: rotate(45deg); }
.cp-modal__close::after  { transform: rotate(-45deg); }

/* Allow consumers to pass their own SVG inside __close — hide the CSS
   bars when an SVG child is present. */
.cp-modal__close:has(> svg)::before,
.cp-modal__close:has(> svg)::after { content: none; }
.cp-modal__close > svg {
  inline-size: var(--cp-space-9);
  block-size: var(--cp-space-9);
}

/* ── Leading icon (for confirm dialogs) ──────────────────────────────── */

.cp-modal__icon {
  flex: 0 0 auto;
  inline-size: var(--cp-space-12);
  block-size: var(--cp-space-12);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--cp-border-thin) solid currentColor;
  border-radius: var(--cp-radius-md);
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-24);
  font-weight: var(--cp-font-weight-semibold);
  color: var(--cp-color-fg-muted);
  /* Slight inward margin so the icon visually aligns with the title
     baseline (optical correction — title sits higher than its box). */
  margin-block-start: -2px; /* optical */
}
.cp-modal__icon > svg {
  inline-size: var(--cp-space-10);
  block-size: var(--cp-space-10);
  stroke: currentColor;
  fill: none;
  stroke-width: 1.5;
}

/* Severity recolours the leading icon automatically. */
.cp-modal--danger  .cp-modal__icon { color: var(--cp-color-status-danger); }
.cp-modal--warning .cp-modal__icon { color: var(--cp-color-status-warning); }
.cp-modal--info    .cp-modal__icon { color: var(--cp-color-status-info); }
.cp-modal--success .cp-modal__icon { color: var(--cp-color-status-success); }

/* ── Body ────────────────────────────────────────────────────────────── */

.cp-modal__body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);
  color: var(--cp-color-fg-muted);
  /* Negative margin + matching padding so the scrollbar appears flush
     against the dialog edge, not floating in the middle of the gap. */
  margin-inline: calc(var(--cp-space-3) * -1);
  padding-inline: var(--cp-space-3);
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-6);
}
.cp-modal__body :first-child { margin-block-start: 0; }
.cp-modal__body :last-child  { margin-block-end: 0; }
.cp-modal__body p { margin: 0; }
.cp-modal__body strong,
.cp-modal__body b { color: var(--cp-color-fg); }

/* ── Footer ──────────────────────────────────────────────────────────── */

.cp-modal__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--cp-space-5);
  flex-wrap: wrap;
}
.cp-modal__footer--start  { justify-content: flex-start; }
.cp-modal__footer--between{ justify-content: space-between; }

/* A "left rail" inside the footer for secondary metadata (a checkbox
   like "don't ask again", a status pill). */
.cp-modal__footer-aside {
  margin-inline-end: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-5);
  font-size: var(--cp-font-size-12);
  color: var(--cp-color-fg-muted);
}

/* ── Size modifiers ──────────────────────────────────────────────────── */

.cp-modal--sm         { --_cp-modal-inline-size: 360px; }
/* default                360px < default(480) < 640 < 800 */
.cp-modal--lg         { --_cp-modal-inline-size: 640px; }
.cp-modal--xl         { --_cp-modal-inline-size: 800px; }

.cp-modal--fullscreen {
  --_cp-modal-inline-size: calc(100vw - var(--cp-space-11));
  --_cp-modal-radius: var(--cp-radius-lg);
  max-block-size: calc(100dvh - var(--cp-space-11));
  block-size: calc(100dvh - var(--cp-space-11));
}

/* ── Severity modifiers — colour the inline-start accent rail. ────────── */

.cp-modal--danger  { --_cp-modal-accent: var(--cp-color-status-danger); }
.cp-modal--warning { --_cp-modal-accent: var(--cp-color-status-warning); }
.cp-modal--info    { --_cp-modal-accent: var(--cp-color-status-info); }
.cp-modal--success { --_cp-modal-accent: var(--cp-color-status-success); }

/* Drop the accent rail's added padding when no severity is set. */
.cp-modal:not(.cp-modal--danger):not(.cp-modal--warning):not(.cp-modal--info):not(.cp-modal--success) {
  padding-inline-start: var(--_cp-modal-pad);
  background-image: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-drawer — edge-anchored side panel. Shares the surface vocabulary
   of .cp-modal (header / body / footer parts) but flips placement and
   motion. The drawer slides in from the inline-end edge by default;
   .cp-drawer--start slides from inline-start.
   ───────────────────────────────────────────────────────────────────────── */

.cp-drawer {
  --_cp-drawer-inline-size: clamp(320px, 40vw, 560px);

  padding: 0;
  margin: 0;
  border: 0;
  inline-size: var(--_cp-drawer-inline-size);
  max-inline-size: 100vw;
  block-size: 100dvh;
  max-block-size: 100dvh;

  inset-block: 0;
  inset-inline-end: 0;
  inset-inline-start: auto;

  background: var(--cp-color-bg-elevated);
  color: var(--cp-color-fg);
  border-inline-start: var(--cp-border-thin) solid var(--cp-color-border-subtle);
  border-radius: 0;
  box-shadow: var(--cp-shadow-3);

  /* Same rationale as .cp-modal — do not set `display` on the closed
     state. The UA rule `dialog:not([open]) { display: none }` keeps
     the drawer hidden when no `open` attribute is present. */
  flex-direction: column;
  /* No gap on the root — body scrolls and we want the header/footer to
     pin to the edges. Use internal padding instead. */

  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-normal);

  transform: translateX(100%);
  transition:
    transform var(--cp-duration-normal) var(--cp-ease-standard),
    overlay var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete,
    display var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete;
}

.cp-drawer[open],
.cp-drawer:not(dialog):not([hidden]) {
  display: flex;
  transform: translateX(0);
}

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

.cp-drawer--start {
  inset-inline-start: 0;
  inset-inline-end: auto;
  border-inline-start: 0;
  border-inline-end: var(--cp-border-thin) solid var(--cp-color-border-subtle);
  transform: translateX(-100%);
}
.cp-drawer--start[open] { transform: translateX(0); }
@starting-style {
  .cp-drawer--start[open] { transform: translateX(-100%); }
}

.cp-drawer::backdrop {
  background: var(--cp-color-bg-overlay);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  transition:
    opacity var(--cp-duration-normal) var(--cp-ease-standard),
    overlay var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete,
    display var(--cp-duration-normal) var(--cp-ease-standard) allow-discrete;
}
.cp-drawer[open]::backdrop { opacity: 1; }
@starting-style {
  .cp-drawer[open]::backdrop { opacity: 0; }
}

/* Drawer parts — re-uses the same naming so consumers don't relearn
   the API. Padding is per-part because drawers scroll within the
   body, and the header/footer should pin to the edges. */
.cp-drawer__header {
  display: flex;
  align-items: flex-start;
  gap: var(--cp-space-7);
  padding: var(--cp-space-10);
  padding-block-end: var(--cp-space-7);
  border-block-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}
.cp-drawer__title {
  flex: 1 1 auto;
  margin: 0;
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-snug);
}
.cp-drawer__subtitle {
  display: block;
  margin-block-start: var(--cp-space-2);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}
.cp-drawer__close {
  /* Re-use modal close styles; the only difference is alignment. */
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  inline-size: var(--cp-target-mouse);
  block-size: var(--cp-target-mouse);
  padding: 0;
  background: transparent;
  color: var(--cp-color-fg-muted);
  border: 0;
  border-radius: var(--cp-radius-sm);
  cursor: pointer;
  appearance: none;
  font-size: var(--cp-font-size-18);
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-drawer__close::before,
.cp-drawer__close::after {
  content: "";
  position: absolute;
  inline-size: 0.9em;
  block-size: var(--cp-border-thin);
  background: currentColor;
  border-radius: var(--cp-border-thin);
}
.cp-drawer__close::before { transform: rotate(45deg); }
.cp-drawer__close::after  { transform: rotate(-45deg); }
.cp-drawer__close:hover { background: var(--cp-color-bg-inset); color: var(--cp-color-fg); }

.cp-drawer__body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  padding: var(--cp-space-10);
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-8);
}

.cp-drawer__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--cp-space-5);
  padding: var(--cp-space-8) var(--cp-space-10);
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  background: var(--cp-color-bg);
}

/* Drawer size modifiers — the inline-size variants. */
.cp-drawer--sm { --_cp-drawer-inline-size: clamp(280px, 28vw, 360px); }
.cp-drawer--lg { --_cp-drawer-inline-size: clamp(420px, 56vw, 720px); }
.cp-drawer--xl { --_cp-drawer-inline-size: clamp(480px, 72vw, 960px); }

/* ── Surfaces ───────────────────────────────────────────────────────── */

.cp-modal[data-surface="mobile"],
.cp-drawer[data-surface="mobile"] {
  --_cp-modal-pad: var(--cp-space-9);
  font-size: var(--cp-font-size-16);
}
.cp-modal[data-surface="mobile"] .cp-modal__title,
.cp-drawer[data-surface="mobile"] .cp-drawer__title {
  font-size: var(--cp-font-size-24);
}

.cp-modal[data-surface="embedded"] {
  --_cp-modal-pad: var(--cp-space-7);
  --_cp-modal-radius: var(--cp-radius-xs);
  --_cp-modal-border-width: var(--cp-border-hairline);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  letter-spacing: var(--cp-tracking-wide);
}
.cp-modal[data-surface="embedded"] .cp-modal__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-14);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
}

/* ── Reduced motion — skip the entry transform, keep the opacity fade
   short. Drawers stop sliding and just fade. */

@media (prefers-reduced-motion: reduce) {
  .cp-modal,
  .cp-drawer {
    transition-duration: 0ms;
    transform: none;
  }
  .cp-modal[open],
  .cp-drawer[open] {
    transform: none;
  }
  @starting-style {
    .cp-modal[open],
    .cp-drawer[open] {
      transform: none;
      opacity: 1;
    }
  }
}

/* ── end src/styles/components/modal.css ── */
/* ── inlined from src/styles/components/nav.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   Navigation family — .cp-topnav, .cp-sidenav, .cp-tabs, .cp-command-bar.

   Four sibling components, one file. They share enough structural DNA
   (item rows, active states keyed on aria-current / aria-selected,
   surface flips for mobile + embedded) that splitting them would force
   duplication of the same idioms.

   Conventions mirror button.css and card.css:
   - Single root class per component, BEM elements (`__part`), BEM
     modifiers (`--variant`).
   - Component-local custom properties (`--_cp-...`) so modifiers swap
     values without re-stating layout.
   - States flow from ARIA first (`aria-current`, `aria-selected`,
     `aria-expanded`, `aria-disabled`, `aria-busy`) — never from
     hand-rolled class soup.
   - No hex, no hardcoded px outside documented optical micro-tweaks.
   ───────────────────────────────────────────────────────────────────────── */


/* ═════════════════════════════════════════════════════════════════════════
   .cp-topnav — top horizontal navigation bar.

   Use case: marketing pages, doc sites, the chrome above a desktop GCS
   workspace. Composes brand on the leading edge, link cluster in the
   middle, and an actions cluster pushed to the trailing edge via
   `margin-inline-start: auto` on .cp-topnav__actions.
   ═════════════════════════════════════════════════════════════════════════ */

.cp-topnav {
  /* Component-local custom properties — modifiers and surfaces override
     these without rewriting the layout rules below. */
  --_cp-topnav-bg: var(--cp-color-bg-elevated);
  --_cp-topnav-fg: var(--cp-color-fg);
  --_cp-topnav-border: var(--cp-color-border-subtle);
  --_cp-topnav-border-width: var(--cp-border-hairline);
  --_cp-topnav-min-size: var(--cp-target-mouse);
  --_cp-topnav-pad-y: var(--cp-space-4);
  --_cp-topnav-pad-x: var(--cp-space-8);
  --_cp-topnav-gap: var(--cp-space-8);

  display: flex;
  align-items: center;
  gap: var(--_cp-topnav-gap);

  min-block-size: var(--_cp-topnav-min-size);
  padding: var(--_cp-topnav-pad-y) var(--_cp-topnav-pad-x);

  font-family: var(--cp-font-family-body);
  color: var(--_cp-topnav-fg);
  background: var(--_cp-topnav-bg);
  border-block-end: var(--_cp-topnav-border-width) solid var(--_cp-topnav-border);
}

/* Leading slot — logo + product wordmark. */
.cp-topnav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-5);
  flex: 0 0 auto;
  color: inherit;
  text-decoration: none;
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-normal);
  line-height: var(--cp-line-height-tight);
}
.cp-topnav__brand > img,
.cp-topnav__brand > svg {
  inline-size: var(--cp-space-10);     /* 24px */
  block-size: var(--cp-space-10);
  flex: 0 0 auto;
}

/* Link cluster — a ul that resets and lays out horizontally. */
.cp-topnav__items {
  display: flex;
  align-items: center;
  gap: var(--cp-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

/* Individual link. The `position: relative` is here so the active
   underline (rendered via ::after) can anchor to the bottom edge of the
   topnav bar, not just the link's own box. */
.cp-topnav__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);

  padding: var(--cp-space-3) var(--cp-space-5);

  font-family: inherit;
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  text-decoration: none;
  white-space: nowrap;

  border-radius: var(--cp-radius-xs);
  background: transparent;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-topnav__item > svg,
.cp-topnav__item > .cp-icon {
  inline-size: 1em;
  block-size: 1em;
  flex: 0 0 auto;
}

.cp-topnav__item:hover,
.cp-topnav__item.cp-is-hover {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
}

/* Active page — bold + accent underline. The underline sits flush with
   the topnav's bottom border so it visually replaces the hairline for
   the active item only. */
.cp-topnav__item[aria-current="page"] {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
}
.cp-topnav__item[aria-current="page"]::after {
  content: "";
  position: absolute;
  inset-inline: var(--cp-space-5);
  inset-block-end: calc(var(--_cp-topnav-pad-y, var(--cp-space-4)) * -1);
  block-size: var(--cp-border-thick);
  background: var(--cp-color-accent);
}

/* Trailing cluster — buttons, theme switch, avatar. */
.cp-topnav__actions {
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-5);
  flex: 0 0 auto;
}

/* Avatar — square-rounded chip used in the actions cluster.
   Composes any image / initials inside. */
.cp-topnav__avatar {
  display: inline-grid;
  place-items: center;
  inline-size: var(--cp-target-mouse);
  block-size: var(--cp-target-mouse);
  border-radius: var(--cp-radius-pill);
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-normal);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  overflow: hidden;
}
.cp-topnav__avatar > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

/* Surface — mobile bumps the bar up to the touch target. */
.cp-topnav[data-surface="mobile"] {
  --_cp-topnav-min-size: var(--cp-target-touch);
  --_cp-topnav-pad-y: var(--cp-space-5);
  --_cp-topnav-pad-x: var(--cp-space-9);
}
.cp-topnav[data-surface="mobile"] .cp-topnav__item {
  font-size: var(--cp-font-size-16);
  padding: var(--cp-space-4) var(--cp-space-6);
}

/* Surface — embedded: mono, hairline, uppercase items. */
.cp-topnav[data-surface="embedded"] {
  --_cp-topnav-min-size: var(--cp-target-compact);
  --_cp-topnav-pad-y: var(--cp-space-2);
  --_cp-topnav-pad-x: var(--cp-space-5);
  font-family: var(--cp-font-family-mono);
}
.cp-topnav[data-surface="embedded"] .cp-topnav__brand {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
}
.cp-topnav[data-surface="embedded"] .cp-topnav__item {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
}


/* ═════════════════════════════════════════════════════════════════════════
   .cp-sidenav — vertical side navigation panel.

   Use case: the workspace shell for a desktop GCS. 240px expanded, 56px
   collapsed (icons only). Composes a top brand area, any number of
   groups (each labeled with a mono uppercase header), and a footer
   slot pushed to the bottom via margin-block-start: auto.
   ═════════════════════════════════════════════════════════════════════════ */

.cp-sidenav {
  --_cp-sidenav-bg: var(--cp-color-bg-elevated);
  --_cp-sidenav-fg: var(--cp-color-fg);
  --_cp-sidenav-border: var(--cp-color-border-subtle);
  --_cp-sidenav-border-width: var(--cp-border-hairline);
  /* 240px / 56px (collapsed) / 280px (mobile) are layout constants for the
     nav rail itself — they sit outside the spacing scale by design (the
     scale tops out at 96px). Authors can override via the local custom
     property if a host needs a different rail width. */
  --_cp-sidenav-inline-size: 240px;
  --_cp-sidenav-pad-y: var(--cp-space-10);  /* 24px */
  --_cp-sidenav-pad-x: var(--cp-space-9);   /* 20px */
  --_cp-sidenav-gap: var(--cp-space-3);

  display: flex;
  flex-direction: column;
  gap: var(--_cp-sidenav-gap);

  inline-size: var(--_cp-sidenav-inline-size);
  block-size: 100%;
  padding: var(--_cp-sidenav-pad-y) var(--_cp-sidenav-pad-x);

  font-family: var(--cp-font-family-body);
  color: var(--_cp-sidenav-fg);
  background: var(--_cp-sidenav-bg);
  border-inline-end: var(--_cp-sidenav-border-width) solid var(--_cp-sidenav-border);

  /* Container query: lets child items adapt when the sidenav is
     resized below the collapse threshold (i.e. authors can drop the
     --collapsed modifier and resize the inline-size on its own). */
  container-type: inline-size;
  container-name: cp-sidenav;
}

/* Brand block at the top — logo + wordmark, separated from the first
   group by the natural sidenav gap. */
.cp-sidenav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-5);
  padding: var(--cp-space-2) var(--cp-space-4);
  color: inherit;
  text-decoration: none;
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-20);
  line-height: var(--cp-line-height-tight);
  margin-block-end: var(--cp-space-5);
}
.cp-sidenav__brand > img,
.cp-sidenav__brand > svg {
  inline-size: var(--cp-space-11);    /* 32px */
  block-size: var(--cp-space-11);
  flex: 0 0 auto;
}

/* Group — a labeled section of items. Author drops a __group-label
   inside as the section header. */
.cp-sidenav__group {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-1);
  margin-block-start: var(--cp-space-5);
}
.cp-sidenav__group:first-of-type { margin-block-start: 0; }

.cp-sidenav__group-label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
  padding: var(--cp-space-3) var(--cp-space-4) var(--cp-space-2);
  margin: 0;
}

/* Individual item — link with optional leading icon and trailing badge.
   padding-block 6px / padding-inline 10px matches the brief; we use
   tokens space-3 (6px) and space-5 (10px) to stay scale-aligned. */
.cp-sidenav__item {
  display: flex;
  align-items: center;
  gap: var(--cp-space-5);

  padding: var(--cp-space-3) var(--cp-space-5);

  font-family: inherit;
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

  background: transparent;
  border: 0;
  border-radius: var(--cp-radius-sm);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  min-inline-size: 0;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-sidenav__item:hover,
.cp-sidenav__item.cp-is-hover {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
}

/* Active item — filled with ink, inverted text. Same idiom as
   .cp-button--solid so the GCS workspace reads consistently. */
.cp-sidenav__item[aria-current="page"] {
  background: var(--cp-color-fg);
  color: var(--cp-color-bg);
  font-weight: var(--cp-font-weight-semibold);
}
.cp-sidenav__item[aria-current="page"]:hover {
  /* Don't let hover muddle the filled active state — keep it crisp. */
  background: var(--cp-color-fg);
  color: var(--cp-color-bg);
}

/* Accent active variant — orange fill instead of ink. Use for the
   "primary" workspace section (Map & Mission in the GCS). */
.cp-sidenav__item--accent[aria-current="page"] {
  background: var(--cp-color-accent);
  color: var(--cp-color-fg-on-accent);
}
.cp-sidenav__item--accent[aria-current="page"]:hover {
  background: color-mix(in srgb, var(--cp-color-accent) 88%, black);
  color: var(--cp-color-fg-on-accent);
}

/* Leading icon slot — sized to the item's font size so it tracks
   typography changes. */
.cp-sidenav__item__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-space-9);     /* 20px */
  block-size: var(--cp-space-9);
  flex: 0 0 auto;
  color: inherit;
}
.cp-sidenav__item__icon > svg,
.cp-sidenav__item__icon > .cp-icon {
  inline-size: 100%;
  block-size: 100%;
}

/* Label — flexes to take the row's remaining width. */
.cp-sidenav__item__label {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Trailing count / badge — tabular figures, mono, hairline border. */
.cp-sidenav__item__count {
  flex: 0 0 auto;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-normal);
  font-variant-numeric: tabular-nums;
  padding: 0 var(--cp-space-3);
  min-inline-size: var(--cp-space-9); /* 20px */
  block-size: var(--cp-space-9);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--cp-radius-xs);
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg-muted);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}
.cp-sidenav__item[aria-current="page"] .cp-sidenav__item__count {
  /* Invert badge against the filled active background so it stays
     legible. Border drops because the contrast already separates it. */
  background: color-mix(in srgb, var(--cp-color-bg) 18%, transparent);
  color: var(--cp-color-bg);
  border-color: transparent;
}

/* Aria-disabled item — focusable, inert, explained. */
.cp-sidenav__item[aria-disabled="true"] {
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
  background: transparent;
}
.cp-sidenav__item[aria-disabled="true"]:hover {
  background: transparent;
  color: var(--cp-color-fg-disabled);
}

/* Footer slot — pinned to the bottom of the panel via auto margin. */
.cp-sidenav__footer {
  margin-block-start: auto;
  padding-block-start: var(--cp-space-5);
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-1);
}

/* Collapsed modifier — 56px wide, icons only. Labels and counts and
   group labels collapse out of the document flow via display: none so
   focus order stays sane. */
.cp-sidenav--collapsed {
  --_cp-sidenav-inline-size: 56px;
  --_cp-sidenav-pad-x: var(--cp-space-4);
  align-items: stretch;
}
.cp-sidenav--collapsed .cp-sidenav__brand {
  justify-content: center;
  padding: var(--cp-space-2);
  font-size: 0;            /* hides any inline wordmark text */
  gap: 0;
}
.cp-sidenav--collapsed .cp-sidenav__group-label {
  /* Collapse the label out but keep an accessible hairline divider so
     the group structure stays visible. */
  block-size: 0;
  padding: 0;
  margin: 0;
  overflow: hidden;
  border-block-start: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}
.cp-sidenav--collapsed .cp-sidenav__group:first-of-type .cp-sidenav__group-label {
  border-block-start: 0;
}
.cp-sidenav--collapsed .cp-sidenav__item {
  justify-content: center;
  padding: var(--cp-space-3);
  gap: 0;
}
.cp-sidenav--collapsed .cp-sidenav__item__label,
.cp-sidenav--collapsed .cp-sidenav__item__count {
  /* Hide accessibly: not announced as a separate node when the row
     itself carries the link's accessible name via aria-label. Authors
     should add aria-label="<route>" when collapsing. */
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Surface — mobile expands targets. */
.cp-sidenav[data-surface="mobile"] {
  --_cp-sidenav-inline-size: 280px;
}
.cp-sidenav[data-surface="mobile"] .cp-sidenav__item {
  min-block-size: var(--cp-target-touch);
  font-size: var(--cp-font-size-16);
  padding: var(--cp-space-5) var(--cp-space-6);
}

/* Surface — embedded: mono, hairline. */
.cp-sidenav[data-surface="embedded"] {
  --_cp-sidenav-border-width: var(--cp-border-hairline);
  font-family: var(--cp-font-family-mono);
}
.cp-sidenav[data-surface="embedded"] .cp-sidenav__item {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-wide);
  border-radius: var(--cp-radius-xs);
}


/* ═════════════════════════════════════════════════════════════════════════
   .cp-tabs — horizontal tab control.

   Use case: switching between views within a single workspace pane
   (Telemetry / Mission / Logs / Settings inside a vehicle panel).
   The base treatment is the underline tab; --enclosed flips to a
   pill-row row; --vertical rotates the whole thing onto the inline-start.
   ═════════════════════════════════════════════════════════════════════════ */

.cp-tabs {
  --_cp-tabs-gap: var(--cp-space-1);
  --_cp-tabs-fg: var(--cp-color-fg-muted);
  --_cp-tabs-bg: transparent;
  --_cp-tabs-border: var(--cp-color-border-subtle);
  --_cp-tabs-pad-y: var(--cp-space-4);
  --_cp-tabs-pad-x: var(--cp-space-7);
  --_cp-tabs-indicator-size: var(--cp-border-thick);

  display: flex;
  align-items: stretch;
  gap: var(--_cp-tabs-gap);

  background: var(--_cp-tabs-bg);
  border-block-end: var(--cp-border-hairline) solid var(--_cp-tabs-border);

  /* Allow horizontal scroll on narrow viewports rather than wrapping —
     tabs read as a single bar, not a wrapping list. */
  overflow-x: auto;
  scrollbar-width: none;
}
.cp-tabs::-webkit-scrollbar { display: none; }

/* Single tab. Border-bottom is transparent by default so the row's
   bottom edge remains the only visible hairline; the active tab takes
   over that hairline with the accent indicator. */
.cp-tabs__tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cp-space-3);

  padding: var(--_cp-tabs-pad-y) var(--_cp-tabs-pad-x);

  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  color: var(--_cp-tabs-fg);
  white-space: nowrap;
  text-decoration: none;

  background: transparent;
  border: 0;
  border-block-end: var(--_cp-tabs-indicator-size) solid transparent;
  /* Pull the bottom indicator down so it sits on the row's own hairline,
     not above it. The 1px equals the row's border-bottom — the only
     hardcoded px in this file is here, where the indicator must mate
     pixel-perfect with the hairline below. */
  margin-block-end: calc(var(--cp-border-hairline) * -1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;

  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-tabs__tab > svg,
.cp-tabs__tab > .cp-icon {
  inline-size: 1em;
  block-size: 1em;
  flex: 0 0 auto;
}

.cp-tabs__tab:hover,
.cp-tabs__tab.cp-is-hover {
  color: var(--cp-color-fg);
}

.cp-tabs__tab[aria-selected="true"] {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
  border-block-end-color: var(--cp-color-accent);
}

.cp-tabs__tab[aria-disabled="true"],
.cp-tabs__tab:disabled {
  color: var(--cp-color-fg-disabled);
  cursor: not-allowed;
}

/* Count chip inside a tab — same idiom as the sidenav count. */
.cp-tabs__tab__count {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-variant-numeric: tabular-nums;
  padding: 0 var(--cp-space-3);
  min-inline-size: var(--cp-space-9);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--cp-radius-pill);
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg-muted);
}
.cp-tabs__tab[aria-selected="true"] .cp-tabs__tab__count {
  background: color-mix(in srgb, var(--cp-color-accent) 16%, transparent);
  color: var(--cp-color-accent);
}

/* Panel — content under the tabs. Authors usually attach role="tabpanel"
   and an aria-labelledby pointing back at the matching tab. */
.cp-tabs__panel {
  padding-block: var(--cp-space-9);
  color: var(--cp-color-fg);
}
.cp-tabs__panel[hidden] { display: none; }

/* Variant — enclosed: pill-like row of buttons, no bottom hairline. */
.cp-tabs--enclosed {
  border-block-end: 0;
  gap: var(--cp-space-1);
  padding: var(--cp-space-1);
  background: var(--cp-color-bg-inset);
  border-radius: var(--cp-radius-sm);
  display: inline-flex;
}
.cp-tabs--enclosed .cp-tabs__tab {
  border-block-end: 0;
  margin-block-end: 0;
  padding: var(--cp-space-3) var(--cp-space-6);
  border-radius: var(--cp-radius-xs);
}
.cp-tabs--enclosed .cp-tabs__tab[aria-selected="true"] {
  background: var(--cp-color-bg-elevated);
  color: var(--cp-color-fg);
  box-shadow: var(--cp-shadow-1);
}

/* Variant — vertical: tabs along the inline-start edge. The accent
   indicator becomes a vertical bar on the trailing edge. */
.cp-tabs--vertical {
  flex-direction: column;
  align-items: stretch;
  border-block-end: 0;
  border-inline-end: var(--cp-border-hairline) solid var(--_cp-tabs-border);
  gap: 0;
  overflow-x: visible;
  overflow-y: auto;
}
.cp-tabs--vertical .cp-tabs__tab {
  justify-content: flex-start;
  border-block-end: 0;
  border-inline-end: var(--_cp-tabs-indicator-size) solid transparent;
  margin-block-end: 0;
  /* Same 1px mate as the horizontal indicator, on the trailing edge. */
  margin-inline-end: calc(var(--cp-border-hairline) * -1);
  padding: var(--cp-space-5) var(--cp-space-7);
}
.cp-tabs--vertical .cp-tabs__tab[aria-selected="true"] {
  border-inline-end-color: var(--cp-color-accent);
}

/* Surface — mobile: larger tap targets. */
.cp-tabs[data-surface="mobile"] {
  --_cp-tabs-pad-y: var(--cp-space-6);
  --_cp-tabs-pad-x: var(--cp-space-9);
}
.cp-tabs[data-surface="mobile"] .cp-tabs__tab {
  min-block-size: var(--cp-target-touch);
  font-size: var(--cp-font-size-16);
}

/* Surface — embedded: mono, hairline, uppercase. */
.cp-tabs[data-surface="embedded"] {
  --_cp-tabs-indicator-size: var(--cp-border-hairline);
}
.cp-tabs[data-surface="embedded"] .cp-tabs__tab {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
  padding: var(--cp-space-2) var(--cp-space-5);
}


/* ═════════════════════════════════════════════════════════════════════════
   .cp-command-bar — a thin toolbar of ghost buttons + segmented groups
   + dividers. Used above log viewers, parameter tables, mission lists.

   Composes existing components rather than reinventing them: the bar
   itself is a flex strip; the children are .cp-button buttons (usually
   .cp-button--ghost / .cp-button--sm) and .cp-button-group segments.
   The bar adds the height contract and the divider element.
   ═════════════════════════════════════════════════════════════════════════ */

.cp-command-bar {
  --_cp-cmdbar-bg: var(--cp-color-bg-elevated);
  --_cp-cmdbar-border: var(--cp-color-border-subtle);
  --_cp-cmdbar-min-size: var(--cp-target-mouse);
  --_cp-cmdbar-pad-y: var(--cp-space-2);
  --_cp-cmdbar-pad-x: var(--cp-space-5);
  --_cp-cmdbar-gap: var(--cp-space-3);

  display: flex;
  align-items: center;
  gap: var(--_cp-cmdbar-gap);
  flex-wrap: wrap;

  min-block-size: var(--_cp-cmdbar-min-size);
  padding: var(--_cp-cmdbar-pad-y) var(--_cp-cmdbar-pad-x);

  background: var(--_cp-cmdbar-bg);
  border: var(--cp-border-hairline) solid var(--_cp-cmdbar-border);
  border-radius: var(--cp-radius-sm);
  color: var(--cp-color-fg);
  font-family: var(--cp-font-family-body);
}

/* Push trailing children to the right. */
.cp-command-bar__spacer {
  flex: 1 1 auto;
}

/* Vertical divider between sections of the bar. */
.cp-command-bar__divider {
  inline-size: var(--cp-border-hairline);
  align-self: stretch;
  background: var(--cp-color-border-subtle);
  margin-inline: var(--cp-space-2);
  flex: 0 0 auto;
}

/* Label — mono uppercase caption used as a section label inside the
   bar (e.g. "FILTER:" before a segmented group). */
.cp-command-bar__label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
  white-space: nowrap;
}

/* Surface — embedded: mono labels, hairline. */
.cp-command-bar[data-surface="embedded"] {
  font-family: var(--cp-font-family-mono);
  --_cp-cmdbar-pad-y: var(--cp-space-1);
  --_cp-cmdbar-pad-x: var(--cp-space-4);
  --_cp-cmdbar-min-size: var(--cp-target-compact);
  border-radius: var(--cp-radius-xs);
}

/* ── end src/styles/components/nav.css ── */
/* ── inlined from src/styles/components/pagination.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-pager — numeric paginator
   .cp-pager-info — companion "Showing X of Y" caption
   .cp-pager-size — segmented page-size selector (10 / 25 / 50 / 100)
   .cp-breadcrumbs — hierarchical wayfinding trail

   Conventions mirror button.css:
   - Real interactive elements (<button>, <a>) only — no <div> targets.
   - Sizes follow --cp-target-{compact,mouse,touch} so the touch minimum
     stays 32px even on desktop.
   - Numeric labels use mono + tabular-nums so the row never jitters when
     "9" turns into "10".
   - Variants are BEM modifiers; states prefer ARIA (aria-current,
     aria-disabled) and fall back to .cp-is-* force-state classes.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Pager (numeric) ─────────────────────────────────────────────────────── */

.cp-pager {
  /* Component-local custom properties so size/surface modifiers can override
     the layout without rewriting every rule. */
  --_cp-pager-size: var(--cp-target-mouse);             /* 32px default */
  --_cp-pager-font-size: var(--cp-font-size-12);
  --_cp-pager-gap: var(--cp-space-2);
  --_cp-pager-pad-x: var(--cp-space-3);
  --_cp-pager-radius: var(--cp-radius-sm);
  --_cp-pager-border-width: var(--cp-border-hairline);
  --_cp-pager-current-bg: var(--cp-color-fg);
  --_cp-pager-current-fg: var(--cp-color-bg);
  --_cp-pager-current-border: var(--cp-color-fg);

  display: inline-flex;
  align-items: center;
  gap: var(--_cp-pager-gap);
  vertical-align: middle;
  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-pager-font-size);
  font-variant-numeric: tabular-nums;
  line-height: var(--cp-line-height-tight);
}

/* All children share the same box geometry; .cp-pager__item layers
   interactive affordances on top. */
.cp-pager__item,
.cp-pager__ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--_cp-pager-size);
  block-size: var(--_cp-pager-size);
  padding-inline: var(--_cp-pager-pad-x);
  border: var(--_cp-pager-border-width) solid var(--cp-color-border-subtle);
  border-radius: var(--_cp-pager-radius);
  background: transparent;
  color: var(--cp-color-fg);
  font: inherit;
  font-variant-numeric: inherit;
  text-decoration: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  appearance: none;
  box-sizing: border-box;
}

/* Only the interactive children get pointer/transition affordances. */
.cp-pager__item {
  cursor: pointer;
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-pager__item:hover,
.cp-pager__item.cp-is-hover {
  background: var(--cp-color-bg-inset);
  border-color: var(--cp-color-border-subtle);
  color: var(--cp-color-fg);
}

.cp-pager__item:active,
.cp-pager__item.cp-is-active {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
}

/* Current page — selected via aria-current OR the explicit force-state
   class. Either works; aria-current is preferred for screen readers. */
.cp-pager__item[aria-current="page"],
.cp-pager__item.cp-is-current {
  background: var(--_cp-pager-current-bg);
  color: var(--_cp-pager-current-fg);
  border-color: var(--_cp-pager-current-border);
  font-weight: var(--cp-font-weight-semibold);
  cursor: default;
}
.cp-pager__item[aria-current="page"]:hover,
.cp-pager__item.cp-is-current:hover {
  /* Current pages don't animate on hover — they're the destination. */
  background: var(--_cp-pager-current-bg);
  color: var(--_cp-pager-current-fg);
  border-color: var(--_cp-pager-current-border);
}

/* Ellipsis — non-interactive separator. Borderless, faint. */
.cp-pager__ellipsis {
  border-color: transparent;
  color: var(--cp-color-fg-faint);
  cursor: default;
  user-select: none;
}

/* Disabled — real <button disabled> OR aria-disabled. Used for the first/prev
   buttons on page 1, last/next on the last page. */
.cp-pager__item:disabled,
.cp-pager__item[aria-disabled="true"],
.cp-pager__item.cp-is-disabled {
  color: var(--cp-color-fg-disabled);
  border-color: var(--cp-color-border-subtle);
  background: transparent;
  cursor: not-allowed;
}
.cp-pager__item:disabled:hover,
.cp-pager__item[aria-disabled="true"]:hover,
.cp-pager__item.cp-is-disabled:hover {
  background: transparent;
  color: var(--cp-color-fg-disabled);
}

/* Edge buttons (first / last) — non-numeric, slightly narrower icon density.
   Same geometry as prev/next; the chevron content carries the meaning. */
.cp-pager__prev,
.cp-pager__next,
.cp-pager__edge {
  /* Pure icon button — square. The text content is the chevron glyph. */
  padding-inline: var(--cp-space-2);
}
.cp-pager__prev > svg,
.cp-pager__next > svg,
.cp-pager__edge > svg {
  inline-size: 1em;
  block-size: 1em;
  flex: 0 0 auto;
}

/* ── Pager size variants ─────────────────────────────────────────────────── */

.cp-pager--compact {
  --_cp-pager-size: var(--cp-target-compact);           /* 24px */
  --_cp-pager-font-size: var(--cp-font-size-11);
  --_cp-pager-gap: var(--cp-space-1);
  --_cp-pager-pad-x: var(--cp-space-2);
  --_cp-pager-radius: var(--cp-radius-xs);
}

/* ── Pager colour variants ───────────────────────────────────────────────── */

.cp-pager--accent {
  --_cp-pager-current-bg: var(--cp-color-accent);
  --_cp-pager-current-fg: var(--cp-color-fg-on-accent);
  --_cp-pager-current-border: var(--cp-color-accent);
}

/* ── Companion info caption ──────────────────────────────────────────────── */

.cp-pager-info {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-4);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-variant-numeric: tabular-nums;
  color: var(--cp-color-fg-muted);
  line-height: var(--cp-line-height-tight);
}
.cp-pager-info strong {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
}

/* A flex row that pairs a pager with its info caption on either side. */
.cp-pager-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--cp-space-6);
}

/* ── Page-size selector (segmented) ──────────────────────────────────────── */

.cp-pager-size {
  --_cp-pager-size-h: var(--cp-target-mouse);
  --_cp-pager-size-font: var(--cp-font-size-11);

  display: inline-flex;
  align-items: stretch;
  isolation: isolate;
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
  overflow: hidden;
  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-pager-size-font);
  font-variant-numeric: tabular-nums;
  background: transparent;
  vertical-align: middle;
}

.cp-pager-size__label {
  display: inline-flex;
  align-items: center;
  padding-inline: var(--cp-space-5);
  padding-block: 0;
  color: var(--cp-color-fg-muted);
  background: var(--cp-color-bg-inset);
  border-inline-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-wide);
  font-size: var(--cp-font-size-10);
  white-space: nowrap;
}

.cp-pager-size__option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--_cp-pager-size-h);
  block-size: var(--_cp-pager-size-h);
  padding-inline: var(--cp-space-5);
  border: 0;
  border-inline-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  background: transparent;
  color: var(--cp-color-fg);
  font: inherit;
  font-variant-numeric: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  appearance: none;
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-pager-size__option:last-child { border-inline-end: 0; }
.cp-pager-size__option:hover,
.cp-pager-size__option.cp-is-hover {
  background: var(--cp-color-bg-inset);
}
.cp-pager-size__option[aria-pressed="true"],
.cp-pager-size__option.cp-is-current {
  background: var(--cp-color-fg);
  color: var(--cp-color-bg);
  z-index: 1;
}

.cp-pager-size--compact {
  --_cp-pager-size-h: var(--cp-target-compact);
  --_cp-pager-size-font: var(--cp-font-size-10);
}

/* ── Breadcrumbs ─────────────────────────────────────────────────────────── */

.cp-breadcrumbs {
  --_cp-crumbs-font-size: var(--cp-font-size-12);

  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--cp-space-3);
  vertical-align: middle;
  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-crumbs-font-size);
  line-height: var(--cp-line-height-snug);
}

.cp-breadcrumbs__list {
  /* Allow consumers to use <ol> for semantic crumb order without inherited
     list-style baggage. The flex layout lives on the wrapper instead. */
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--cp-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.cp-breadcrumbs__item {
  color: var(--cp-color-fg-muted);
  text-decoration: none;
  font-weight: var(--cp-font-weight-regular);
  border-radius: var(--cp-radius-xs);
  padding-inline: var(--cp-space-1);
  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-breadcrumbs__item:hover,
.cp-breadcrumbs__item.cp-is-hover {
  color: var(--cp-color-fg);
  background: var(--cp-color-bg-inset);
}

.cp-breadcrumbs__item[aria-current="page"],
.cp-breadcrumbs__item.cp-is-current {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
  cursor: default;
}
.cp-breadcrumbs__item[aria-current="page"]:hover,
.cp-breadcrumbs__item.cp-is-current:hover {
  background: transparent;
}

.cp-breadcrumbs__separator {
  color: var(--cp-color-fg-faint);
  user-select: none;
  /* Optical: drop 1px so the slash sits with the x-height of the labels. */
  transform: translateY(-0.5px);
}

/* Truncation cue: a non-interactive ellipsis crumb for deep trees. */
.cp-breadcrumbs__ellipsis {
  color: var(--cp-color-fg-faint);
  user-select: none;
  cursor: default;
}

/* ── Surfaces ────────────────────────────────────────────────────────────── */

.cp-pager[data-surface="mobile"] {
  --_cp-pager-size: var(--cp-target-touch);             /* 44px */
  --_cp-pager-font-size: var(--cp-font-size-14);
  --_cp-pager-gap: var(--cp-space-3);
  --_cp-pager-pad-x: var(--cp-space-5);
  --_cp-pager-radius: var(--cp-radius-md);
}

.cp-pager[data-surface="embedded"] {
  --_cp-pager-size: var(--cp-target-compact);
  --_cp-pager-font-size: var(--cp-font-size-10);
  --_cp-pager-gap: var(--cp-space-1);
  --_cp-pager-pad-x: var(--cp-space-2);
  --_cp-pager-radius: var(--cp-radius-xs);
  --_cp-pager-border-width: var(--cp-border-hairline);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}

.cp-breadcrumbs[data-surface="mobile"] {
  --_cp-crumbs-font-size: var(--cp-font-size-14);
  gap: var(--cp-space-4);
}

.cp-breadcrumbs[data-surface="embedded"] {
  --_cp-crumbs-font-size: var(--cp-font-size-10);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
  gap: var(--cp-space-2);
}

.cp-pager-size[data-surface="mobile"] {
  --_cp-pager-size-h: var(--cp-target-touch);
  --_cp-pager-size-font: var(--cp-font-size-13, var(--cp-font-size-12));
}
.cp-pager-size[data-surface="embedded"] {
  --_cp-pager-size-h: var(--cp-target-compact);
  --_cp-pager-size-font: var(--cp-font-size-10);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  text-transform: uppercase;
}

/* ── end src/styles/components/pagination.css ── */
/* ── inlined from src/styles/components/pill.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-pill — generic status pill / chip / badge family.

   The aviation-grade indicator. Compact, mono-cased, glanceable. Lives
   in telemetry strips, mode rows, toolbar trays, mission cards.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-pill` is the root. `__dot`, `__icon`, `__count`, `__remove`
     are the parts.
   - Severity modifiers (`--success`, `--warning`, `--danger`, `--info`,
     `--accent`, `--neutral`) paint the border + dot/icon color. Default
     is neutral.
   - Visual treatment modifiers (`--filled`, `--soft`, `--ghost`,
     `--square`) restyle without rewriting layout.
   - Sizes `--xs`, default, `--lg`.
   - Domain presets: `.cp-status-pill` + `--armed` / `--disarmed` /
     `--standby` / `--rtl` / `--manual` for vehicle states.
   - `.cp-chip` is the dismissible pattern (a pill with `__remove`).
   - `.cp-badge` is the count/dot indicator — smaller and frame-less.

   States flow from ARIA where applicable; the chip's remove button is
   a real `<button>` with focus + hover.
   ───────────────────────────────────────────────────────────────────────── */

.cp-pill {
  /* Component-local custom properties. Severity / size / treatment
     modifiers override these without rewriting the layout block. */
  --_cp-pill-bg: transparent;
  --_cp-pill-fg: var(--cp-color-fg);
  --_cp-pill-border: var(--cp-color-border-subtle);
  --_cp-pill-border-width: var(--cp-border-hairline);
  --_cp-pill-radius: var(--cp-radius-pill);
  --_cp-pill-pad-x: var(--cp-space-5);
  --_cp-pill-pad-y: var(--cp-space-1);
  --_cp-pill-gap: var(--cp-space-3);
  --_cp-pill-font-size: var(--cp-font-size-11);
  --_cp-pill-min-size: 22px; /* optical: tight pill that still clears 11px caps */
  --_cp-pill-accent: var(--cp-color-fg-muted);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--_cp-pill-gap);
  vertical-align: middle;

  min-block-size: var(--_cp-pill-min-size);
  padding: var(--_cp-pill-pad-y) var(--_cp-pill-pad-x);

  font-family: var(--cp-font-family-mono);
  font-size: var(--_cp-pill-font-size);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;

  background: var(--_cp-pill-bg);
  color: var(--_cp-pill-fg);
  border: var(--_cp-pill-border-width) solid var(--_cp-pill-border);
  border-radius: var(--_cp-pill-radius);

  user-select: none;
  -webkit-tap-highlight-color: transparent;

  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard),
    border-color var(--cp-duration-fast) var(--cp-ease-standard);
}

/* ── Parts ────────────────────────────────────────────────────────────── */

/* Leading dot — 6×6 disc, picks up the pill's accent color via currentColor
   on the parent's --_cp-pill-accent. */
.cp-pill__dot {
  flex: 0 0 auto;
  display: inline-block;
  inline-size: var(--cp-space-3);  /* 6px */
  block-size: var(--cp-space-3);
  border-radius: var(--cp-radius-pill);
  background: var(--_cp-pill-accent);
  /* Slight optical pull toward the cap-height baseline so the dot doesn't
     sit on the descender line of the mono font. */
  margin-block-start: -0.5px; /* optical */
}

/* Leading icon — 12px square. Track .cp-icon's currentColor pattern. */
.cp-pill__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-font-size-12);
  block-size: var(--cp-font-size-12);
  color: var(--_cp-pill-accent);
}
.cp-pill__icon > svg {
  inline-size: 100%;
  block-size: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

/* Trailing tabular number — voltages, counts, percentages. The pill's
   uppercase rule doesn't affect digits, but we make sure the numeric
   feature is on for clean alignment in stacked tables. */
.cp-pill__count {
  font-variant-numeric: tabular-nums;
  font-weight: var(--cp-font-weight-semibold);
  letter-spacing: var(--cp-tracking-normal);
}

/* Trailing dismissable X — a real <button>. */
.cp-pill__remove {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-font-size-12);
  block-size: var(--cp-font-size-12);
  padding: 0;
  margin-inline-start: var(--cp-space-1);
  margin-inline-end: calc(-1 * var(--cp-space-2)); /* optical: tuck against the pill's right edge */
  background: transparent;
  color: currentColor;
  border: 0;
  border-radius: var(--cp-radius-pill);
  cursor: pointer;
  opacity: 0.55;
  transition:
    opacity var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard),
    transform var(--cp-duration-instant) var(--cp-ease-standard);
}
.cp-pill__remove > svg {
  inline-size: 100%;
  block-size: 100%;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
}
.cp-pill__remove:hover,
.cp-pill__remove:focus-visible {
  opacity: 1;
  background: color-mix(in srgb, currentColor 18%, transparent);
}
.cp-pill__remove:active {
  transform: scale(0.9);
}

/* ── Severity / state variants ───────────────────────────────────────── */

.cp-pill--neutral {
  --_cp-pill-border: var(--cp-color-border-subtle);
  --_cp-pill-accent: var(--cp-color-fg-muted);
}

.cp-pill--success {
  --_cp-pill-border: var(--cp-color-status-success);
  --_cp-pill-accent: var(--cp-color-status-success);
}
.cp-pill--warning {
  --_cp-pill-border: var(--cp-color-status-warning);
  --_cp-pill-accent: var(--cp-color-status-warning);
}
.cp-pill--danger {
  --_cp-pill-border: var(--cp-color-status-danger);
  --_cp-pill-accent: var(--cp-color-status-danger);
}
.cp-pill--info {
  --_cp-pill-border: var(--cp-color-status-info);
  --_cp-pill-accent: var(--cp-color-status-info);
}
.cp-pill--accent {
  --_cp-pill-border: var(--cp-color-accent);
  --_cp-pill-accent: var(--cp-color-accent);
}

/* ── Treatment variants ──────────────────────────────────────────────── */

/* Filled — invert: solid colored bg, on-accent text. The dot becomes a
   contrasting disc on the bg so the part-name still reads. */
.cp-pill--filled {
  --_cp-pill-bg: var(--_cp-pill-accent);
  --_cp-pill-fg: var(--cp-color-fg-on-accent);
  --_cp-pill-border: var(--_cp-pill-accent);
}
.cp-pill--filled .cp-pill__dot,
.cp-pill--filled .cp-pill__icon {
  color: var(--cp-color-fg-on-accent);
  background: var(--cp-color-fg-on-accent);
}
/* Icons are stroke-only — overriding the dot's bg above would lose the
   stroke. Reset to currentColor for icons specifically. */
.cp-pill--filled .cp-pill__icon { background: transparent; }

/* Soft — translucent fill matching the accent at 10%. Border is kept so
   the pill still has a defined edge on busy backgrounds. */
.cp-pill--soft {
  --_cp-pill-bg: color-mix(in srgb, var(--_cp-pill-accent) 10%, transparent);
  --_cp-pill-fg: var(--_cp-pill-accent);
  --_cp-pill-border: color-mix(in srgb, var(--_cp-pill-accent) 45%, transparent);
}
/* Neutral soft uses fg-muted, which doesn't tint well — fall back to
   the bg-inset surface so it still reads as a chip. */
.cp-pill--soft.cp-pill--neutral,
.cp-pill--soft:not(.cp-pill--success, .cp-pill--warning, .cp-pill--danger,
                   .cp-pill--info,    .cp-pill--accent) {
  --_cp-pill-bg: var(--cp-color-bg-inset);
  --_cp-pill-fg: var(--cp-color-fg);
}

/* Ghost — no border, dot/icon and text only. The minimalist mode. */
.cp-pill--ghost {
  --_cp-pill-bg: transparent;
  --_cp-pill-border: transparent;
  --_cp-pill-border-width: 0;
  --_cp-pill-fg: var(--_cp-pill-accent);
  --_cp-pill-pad-x: var(--cp-space-3);
}

/* Square — for the embedded / mono surface where pills look more
   like data fields than pills. */
.cp-pill--square {
  --_cp-pill-radius: var(--cp-radius-xs);
}

/* ── Sizes ────────────────────────────────────────────────────────────── */

.cp-pill--xs {
  --_cp-pill-font-size: var(--cp-font-size-10);
  --_cp-pill-pad-x: var(--cp-space-4);
  --_cp-pill-pad-y: 0;
  --_cp-pill-gap: var(--cp-space-2);
  --_cp-pill-min-size: 18px; /* optical: match the badge dot column */
}

.cp-pill--lg {
  --_cp-pill-font-size: var(--cp-font-size-12);
  --_cp-pill-pad-x: var(--cp-space-7);
  --_cp-pill-pad-y: var(--cp-space-3);
  --_cp-pill-gap: var(--cp-space-4);
  --_cp-pill-min-size: 28px; /* optical: 28px keeps caps centred at 12px */
}
.cp-pill--lg .cp-pill__dot {
  inline-size: var(--cp-space-4);  /* 8px at large */
  block-size: var(--cp-space-4);
}
.cp-pill--lg .cp-pill__icon,
.cp-pill--lg .cp-pill__remove {
  inline-size: var(--cp-font-size-14);
  block-size: var(--cp-font-size-14);
}

/* ── Pulse animation — for ARMED / FAILSAFE / live indicators ─────────── */

@keyframes cp-pill-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

@keyframes cp-pill-dot-pulse {
  0%, 100% { transform: scale(1);    opacity: 1;    box-shadow: 0 0 0 0   color-mix(in srgb, currentColor 60%, transparent); }
  70%      { transform: scale(1.05); opacity: 0.85; box-shadow: 0 0 0 6px transparent; }
  100%     { transform: scale(1);    opacity: 1;    box-shadow: 0 0 0 0   transparent; }
}

/* Whole-pill pulse: gentle opacity breath. Use for FAILSAFE / RTL. */
.cp-pill--pulse {
  animation: cp-pill-pulse calc(var(--cp-duration-slow) * 5) var(--cp-ease-standard) infinite;
}

/* Dot-only pulse: the dot expands + glows. Use for ARMED. */
.cp-pill__dot--pulse {
  position: relative;
  animation: cp-pill-dot-pulse calc(var(--cp-duration-slow) * 4) var(--cp-ease-out) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .cp-pill--pulse,
  .cp-pill__dot--pulse {
    animation: none;
  }
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-pill[data-surface="mobile"] {
  --_cp-pill-font-size: var(--cp-font-size-12);
  --_cp-pill-pad-x: var(--cp-space-6);
  --_cp-pill-pad-y: var(--cp-space-2);
  --_cp-pill-min-size: 26px; /* optical: still slim but easier to read in motion */
}

.cp-pill[data-surface="embedded"] {
  --_cp-pill-font-size: var(--cp-font-size-10);
  --_cp-pill-pad-x: var(--cp-space-3);
  --_cp-pill-pad-y: 0;
  --_cp-pill-radius: var(--cp-radius-xs);
  --_cp-pill-min-size: 16px; /* optical: clearance for hairline 10px caps */
  border-width: var(--cp-border-hairline);
  letter-spacing: var(--cp-tracking-wide);
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-status-pill — domain preset built on .cp-pill.

   These are not standalone classes; pair `.cp-status-pill` with
   `.cp-pill` (the consumer copy-pastes both). The base class adds the
   layout; the preset adds the chrome.
   ───────────────────────────────────────────────────────────────────────── */

.cp-status-pill { /* alias hook — same as .cp-pill, but a discrete selector
                     lets consumers target the domain pattern without
                     having to know its severity. */
}

/* ARMED — red filled with a pulsing dot. Unmissable. */
.cp-status-pill--armed {
  --_cp-pill-accent: var(--cp-color-status-danger);
  --_cp-pill-bg: var(--cp-color-status-danger);
  --_cp-pill-fg: var(--cp-color-fg-on-accent);
  --_cp-pill-border: var(--cp-color-status-danger);
}
.cp-status-pill--armed .cp-pill__dot {
  background: var(--cp-color-fg-on-accent);
}

/* DISARMED — green outline. Static. */
.cp-status-pill--disarmed {
  --_cp-pill-accent: var(--cp-color-status-success);
  --_cp-pill-border: var(--cp-color-status-success);
}

/* STANDBY — amber outline. The "warmed up but not committed" state. */
.cp-status-pill--standby {
  --_cp-pill-accent: var(--cp-color-status-warning);
  --_cp-pill-border: var(--cp-color-status-warning);
}

/* RTL — blue outline. Returning to launch. Often paired with --pulse. */
.cp-status-pill--rtl {
  --_cp-pill-accent: var(--cp-color-status-info);
  --_cp-pill-border: var(--cp-color-status-info);
}

/* MANUAL — neutral outline. Default. */
.cp-status-pill--manual {
  --_cp-pill-accent: var(--cp-color-fg);
  --_cp-pill-border: var(--cp-color-border);
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-chip — dismissible alias of .cp-pill.

   Functionally identical to a pill carrying a `.cp-pill__remove` button;
   this class exists so consumers can express intent at the root level
   (filter chips, tag inputs).
   ───────────────────────────────────────────────────────────────────────── */

.cp-chip { /* extends .cp-pill — no extra layout needed; the part does
              the work. The class is a semantic hook for consumers and
              for future state styling (e.g. selected chips). */
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-badge — count indicator. Smaller and frame-less than a pill.

   Used for notification counts, tabs with unread counts, dot indicators
   over avatars or icons.
   ───────────────────────────────────────────────────────────────────────── */

.cp-badge {
  --_cp-badge-bg: var(--cp-color-accent);
  --_cp-badge-fg: var(--cp-color-fg-on-accent);
  --_cp-badge-size: 18px; /* optical: half-step under target-compact, big enough for 2 digits */

  display: inline-flex;
  align-items: center;
  justify-content: center;

  min-inline-size: var(--_cp-badge-size);
  block-size: var(--_cp-badge-size);
  padding-inline: var(--cp-space-3);

  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-semibold);
  line-height: 1;
  letter-spacing: var(--cp-tracking-normal);
  font-variant-numeric: tabular-nums;

  background: var(--_cp-badge-bg);
  color: var(--_cp-badge-fg);
  border-radius: var(--cp-radius-pill);
  vertical-align: middle;
  white-space: nowrap;
}

.cp-badge--neutral {
  --_cp-badge-bg: var(--cp-color-fg);
  --_cp-badge-fg: var(--cp-color-bg);
}
.cp-badge--success { --_cp-badge-bg: var(--cp-color-status-success); }
.cp-badge--warning {
  --_cp-badge-bg: var(--cp-color-status-warning);
  --_cp-badge-fg: var(--cp-color-cube-black);
}
.cp-badge--danger  { --_cp-badge-bg: var(--cp-color-status-danger); }
.cp-badge--info    { --_cp-badge-bg: var(--cp-color-status-info); }

/* Dot mode — no text, just a small filled circle. Used for "unread" /
   "live" markers that sit on top of icons or avatars. */
.cp-badge--dot {
  --_cp-badge-size: 8px; /* optical: smallest disc that still reads at arms-length */
  min-inline-size: var(--_cp-badge-size);
  padding-inline: 0;
  font-size: 0;
  line-height: 0;
}

/* Anchored badge — when a badge floats on top of a button / avatar /
   icon, the parent needs `position: relative` and adds `.cp-badge-anchor`. */
.cp-badge-anchor {
  position: relative;
  display: inline-flex;
}
.cp-badge-anchor > .cp-badge {
  position: absolute;
  inset-block-start: calc(-1 * var(--cp-space-2));
  inset-inline-end: calc(-1 * var(--cp-space-2));
  box-shadow: 0 0 0 2px var(--cp-color-bg); /* optical: 2px halo against any host bg */
}

/* Pulse for badge dots — telemetry "live" marker. */
.cp-badge--dot.cp-badge--pulse {
  position: relative;
}
.cp-badge--dot.cp-badge--pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: currentColor;
  opacity: 0.5;
  animation: cp-badge-pulse calc(var(--cp-duration-slow) * 5) var(--cp-ease-out) infinite;
}
@keyframes cp-badge-pulse {
  0%   { transform: scale(1);   opacity: 0.5; }
  70%  { transform: scale(2.4); opacity: 0;   }
  100% { transform: scale(2.4); opacity: 0;   }
}
@media (prefers-reduced-motion: reduce) {
  .cp-badge--dot.cp-badge--pulse::after { animation: none; }
}

/* ── end src/styles/components/pill.css ── */
/* ── inlined from src/styles/components/table.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-table — datasheets, parameter lists, vehicle telemetry, log readouts.

   Tables are dense by default; figures are tabular. Spec sheets and
   parameter editors lean on the mono face for spec values (.cp-num).

   Architecture follows the button.css convention:
   - Single root class `.cp-table` plus BEM elements (`__sort`, `__status`).
   - Density modifiers `--compact` `--roomy`.
   - Visual modifiers `--striped` `--bordered` `--borderless` `--sticky-head`.
   - Responsive collapse via `--responsive` + `data-label` on each cell.
   - Component-local custom properties (`--_cp-table-*`) so density modifiers
     re-tune padding without rewriting cell selectors.
   - `data-surface="embedded"` swaps the face to mono and removes header bg.
   ───────────────────────────────────────────────────────────────────────── */

.cp-table {
  /* Component-local custom properties — density variants override these. */
  --_cp-table-pad-y: var(--cp-space-5);
  --_cp-table-pad-x: var(--cp-space-7);
  --_cp-table-font-size: var(--cp-font-size-14);
  --_cp-table-head-bg: var(--cp-color-bg-inset);
  --_cp-table-row-border: var(--cp-color-border-subtle);
  --_cp-table-head-border: var(--cp-color-border);

  inline-size: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
  /* overflow:hidden lets the outer radius clip the head bg cleanly. */
  overflow: hidden;

  font-family: var(--cp-font-family-body);
  font-size: var(--_cp-table-font-size);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg);
  background: var(--cp-color-bg-elevated);
}

/* ── Cells ────────────────────────────────────────────────────────────── */

.cp-table th,
.cp-table td {
  padding: var(--_cp-table-pad-y) var(--_cp-table-pad-x);
  /* `start` is RTL-friendly. Numeric cells override below. */
  text-align: start;
  vertical-align: top;
  border-block-end: var(--cp-border-hairline) solid var(--_cp-table-row-border);
  /* Allow long callsigns/identifiers to wrap nicely without blowing layout. */
  overflow-wrap: anywhere;
}

/* Last-row borders look doubled against the table outer border — clip. */
.cp-table tbody tr:last-child > td {
  border-block-end: 0;
}

/* ── Head ─────────────────────────────────────────────────────────────── */

.cp-table thead {
  background: var(--_cp-table-head-bg);
}

.cp-table thead th {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  /* Heavier rule between head and body. */
  border-block-end: var(--cp-border-thin) solid var(--_cp-table-head-border);
  white-space: nowrap;
  vertical-align: middle;
}

/* Sticky head — pair with a constrained-height scroll container. */
.cp-table--sticky-head thead th {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--cp-z-sticky);
  /* sticky cells need an opaque background to mask scrolled content. */
  background: var(--_cp-table-head-bg);
}

/* ── Sortable header ──────────────────────────────────────────────────── */

.cp-table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  padding: 0;
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--cp-duration-fast) var(--cp-ease-standard);
}

/* Chevron — drawn via ::after so it inherits currentColor cleanly. */
.cp-table__sort::after {
  content: "";
  inline-size: 0.7em;
  block-size: 0.7em;
  background-color: currentColor;
  /* Default "unsorted" chevron — opposing arrows. */
  -webkit-mask: var(--_cp-table-sort-icon) center / contain no-repeat;
          mask: var(--_cp-table-sort-icon) center / contain no-repeat;
  opacity: 0.55;
  flex: 0 0 auto;
  transition: opacity var(--cp-duration-fast) var(--cp-ease-standard);
  --_cp-table-sort-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M6 1 3 4.5h6L6 1Zm0 10 3-3.5H3L6 11Z"/></svg>');
}

.cp-table__sort:hover,
.cp-table__sort:focus-visible {
  color: var(--cp-color-fg);
}
.cp-table__sort:hover::after,
.cp-table__sort:focus-visible::after {
  opacity: 1;
}

/* aria-sort drives the chevron direction. */
.cp-table th[aria-sort="ascending"] .cp-table__sort {
  color: var(--cp-color-fg);
}
.cp-table th[aria-sort="ascending"] .cp-table__sort::after {
  opacity: 1;
  --_cp-table-sort-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M6 2 1.5 8.5h9L6 2Z"/></svg>');
}
.cp-table th[aria-sort="descending"] .cp-table__sort {
  color: var(--cp-color-fg);
}
.cp-table th[aria-sort="descending"] .cp-table__sort::after {
  opacity: 1;
  --_cp-table-sort-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M6 10 1.5 3.5h9L6 10Z"/></svg>');
}

/* Hover highlight on a sortable column header for affordance. */
.cp-table th:has(.cp-table__sort):hover {
  background: color-mix(in srgb, var(--cp-color-fg) 4%, transparent);
}

/* ── Numeric cells ────────────────────────────────────────────────────── */

.cp-table td.cp-num,
.cp-table th.cp-num--head {
  text-align: end;
  font-variant-numeric: tabular-nums;
  font-family: var(--cp-font-family-mono);
}
.cp-table th.cp-num--head {
  /* Head retains its mono caps treatment but right-aligns. */
  text-align: end;
}

/* ── Row variants ─────────────────────────────────────────────────────── */

.cp-table tbody tr {
  transition: background var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-table tbody tr:hover,
.cp-table tbody tr.cp-is-hover {
  background: var(--cp-color-bg-inset);
}

.cp-table tbody tr.cp-is-selected {
  background: color-mix(in srgb, var(--cp-color-accent) 12%, transparent);
}
.cp-table tbody tr.cp-is-selected:hover {
  background: color-mix(in srgb, var(--cp-color-accent) 18%, transparent);
}

.cp-table tbody tr.cp-is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.cp-table tbody tr.cp-is-disabled:hover {
  background: transparent;
}

/* ── Status cells ─────────────────────────────────────────────────────── */

.cp-table td.cp-table__status {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  white-space: nowrap;
}
.cp-table__status .cp-table__dot {
  display: inline-block;
  inline-size: 0.55em;
  block-size: 0.55em;
  margin-inline-end: var(--cp-space-3);
  border-radius: var(--cp-radius-pill);
  background: currentColor;
  vertical-align: 0.08em; /* optical centering against caps text */
}
.cp-table__status.cp-is-success { color: var(--cp-color-status-success); }
.cp-table__status.cp-is-warning { color: var(--cp-color-status-warning); }
.cp-table__status.cp-is-error   { color: var(--cp-color-status-danger);  }
.cp-table__status.cp-is-info    { color: var(--cp-color-status-info);    }

/* ── Density variants ─────────────────────────────────────────────────── */

.cp-table--compact {
  --_cp-table-pad-y: var(--cp-space-3);
  --_cp-table-pad-x: var(--cp-space-5);
  --_cp-table-font-size: var(--cp-font-size-12);
}
.cp-table--roomy {
  --_cp-table-pad-y: var(--cp-space-8);
  --_cp-table-pad-x: var(--cp-space-9);
}

/* ── Striped ──────────────────────────────────────────────────────────── */

.cp-table--striped tbody tr:nth-child(odd of :not(.cp-is-selected)) {
  background: var(--cp-color-bg-inset);
}
.cp-table--striped tbody tr:hover {
  background: color-mix(in srgb, var(--cp-color-fg) 5%, var(--cp-color-bg-inset));
}

/* ── Bordered — vertical hairlines between every cell ─────────────────── */

.cp-table--bordered th + th,
.cp-table--bordered td + td {
  border-inline-start: var(--cp-border-hairline) solid var(--_cp-table-row-border);
}

/* ── Borderless — only the head underline survives ────────────────────── */

.cp-table--borderless {
  border-color: transparent;
}
.cp-table--borderless tbody tr td {
  border-block-end: 0;
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-table[data-surface="embedded"] {
  --_cp-table-head-bg: transparent;
  --_cp-table-pad-y: var(--cp-space-3);
  --_cp-table-pad-x: var(--cp-space-5);
  --_cp-table-font-size: var(--cp-font-size-11);
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
  border-width: var(--cp-border-hairline);
  border-radius: var(--cp-radius-xs);
}
.cp-table[data-surface="embedded"] thead th {
  background: transparent;
  font-size: var(--cp-font-size-10);
  border-block-end-width: var(--cp-border-hairline);
}
.cp-table[data-surface="embedded"] td {
  /* Embedded readouts read like log lines — let everything flow as mono. */
  font-family: var(--cp-font-family-mono);
}

/* ── Mobile-responsive card collapse ──────────────────────────────────── */

/* On narrow viewports, rows stack into card-like blocks; each cell
   shows its header label via `data-label="…"`. Apply
   `.cp-table--responsive` and ensure every <td> carries `data-label`. */
@media (max-width: 720px) {
  .cp-table--responsive {
    border: 0;
    border-radius: 0;
    background: transparent;
    overflow: visible;
  }
  .cp-table--responsive thead {
    /* Screen readers still need the head, sighted users do not. */
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .cp-table--responsive tbody tr {
    display: block;
    margin-block-end: var(--cp-space-7);
    border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
    border-radius: var(--cp-radius-sm);
    background: var(--cp-color-bg-elevated);
    overflow: hidden;
  }
  .cp-table--responsive tbody tr:last-child {
    margin-block-end: 0;
  }
  .cp-table--responsive tbody tr:hover {
    /* Hover doesn't translate to touch — neutralise. */
    background: var(--cp-color-bg-elevated);
  }
  .cp-table--responsive tbody td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--cp-space-7);
    padding: var(--cp-space-5) var(--cp-space-7);
    border-block-end: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
    text-align: end;
  }
  .cp-table--responsive tbody td:last-child {
    border-block-end: 0;
  }
  .cp-table--responsive tbody td::before {
    content: attr(data-label);
    font-family: var(--cp-font-family-mono);
    font-size: var(--cp-font-size-10);
    letter-spacing: var(--cp-tracking-caps);
    text-transform: uppercase;
    color: var(--cp-color-fg-muted);
    text-align: start;
    flex: 0 0 auto;
  }
  /* Numeric cells stay tabular but no longer need right-align — flex handles it. */
  .cp-table--responsive tbody td.cp-num {
    text-align: end;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Dense-grid affordances — for wide operational tables (orders, inventory,
   users) that must declutter via progressive disclosure rather than show
   every column at once.

   - `.cp-table-scroll` — overflow wrapper so a wide table scrolls
     horizontally inside its container instead of blowing out the page.
   - `.cp-table--frozen-first` — pin the first column so the row's
     identity (name / id) stays visible while scrolling right.
   - `.cp-table__expand` — a per-row toggle button (chevron) that reveals…
   - `.cp-table__detail` — …a full-width detail row holding the secondary
     fields the primary columns no longer show.
   - `.cp-num--delta` — a signed numeric cell (stock movements, diffs) that
     colors positive vs negative with a directional glyph.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Scroll wrapper ───────────────────────────────────────────────────────
   Wrap a wide table in <div class="cp-table-scroll"> so it scrolls in place.
   The wrapper owns the frame — outer border, radius and background — and the
   inner table drops its own entirely. If the table kept any border it would be
   wider than the wrapper and that edge would scroll away with the content,
   leaving the frame open on the scrolled side. Add `--y` for a
   constrained-height vertical scroll that pairs with `.cp-table--sticky-head`. */
.cp-table-scroll {
  inline-size: 100%;
  max-inline-size: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
  background: var(--cp-color-bg-elevated);
  /* Subtle inset so the scroll region reads as one surface. */
  -webkit-overflow-scrolling: touch;
}
.cp-table-scroll--y {
  overflow-y: auto;
  /* Consumer sets max-block-size to taste; this is a sane default. */
  max-block-size: 70vh;
}
/* Inside a scroll wrapper the table is frameless — the wrapper draws the
   border, radius and background so the frame stays put while content scrolls. */
.cp-table-scroll > .cp-table {
  border: 0;
  border-radius: 0;
  background: transparent;
}

/* ── Frozen first column ───────────────────────────────────────────────────
   Keeps the identity column glued to the inline-start edge during
   horizontal scroll. Needs an opaque background to mask scrolled cells. */
.cp-table--frozen-first th:first-child,
.cp-table--frozen-first td:first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: var(--cp-z-raised);
  background: var(--cp-color-bg-elevated);
  /* Hairline separator so the frozen column reads as pinned. */
  border-inline-end: var(--cp-border-thin) solid var(--_cp-table-row-border);
}
/* Head's frozen corner must sit above both the sticky head and the frozen
   column so neither scrolls over it. */
.cp-table--frozen-first thead th:first-child {
  z-index: var(--cp-z-sticky);
  background: var(--_cp-table-head-bg);
}
/* The frozen cell must track the row background in every state or it reads as
   a detached column. Mirror the row rules (hover / selected / striped) against
   td:first-child, in the same source order so the cascade resolves the same
   way — but with opaque fills: the sticky cell sits over scrolled content, so
   the accent tints mix onto bg-elevated (not transparent) and the striped
   variants reuse the row's opaque values. */
.cp-table--frozen-first tbody tr:hover td:first-child,
.cp-table--frozen-first tbody tr.cp-is-hover td:first-child {
  background: var(--cp-color-bg-inset);
}
.cp-table--frozen-first tbody tr.cp-is-selected td:first-child {
  background: color-mix(in srgb, var(--cp-color-accent) 12%, var(--cp-color-bg-elevated));
}
.cp-table--frozen-first tbody tr.cp-is-selected:hover td:first-child {
  background: color-mix(in srgb, var(--cp-color-accent) 18%, var(--cp-color-bg-elevated));
}
.cp-table--frozen-first.cp-table--striped tbody tr:nth-child(odd of :not(.cp-is-selected)) td:first-child {
  background: var(--cp-color-bg-inset);
}
.cp-table--frozen-first.cp-table--striped tbody tr:hover td:first-child {
  background: color-mix(in srgb, var(--cp-color-fg) 5%, var(--cp-color-bg-inset));
}

/* ── Row expand toggle ─────────────────────────────────────────────────────
   A real <button> in a narrow leading cell. aria-expanded drives the chevron
   rotation; the consumer (or framework) shows/hides the matching detail row. */
.cp-table__expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-space-9);
  block-size: var(--cp-space-9);
  padding: 0;
  color: var(--cp-color-fg-muted);
  background: transparent;
  border: 0;
  border-radius: var(--cp-radius-xs);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    color var(--cp-duration-fast) var(--cp-ease-standard),
    background var(--cp-duration-fast) var(--cp-ease-standard);
}
.cp-table__expand::before {
  content: "";
  inline-size: 0.7em;
  block-size: 0.7em;
  background-color: currentColor;
  -webkit-mask: var(--_cp-table-chevron) center / contain no-repeat;
          mask: var(--_cp-table-chevron) center / contain no-repeat;
  transition: transform var(--cp-duration-fast) var(--cp-ease-standard);
  --_cp-table-chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M4 2.5 7.5 6 4 9.5"/></svg>');
}
.cp-table__expand:hover,
.cp-table__expand:focus-visible {
  color: var(--cp-color-fg);
  background: color-mix(in srgb, var(--cp-color-fg) 8%, transparent);
}
.cp-table__expand[aria-expanded="true"]::before {
  transform: rotate(90deg);
}
@media (prefers-reduced-motion: reduce) {
  .cp-table__expand::before { transition: none; }
}

/* ── Detail row ────────────────────────────────────────────────────────────
   A full-width row revealed under its parent. Its single <td> spans all
   columns (consumer sets colspan); the panel inside holds the secondary
   fields — typically a `.cp-disclosure` group, a description grid, etc. */
.cp-table tbody tr.cp-table__detail > td {
  padding: 0;
  background: var(--cp-color-bg-inset);
  border-block-end: var(--cp-border-hairline) solid var(--_cp-table-row-border);
}
.cp-table tbody tr.cp-table__detail:hover {
  /* The detail row is not itself interactive — don't react to hover. */
  background: transparent;
}
.cp-table__detail-panel {
  padding: var(--cp-space-7) var(--cp-space-9);
  font-size: var(--cp-font-size-12);
  color: var(--cp-color-fg-muted);
}
/* A simple label/value grid for the secondary fields inside the panel. */
.cp-table__detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--cp-space-5) var(--cp-space-9);
}
.cp-table__detail-field {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-1);
}
.cp-table__detail-label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-faint);
}
.cp-table__detail-value {
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
}

/* ── Signed delta cell ─────────────────────────────────────────────────────
   Stock movements / diffs. Pair `.cp-num--delta` with `.cp-is-pos` or
   `.cp-is-neg`; a directional glyph is added so the sign isn't color-only. */
.cp-table td.cp-num--delta {
  text-align: end;
  font-variant-numeric: tabular-nums;
  font-family: var(--cp-font-family-mono);
}
.cp-table td.cp-num--delta.cp-is-pos { color: var(--cp-color-status-success); }
.cp-table td.cp-num--delta.cp-is-neg { color: var(--cp-color-status-danger); }
.cp-table td.cp-num--delta.cp-is-pos::before {
  content: "\25B2\00A0"; /* ▲ + nbsp — direction is not color-only */
  font-size: 0.8em;
}
.cp-table td.cp-num--delta.cp-is-neg::before {
  content: "\25BC\00A0"; /* ▼ + nbsp */
  font-size: 0.8em;
}

/* ── end src/styles/components/table.css ── */
/* ── inlined from src/styles/components/telemetry.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-* — domain telemetry widgets.

   The aviation-grade readouts that turn a CubePilot GCS into an instrument
   panel. Each widget composes the primitives (`.cp-card`, `.cp-pill`,
   `.cp-icon`, `.cp-dot`) into a single meaningful cluster, surfaces a
   small consumer-facing custom-property interface (e.g. `--cp-tm-roll`,
   `--cp-tm-pitch`, `--cp-tm-pct`), and follows the conventions established
   by button.css and card.css:

   - Single root class per widget (`.cp-tm-battery`, `.cp-tm-gps`, …).
   - BEM parts (`__value`, `__bar`, `__needle`).
   - Modifier classes for semantic state (`--ok`, `--warn`, `--danger`,
     `--rtk`, `--3d`, `--2d`, `--nofix`).
   - Component-local custom properties prefixed `--_cp-tm-*` so modifiers
     override layout without rewriting it.
   - Numeric content uses `font-variant-numeric: tabular-nums` everywhere
     so values don't dance frame-to-frame.
   - All semantic colour comes from the shared status tokens. The only
     raw colour reference is the cube-orange/cube-blue in the attitude
     horizon — those are explicit aviation duotone, intentional.

   ─────────────────────────────────────────────────────────────────────── */


/* ── Shared widget chrome — applied to any .cp-tm-* root ────────────────── */

/* Every widget sits inside a `.cp-card` by default, but the root class also
   carries the in-widget grid. Authors can drop the card wrapper for embedded
   contexts; the widget will still self-contain. */

.cp-tm-battery,
.cp-tm-gps,
.cp-tm-attitude,
.cp-tm-altitude,
.cp-tm-speed,
.cp-tm-rc,
.cp-tm-mission {
  --_cp-tm-gap: var(--cp-space-7);
  --_cp-tm-accent: var(--cp-color-fg);          /* status colour for this widget */
  --_cp-tm-track: var(--cp-color-bg-inset);
  --_cp-tm-axis:  var(--cp-color-border-subtle);
  --_cp-tm-label: var(--cp-color-fg-muted);

  display: grid;
  gap: var(--_cp-tm-gap);
  min-inline-size: 0;
  color: var(--cp-color-fg);
  font-family: var(--cp-font-family-body);
  font-variant-numeric: tabular-nums;
}

/* Eyebrow label shared by every widget — mono caps, sits at the top. The
   `.cp-tm__eyebrow` part stays widget-agnostic. */
.cp-tm__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  line-height: var(--cp-line-height-snug);
}
.cp-tm__eyebrow > .cp-icon,
.cp-tm__eyebrow > svg {
  inline-size: var(--cp-font-size-14);
  block-size: var(--cp-font-size-14);
  color: currentColor;
}

/* Headline value — display face, tabular, tracking-tight. Used by the
   widgets that lead with a single big number (voltage, altitude, speed). */
.cp-tm__value {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-40);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
}
.cp-tm__unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
  margin-inline-start: var(--cp-space-2);
  vertical-align: 0.2em;     /* optical: aligns mono caps with display x-height */
}

/* Supporting stat-row grid — two-up stats below the headline. */
.cp-tm__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(7rem, 100%), 1fr));
  gap: var(--cp-space-5) var(--cp-space-9);
}
.cp-tm__stat {
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-1);
  min-inline-size: 0;
}
.cp-tm__stat-label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}
.cp-tm__stat-value {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-medium);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-battery — voltage / percentage / current / time-remaining
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-battery {
  --_cp-tm-pct: 72;                                /* 0..100 — consumer override */
  --_cp-tm-accent: var(--cp-color-status-success);

  grid-template-areas:
    "eyebrow eyebrow"
    "value   pct"
    "meter   meter"
    "stats   stats";
  grid-template-columns: 1fr auto;
  align-items: end;
}
.cp-tm-battery > .cp-tm__eyebrow { grid-area: eyebrow; }
.cp-tm-battery > .cp-tm-battery__value { grid-area: value; }
.cp-tm-battery > .cp-tm-battery__pct   { grid-area: pct; justify-self: end; }
.cp-tm-battery > .cp-tm-battery__meter { grid-area: meter; }
.cp-tm-battery > .cp-tm__stats         { grid-area: stats; }

.cp-tm-battery__value {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-40);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
}
.cp-tm-battery__value-unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  color: var(--cp-color-fg-muted);
  margin-inline-start: var(--cp-space-2);
  vertical-align: 0.4em;     /* optical: align with display cap line */
}
.cp-tm-battery__pct {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-18);
  font-weight: var(--cp-font-weight-semibold);
  color: var(--_cp-tm-accent);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--cp-tracking-normal);
}
.cp-tm-battery__pct::after {
  content: "%";
  font-size: 0.7em;
  margin-inline-start: 0.1em;
  color: var(--cp-color-fg-muted);
  font-weight: var(--cp-font-weight-medium);
}

/* The horizontal meter. A track + filled bar driven by --_cp-tm-pct. */
.cp-tm-battery__meter {
  position: relative;
  block-size: var(--cp-space-5);                   /* 10px */
  background: var(--_cp-tm-track);
  border-radius: var(--cp-radius-xs);
  overflow: hidden;
}
.cp-tm-battery__meter::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: calc(var(--_cp-tm-pct) * 1%);
  background: var(--_cp-tm-accent);
  transition: inline-size var(--cp-duration-normal) var(--cp-ease-standard),
              background var(--cp-duration-fast) var(--cp-ease-standard);
}
/* Tick marks at every 25% — hairline tabs on top of the track. Two
   sub-pixel breakpoints to draw a 1px hairline are the canonical idiom
   per SPEC §4 — optical-correction exception. */
.cp-tm-battery__meter::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg,
      transparent calc(25% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(25% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(25% + 0.5px),
      transparent calc(25% + 0.5px)),
    linear-gradient(90deg,
      transparent calc(50% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(50% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)),
    linear-gradient(90deg,
      transparent calc(75% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(75% - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc(75% + 0.5px),
      transparent calc(75% + 0.5px));
  pointer-events: none;
  mix-blend-mode: difference;
  opacity: 0.5;
}

/* Severity modifiers — flip the accent and (for danger) trigger a slow
   pulse so the operator's eye is pulled. */
.cp-tm-battery--ok       { --_cp-tm-accent: var(--cp-color-status-success); }
.cp-tm-battery--warn     { --_cp-tm-accent: var(--cp-color-status-warning); }
.cp-tm-battery--danger   { --_cp-tm-accent: var(--cp-color-status-danger); }
.cp-tm-battery--empty    {
  --_cp-tm-accent: var(--cp-color-status-danger);
  --_cp-tm-pct: 0;
}

.cp-tm-battery--danger .cp-tm-battery__meter::before {
  animation: cp-tm-pulse calc(var(--cp-duration-slow) * 5) var(--cp-ease-standard) infinite;
}

@keyframes cp-tm-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-gps — fix type / sats / hdop / lat-lon
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-gps {
  --_cp-tm-accent: var(--cp-color-status-success);
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "eyebrow pill"
    "head    head"
    "stats   stats"
    "coords  coords";
  align-items: center;
}
.cp-tm-gps > .cp-tm__eyebrow { grid-area: eyebrow; }
.cp-tm-gps > .cp-tm-gps__pill { grid-area: pill; justify-self: end; }
.cp-tm-gps > .cp-tm-gps__head { grid-area: head; }
.cp-tm-gps > .cp-tm__stats    { grid-area: stats; }
.cp-tm-gps > .cp-tm-gps__coords { grid-area: coords; }

.cp-tm-gps__head {
  display: flex;
  align-items: baseline;
  gap: var(--cp-space-5);
}
.cp-tm-gps__fix {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-32);
  line-height: var(--cp-line-height-tight);
  letter-spacing: var(--cp-tracking-tight);
  color: var(--_cp-tm-accent);
  font-variant-numeric: tabular-nums;
}
.cp-tm-gps__sats {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-14);
  color: var(--cp-color-fg-muted);
}
.cp-tm-gps__sats > b {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
}

/* Lat/lon row — mono, faint, copyable. */
.cp-tm-gps__coords {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cp-space-3) var(--cp-space-7);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-faint);
  padding-block-start: var(--cp-space-3);
  border-block-start: var(--cp-border-hairline) dashed var(--cp-color-border-subtle);
}
.cp-tm-gps__coords > span {
  font-variant-numeric: tabular-nums;
}

/* Severity modifiers. */
.cp-tm-gps--rtk    { --_cp-tm-accent: var(--cp-color-status-success); }
.cp-tm-gps--3d     { --_cp-tm-accent: var(--cp-color-status-warning); }
.cp-tm-gps--2d     { --_cp-tm-accent: var(--cp-color-status-warning); }
.cp-tm-gps--nofix  { --_cp-tm-accent: var(--cp-color-status-danger); }

/* When there's no fix the headline reads "NO FIX" — slightly smaller so
   long phrasing doesn't break the column. */
.cp-tm-gps--nofix .cp-tm-gps__fix {
  font-size: var(--cp-font-size-24);
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-attitude — artificial horizon

   The signature aviation instrument. Renders an inline SVG with:
   - sky half (top)       — cube-blue at low alpha
   - ground half (bottom) — cube-orange at low alpha
   - pitch ladder         — degree marks every 10°
   - horizon line         — full-width hairline
   - roll dial            — rotation marks around the rim
   - center reticle       — fixed cross/T at the geometric centre

   Two CSS variables drive the live position:
     --cp-tm-roll   (deg, signed; positive = right wing down)
     --cp-tm-pitch  (deg, signed; positive = nose up)

   The variables are read on the root and applied via `transform` to an
   inner `<g class="cp-tm-attitude__inner">`. The pitch translation is
   tuned so 10° = 14 viewBox units (matches the ladder spacing).
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-attitude {
  --cp-tm-roll: 0deg;
  --cp-tm-pitch: 0;            /* unitless degrees; multiplied by ladder step in math below */
  --_cp-tm-pitch-step: 1.4;    /* viewBox units per degree of pitch — keep in sync with SVG */

  position: relative;
  aspect-ratio: 1 / 1;
  inline-size: 100%;
  max-inline-size: 320px;        /* optical: prevents the instrument ballooning to card width */
  margin-inline: auto;
  border-radius: var(--cp-radius-md);
  overflow: hidden;
  background: var(--cp-color-cube-black);  /* the rim against the cockpit bezel */
  border: var(--cp-border-thin) solid var(--cp-color-border-subtle);
  display: block;
  gap: 0;                                         /* override the shared grid gap */
}

.cp-tm-attitude > svg {
  display: block;
  inline-size: 100%;
  block-size: 100%;
}

.cp-tm-attitude__inner {
  transform-origin: 50% 50%;
  transform:
    rotate(var(--cp-tm-roll))
    translateY(calc(var(--cp-tm-pitch) * var(--_cp-tm-pitch-step) * 1px));
  transition: transform var(--cp-duration-normal) var(--cp-ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .cp-tm-attitude__inner { transition: none; }
}

/* The reticle sits OUTSIDE the rotating inner group so it stays fixed
   relative to the airframe (this is the correct aviation convention:
   the world tumbles, the aircraft stays still). */
.cp-tm-attitude__reticle {
  pointer-events: none;
}

/* Eyebrow overlay — sits in the top-left corner of the instrument. */
.cp-tm-attitude__overlay {
  position: absolute;
  inset-block-start: var(--cp-space-5);
  inset-inline-start: var(--cp-space-5);
  inset-inline-end: var(--cp-space-5);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--cp-space-5);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-chalk-0);
  text-shadow: 0 0 4px color-mix(in srgb, var(--cp-color-cube-black) 64%, transparent); /* optical: keep mono readable over sky/ground */
  pointer-events: none;
  z-index: 1;
}
.cp-tm-attitude__readout {
  position: absolute;
  inset-block-end: var(--cp-space-5);
  inset-inline-start: var(--cp-space-5);
  inset-inline-end: var(--cp-space-5);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-variant-numeric: tabular-nums;
  color: var(--cp-color-chalk-0);
  text-shadow: 0 0 4px color-mix(in srgb, var(--cp-color-cube-black) 64%, transparent);
  pointer-events: none;
  z-index: 1;
}
.cp-tm-attitude__readout > span > b {
  font-weight: var(--cp-font-weight-semibold);
  color: var(--cp-color-cube-white);
}

/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-altitude — vertical altitude tape
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-altitude {
  --_cp-tm-current: 50;       /* m AGL — consumer override */
  --_cp-tm-target:  100;      /* m AGL target marker */
  --_cp-tm-max:     150;      /* tape upper bound */

  grid-template-columns: 1fr auto;
  grid-template-areas:
    "eyebrow stat"
    "tape    tape"
    "stats   stats";
}
.cp-tm-altitude > .cp-tm__eyebrow { grid-area: eyebrow; }
.cp-tm-altitude > .cp-tm-altitude__stat { grid-area: stat; justify-self: end; }
.cp-tm-altitude > .cp-tm-altitude__tape { grid-area: tape; }
.cp-tm-altitude > .cp-tm__stats { grid-area: stats; }

.cp-tm-altitude__stat {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-24);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
}
.cp-tm-altitude__stat-unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-muted);
  margin-inline-start: var(--cp-space-2);
  vertical-align: 0.3em;     /* optical: align mono caps under display cap-line */
}

.cp-tm-altitude__tape {
  position: relative;
  block-size: var(--cp-space-15);                /* 80px tall — readable, compact */
  background: var(--_cp-tm-track);
  border-radius: var(--cp-radius-xs);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  overflow: hidden;
}

/* The horizontal layout looks more natural for "altitude AGL" tile;
   we render the tape as a vertical bar with bottom-anchored fill.
   `var(--_cp-tm-current) / var(--_cp-tm-max)` is the fill ratio. */
.cp-tm-altitude__tape::before {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: calc(var(--_cp-tm-current) / var(--_cp-tm-max) * 100%);
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--_cp-tm-accent, var(--cp-color-status-info)) 60%, transparent),
    color-mix(in srgb, var(--_cp-tm-accent, var(--cp-color-status-info)) 18%, transparent)
  );
  transition: block-size var(--cp-duration-normal) var(--cp-ease-standard);
}

/* Target-altitude marker — dashed line at target/max ratio. Positioned
   absolutely from the bottom. */
.cp-tm-altitude__target {
  position: absolute;
  inset-inline: 0;
  inset-block-end: calc(var(--_cp-tm-target) / var(--_cp-tm-max) * 100% - 0.5px);
  block-size: 0;
  border-block-start: var(--cp-border-hairline) dashed var(--cp-color-fg);
  pointer-events: none;
}
.cp-tm-altitude__target::after {
  content: "TGT " attr(data-target);
  position: absolute;
  inset-inline-end: var(--cp-space-3);
  inset-block-end: 1px;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  color: var(--cp-color-fg-muted);
}

/* Current-altitude arrow on the leading edge. */
.cp-tm-altitude__cursor {
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: calc(var(--_cp-tm-current) / var(--_cp-tm-max) * 100%);
  transform: translateY(50%);
  display: flex;
  align-items: center;
  gap: var(--cp-space-2);
  padding: 1px var(--cp-space-2);
  background: var(--_cp-tm-accent, var(--cp-color-status-info));
  color: var(--cp-color-fg-on-accent);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  font-weight: var(--cp-font-weight-semibold);
  letter-spacing: var(--cp-tracking-normal);
  font-variant-numeric: tabular-nums;
  clip-path: polygon(0 50%, var(--cp-space-3) 0, 100% 0, 100% 100%, var(--cp-space-3) 100%);
  padding-inline-start: var(--cp-space-5);
  pointer-events: none;
}

/* Tick marks along the trailing edge (every 25 m by default). */
.cp-tm-altitude__ticks {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: var(--cp-space-5);
  pointer-events: none;
  background-image:
    repeating-linear-gradient(
      to top,
      transparent 0,
      transparent calc(20% - 0.5px),
      var(--cp-color-border-subtle) calc(20% - 0.5px),
      var(--cp-color-border-subtle) calc(20% + 0.5px),
      transparent calc(20% + 0.5px)
    );
}

/* Severity — danger zone (too low) — accent flips and the fill turns red. */
.cp-tm-altitude--ok      { --_cp-tm-accent: var(--cp-color-status-info); }
.cp-tm-altitude--warn    { --_cp-tm-accent: var(--cp-color-status-warning); }
.cp-tm-altitude--danger  { --_cp-tm-accent: var(--cp-color-status-danger); }


/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-speed — speedometer arc / horizontal needle gauge
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-speed {
  --_cp-tm-current: 8.4;
  --_cp-tm-max:    20;
  --_cp-tm-cruise: 12;
  --_cp-tm-accent: var(--cp-color-status-info);

  grid-template-columns: auto 1fr;
  grid-template-areas:
    "eyebrow eyebrow"
    "value   arc"
    "stats   stats";
  align-items: center;
}
.cp-tm-speed > .cp-tm__eyebrow { grid-area: eyebrow; }
.cp-tm-speed > .cp-tm-speed__value { grid-area: value; }
.cp-tm-speed > .cp-tm-speed__arc { grid-area: arc; justify-self: stretch; }
.cp-tm-speed > .cp-tm__stats { grid-area: stats; }

.cp-tm-speed__value {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-56);
  line-height: 0.9;
  letter-spacing: var(--cp-tracking-tight);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: var(--cp-space-3);
}
.cp-tm-speed__value-unit {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-medium);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}

/* The half-arc gauge. SVG inside renders the track + the colored sweep.
   Sweep length is driven by --_cp-tm-current / --_cp-tm-max. */
.cp-tm-speed__arc {
  display: block;
  inline-size: 100%;
  max-inline-size: var(--cp-space-16);            /* 96px */
  margin-inline-start: auto;
  color: var(--_cp-tm-accent);
}
.cp-tm-speed__arc svg {
  inline-size: 100%;
  block-size: auto;
  display: block;
}
.cp-tm-speed__arc-track {
  fill: none;
  stroke: var(--cp-color-border-subtle);
  stroke-width: 6;
  stroke-linecap: round;
}
.cp-tm-speed__arc-fill {
  fill: none;
  stroke: currentColor;
  stroke-width: 6;
  stroke-linecap: round;
  /* The arc length (semicircle, r=42) is ~131.95. We pre-compute via
     the inline `stroke-dasharray="132 132"` attribute on the consumer
     side; here we just animate the dashoffset for smoothness. */
  transition: stroke-dashoffset var(--cp-duration-normal) var(--cp-ease-standard);
}
.cp-tm-speed__arc-cruise {
  fill: var(--cp-color-fg-muted);
}

.cp-tm-speed--max    { --_cp-tm-accent: var(--cp-color-status-warning); }
.cp-tm-speed--over   { --_cp-tm-accent: var(--cp-color-status-danger); }


/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-rc — RC stick / channel display
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-rc {
  --_cp-tm-gap: var(--cp-space-5);
}

.cp-tm-rc__channels {
  display: grid;
  gap: var(--cp-space-4);
  grid-template-columns: 1fr;
}

.cp-tm-rc__row {
  display: grid;
  grid-template-columns: var(--cp-space-15) 1fr auto;     /* label · bar · value */
  align-items: center;
  gap: var(--cp-space-5);
}
.cp-tm-rc__label {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}
.cp-tm-rc__readout {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-semibold);
  color: var(--cp-color-fg);
  font-variant-numeric: tabular-nums;
  min-inline-size: var(--cp-space-12);                    /* prevents jitter as value changes */
  text-align: end;
}

/* Center-zeroed bar (roll, pitch, yaw). --cp-tm-rc-value is signed, -100..100. */
.cp-tm-rc__bar {
  --cp-tm-rc-value: 0;     /* signed -100..100 */
  position: relative;
  block-size: var(--cp-space-5);
  background: var(--cp-color-bg-inset);
  border-radius: var(--cp-radius-xs);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  overflow: hidden;
}
.cp-tm-rc__bar::before {
  /* Centre tick — a 1px guide at 50%. Optical exception. */
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 50%;
  inline-size: 1px;
  background: color-mix(in srgb, var(--cp-color-fg) 30%, transparent);
  margin-inline-start: -0.5px;
  z-index: 1;
}
.cp-tm-rc__bar::after {
  content: "";
  position: absolute;
  inset-block: 1px;                                       /* hairline gap from the rim */
  background: var(--cp-color-status-info);
  border-radius: var(--cp-radius-xs);
  inset-inline-start: 50%;
  inline-size: 0;
  transform: translateX(0);
  transition:
    inline-size var(--cp-duration-fast) var(--cp-ease-out),
    transform var(--cp-duration-fast) var(--cp-ease-out);
  /* The fill grows OUTWARD from the centre. Positive values fill right,
     negative fill left. We use scaleX on the positive direction and a
     negative-translate trick for the negative half. */
  inline-size: calc(abs(var(--cp-tm-rc-value)) * 0.5%);
  transform: translateX(
    calc(min(var(--cp-tm-rc-value), 0) * 1%)
  );
}

/* Throttle is unsigned 0..100 — fills from the left. */
.cp-tm-rc__bar--throttle::before { display: none; }
.cp-tm-rc__bar--throttle::after {
  inset-inline-start: 0;
  inline-size: calc(var(--cp-tm-rc-value) * 1%);
  transform: none;
  background: var(--cp-color-cube-green);
}

/* Sticks visual — a 2D pad for roll+pitch combined. Used in the
   .cp-tm-rc__sticks block. */
.cp-tm-rc__sticks {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--cp-space-7);
}
.cp-tm-rc__pad {
  --cp-tm-rc-x: 0;        /* -100..100 */
  --cp-tm-rc-y: 0;        /* -100..100 */
  position: relative;
  aspect-ratio: 1 / 1;
  background:
    linear-gradient(var(--cp-color-border-subtle), var(--cp-color-border-subtle)) center / 1px 100% no-repeat,
    linear-gradient(var(--cp-color-border-subtle), var(--cp-color-border-subtle)) center / 100% 1px no-repeat,
    var(--cp-color-bg-inset);
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
  border-radius: var(--cp-radius-sm);
}
.cp-tm-rc__pad-dot {
  position: absolute;
  /* Map signed -100..100 to 0..100% via 50% + value/2.
     Y is inverted so positive Y reads as "up" (throttle high, pitch up). */
  inset-block-start: calc(50% - var(--cp-tm-rc-y) * 0.5%);
  inset-inline-start: calc(50% + var(--cp-tm-rc-x) * 0.5%);
  inline-size: var(--cp-space-5);
  block-size: var(--cp-space-5);
  /* Re-center the dot on the target point. */
  transform: translate(-50%, -50%);
  background: var(--cp-color-accent);
  border-radius: var(--cp-radius-pill);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--cp-color-accent) 28%, transparent);
  transition:
    inset-block-start var(--cp-duration-fast) var(--cp-ease-out),
    inset-inline-start var(--cp-duration-fast) var(--cp-ease-out);
}
.cp-tm-rc__pad-label {
  position: absolute;
  inset-block-start: var(--cp-space-3);
  inset-inline-start: var(--cp-space-3);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  color: var(--cp-color-fg-muted);
}


/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-mission — mission name / phase / progress / ETA
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-mission {
  --_cp-tm-wp: 4;
  --_cp-tm-wp-total: 12;
}
.cp-tm-mission__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--cp-space-5);
}
.cp-tm-mission__title {
  font-family: var(--cp-font-family-display);
  font-size: var(--cp-font-size-20);
  font-weight: var(--cp-font-weight-regular);
  letter-spacing: var(--cp-tracking-normal);
  margin: 0;
  color: var(--cp-color-fg);
}
.cp-tm-mission__progress {
  display: grid;
  gap: var(--cp-space-3);
}
.cp-tm-mission__progress-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-muted);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
}
.cp-tm-mission__progress-row > b {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--cp-tracking-normal);
  text-transform: none;
}
.cp-tm-mission__bar {
  position: relative;
  block-size: var(--cp-space-4);
  background: var(--cp-color-bg-inset);
  border-radius: var(--cp-radius-xs);
  overflow: hidden;
  border: var(--cp-border-hairline) solid var(--cp-color-border-subtle);
}
.cp-tm-mission__bar::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: calc(var(--_cp-tm-wp) / var(--_cp-tm-wp-total) * 100%);
  background: var(--cp-color-accent);
  transition: inline-size var(--cp-duration-normal) var(--cp-ease-standard);
}
/* Waypoint pip marks — render as tiny ticks across the bar. The number of
   pips is set via --_cp-tm-wp-total. We render up to 24 pip "stops". */
.cp-tm-mission__bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(
      to right,
      transparent 0,
      transparent calc((100% / var(--_cp-tm-wp-total)) - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc((100% / var(--_cp-tm-wp-total)) - 0.5px),
      color-mix(in srgb, var(--cp-color-fg) 35%, transparent) calc((100% / var(--_cp-tm-wp-total)) + 0.5px),
      transparent calc((100% / var(--_cp-tm-wp-total)) + 0.5px)
    );
  pointer-events: none;
  mix-blend-mode: difference;
  opacity: 0.4;
}

.cp-tm-mission__eta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cp-space-3) var(--cp-space-7);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  color: var(--cp-color-fg-muted);
}
.cp-tm-mission__eta > b {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  margin-inline-start: var(--cp-space-2);
}


/* ─────────────────────────────────────────────────────────────────────────
   .cp-tm-strip — horizontal HUD-style status strip

   A compact one-line glance bar. Typically pinned to the top of a GCS
   window or the top of a Herelink screen. Composes a row of pills,
   monospace values, and an optional severity background tint.
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-strip {
  --_cp-tm-strip-accent: var(--cp-color-border-subtle);
  --_cp-tm-strip-tint: transparent;

  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cp-space-3) var(--cp-space-7);

  padding: var(--cp-space-3) var(--cp-space-7);
  background: var(--_cp-tm-strip-tint);
  border: var(--cp-border-hairline) solid var(--_cp-tm-strip-accent);
  border-radius: var(--cp-radius-sm);

  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-variant-numeric: tabular-nums;
  color: var(--cp-color-fg);
  line-height: var(--cp-line-height-tight);
}

.cp-tm-strip__sep {
  inline-size: 1px;
  block-size: 1em;
  background: var(--cp-color-border-subtle);
  margin-inline: var(--cp-space-2);
}
.cp-tm-strip__group {
  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-2);
}
.cp-tm-strip__label {
  color: var(--cp-color-fg-muted);
  letter-spacing: var(--cp-tracking-caps);
  text-transform: uppercase;
  font-size: var(--cp-font-size-10);
}
.cp-tm-strip__value {
  color: var(--cp-color-fg);
  font-weight: var(--cp-font-weight-semibold);
}
.cp-tm-strip__spacer { flex: 1 1 auto; }

.cp-tm-strip--armed {
  --_cp-tm-strip-accent: var(--cp-color-status-danger);
  --_cp-tm-strip-tint: color-mix(in srgb, var(--cp-color-status-danger) 6%, transparent);
}
.cp-tm-strip--warn {
  --_cp-tm-strip-accent: var(--cp-color-status-warning);
  --_cp-tm-strip-tint: color-mix(in srgb, var(--cp-color-status-warning) 8%, transparent);
}
.cp-tm-strip--failsafe {
  --_cp-tm-strip-accent: var(--cp-color-status-danger);
  --_cp-tm-strip-tint: color-mix(in srgb, var(--cp-color-status-danger) 12%, transparent);
  animation: cp-tm-pulse calc(var(--cp-duration-slow) * 5) var(--cp-ease-standard) infinite;
}


/* ─────────────────────────────────────────────────────────────────────────
   Surfaces — mobile (touch-bigger), embedded (mono-only, hairline).
   Applied via `data-surface` on any widget root.
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-battery[data-surface="mobile"] .cp-tm-battery__value,
.cp-tm-altitude[data-surface="mobile"] .cp-tm-altitude__stat,
.cp-tm-gps[data-surface="mobile"] .cp-tm-gps__fix {
  font-size: var(--cp-font-size-32);
}
.cp-tm-speed[data-surface="mobile"] .cp-tm-speed__value {
  font-size: var(--cp-font-size-40);
}

/* Embedded — strip the colour, fall back to mono everything. The host
   theme provides the green-on-black palette via [data-theme] tokens. */
.cp-tm-battery[data-surface="embedded"],
.cp-tm-gps[data-surface="embedded"],
.cp-tm-altitude[data-surface="embedded"],
.cp-tm-speed[data-surface="embedded"],
.cp-tm-rc[data-surface="embedded"],
.cp-tm-mission[data-surface="embedded"] {
  --_cp-tm-gap: var(--cp-space-4);
  font-family: var(--cp-font-family-mono);
}
.cp-tm-battery[data-surface="embedded"] .cp-tm-battery__value,
.cp-tm-gps[data-surface="embedded"] .cp-tm-gps__fix,
.cp-tm-altitude[data-surface="embedded"] .cp-tm-altitude__stat,
.cp-tm-speed[data-surface="embedded"] .cp-tm-speed__value,
.cp-tm-mission[data-surface="embedded"] .cp-tm-mission__title {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-20);
  letter-spacing: var(--cp-tracking-normal);
  font-weight: var(--cp-font-weight-medium);
}

.cp-tm-strip[data-surface="embedded"] {
  font-size: var(--cp-font-size-11);
  border-width: var(--cp-border-hairline);
  border-radius: var(--cp-radius-0);
  padding: var(--cp-space-2) var(--cp-space-4);
  gap: var(--cp-space-2) var(--cp-space-5);
}


/* ─────────────────────────────────────────────────────────────────────────
   Composite dashboard grid — the showpiece. A sensible default tile
   layout for combining widgets. Pure layout helper; widgets do all the
   work themselves.
   ───────────────────────────────────────────────────────────────────────── */

.cp-tm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: var(--cp-space-8);
  align-items: stretch;
}
.cp-tm-grid > .cp-card { block-size: 100%; }
.cp-tm-grid--showpiece {
  /* When the showpiece grid is used, we hint the attitude tile to span
     a 2-column slot wherever room exists. */
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.cp-tm-grid--showpiece > .cp-tm-grid__attitude {
  grid-column: span 2;
}
@media (max-width: 720px) {
  .cp-tm-grid--showpiece > .cp-tm-grid__attitude {
    grid-column: span 1;
  }
}

/* ── end src/styles/components/telemetry.css ── */
/* ── inlined from src/styles/components/toast.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-toast — transient notification.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-toast` is the card; `__icon` `__body` `__title` `__message`
     `__actions` `__close` are the parts.
   - Severity modifiers `--success` `--warning` `--danger` `--info`
     paint the left accent bar + icon. Default is neutral.
   - `.cp-toast-region` is the fixed-position viewport-edge container;
     positional modifiers `--top-end` (default) `--top-start`
     `--bottom-end` `--bottom-start` `--bottom-center` `--top-center`
     decide where the stack lives.
   - Surface variation via `data-surface`.

   Accessibility:
   - Use `role="status"` + `aria-live="polite"` for non-urgent severities
     (default, success, info, warning).
   - Use `role="alert"` + `aria-live="assertive"` for `--danger` only.
   - The dismiss `<button>` carries `aria-label="Dismiss notification"`.
   - The region uses `aria-label="Notifications"` so screen readers
     announce its landmark name.
   ───────────────────────────────────────────────────────────────────────── */

.cp-toast {
  /* Component-local custom properties so severity / surface modifiers can
     override them without rewriting layout rules. */
  --_cp-toast-bg: var(--cp-color-bg-elevated);
  --_cp-toast-fg: var(--cp-color-fg);
  --_cp-toast-border: var(--cp-color-border-subtle);
  --_cp-toast-border-width: var(--cp-border-thin);
  --_cp-toast-accent: var(--cp-color-border);
  --_cp-toast-radius: var(--cp-radius-md);
  --_cp-toast-pad-x: var(--cp-space-8);
  --_cp-toast-pad-y: var(--cp-space-7);
  --_cp-toast-gap: var(--cp-space-6);
  --_cp-toast-shadow: var(--cp-shadow-2);
  --_cp-toast-enter-x: 100%;

  position: relative;
  display: flex;
  gap: var(--_cp-toast-gap);
  align-items: flex-start;

  min-inline-size: 280px;
  max-inline-size: 420px;
  padding: var(--_cp-toast-pad-y) var(--_cp-toast-pad-x);

  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-14);
  line-height: var(--cp-line-height-snug);
  color: var(--_cp-toast-fg);

  background: var(--_cp-toast-bg);
  border: var(--_cp-toast-border-width) solid var(--_cp-toast-border);
  border-inline-start: var(--cp-border-thick) solid var(--_cp-toast-accent);
  border-radius: var(--_cp-toast-radius);
  box-shadow: var(--_cp-toast-shadow);

  /* Toasts in a region are inert by default for clicks (the region itself
     turns off pointer-events). Re-enable on the toast so its actions and
     dismiss button stay reachable. */
  pointer-events: auto;

  /* Enter animation. Reduced-motion override below kills the slide; the
     prefers-reduced-motion media query in tokens.css already collapses
     durations to 0, so this is belt-and-braces. */
  animation: cp-toast-enter var(--cp-duration-normal) var(--cp-ease-spring) both;
}

.cp-toast.cp-toast--leaving {
  animation: cp-toast-leave var(--cp-duration-normal) var(--cp-ease-in) both;
}

/* ── Parts ────────────────────────────────────────────────────────────── */

.cp-toast__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-space-9);
  block-size: var(--cp-space-9);
  border-radius: var(--cp-radius-pill);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-semibold);
  color: var(--_cp-toast-accent);
  background: color-mix(in srgb, var(--_cp-toast-accent) 14%, transparent);
}

.cp-toast__icon > svg {
  inline-size: 1em;
  block-size: 1em;
}

.cp-toast__body {
  flex: 1 1 auto;
  min-inline-size: 0; /* allow text wrapping inside flex */
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-2);
}

.cp-toast__title {
  font-size: var(--cp-font-size-14);
  font-weight: var(--cp-font-weight-semibold);
  line-height: var(--cp-line-height-tight);
  color: var(--_cp-toast-fg);
  margin: 0;
}

.cp-toast__message {
  font-size: var(--cp-font-size-12);
  font-weight: var(--cp-font-weight-regular);
  line-height: var(--cp-line-height-snug);
  color: var(--cp-color-fg-muted);
  margin: 0;
}

.cp-toast__actions {
  display: flex;
  gap: var(--cp-space-4);
  align-items: center;
  margin-block-start: var(--cp-space-4);
  flex-wrap: wrap;
}

.cp-toast__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cp-space-9);
  block-size: var(--cp-space-9);
  padding: 0;
  margin-inline-start: var(--cp-space-3);
  margin-block-start: calc(-1 * var(--cp-space-1)); /* optical: align with title cap-height */
  background: transparent;
  color: var(--cp-color-fg-muted);
  border: 0;
  border-radius: var(--cp-radius-sm);
  cursor: pointer;
  font-size: var(--cp-font-size-16);
  line-height: 1;
  transition:
    background var(--cp-duration-fast) var(--cp-ease-standard),
    color var(--cp-duration-fast) var(--cp-ease-standard);
}

.cp-toast__close:hover,
.cp-toast__close.cp-is-hover {
  background: var(--cp-color-bg-inset);
  color: var(--cp-color-fg);
}

.cp-toast__close:active,
.cp-toast__close.cp-is-active {
  background: color-mix(in srgb, var(--cp-color-fg) 12%, transparent);
}

/* ── Severity variants ────────────────────────────────────────────────── */

.cp-toast--success {
  --_cp-toast-accent: var(--cp-color-status-success);
}
.cp-toast--warning {
  --_cp-toast-accent: var(--cp-color-status-warning);
}
.cp-toast--danger {
  --_cp-toast-accent: var(--cp-color-status-danger);
}
.cp-toast--info {
  --_cp-toast-accent: var(--cp-color-status-info);
}

/* ── Region — fixed positional container ──────────────────────────────── */

.cp-toast-region {
  position: fixed;
  display: flex;
  flex-direction: column;
  gap: var(--cp-space-5);
  z-index: var(--cp-z-toast);

  /* Region itself is inert; toasts inside re-enable pointer events so they
     can be dismissed/acted on without blocking the page beneath. */
  pointer-events: none;

  /* Don't let an overly-wide max-inline-size on toasts blow out the region. */
  max-inline-size: min(420px, calc(100vw - 2 * var(--cp-space-9)));
}

/* Positional modifiers — set two edges, leave the others unset. */
.cp-toast-region--top-end {
  inset-block-start: var(--cp-space-9);
  inset-inline-end: var(--cp-space-9);
}
.cp-toast-region--top-start {
  inset-block-start: var(--cp-space-9);
  inset-inline-start: var(--cp-space-9);
}
.cp-toast-region--bottom-end {
  inset-block-end: var(--cp-space-9);
  inset-inline-end: var(--cp-space-9);
  flex-direction: column-reverse;
}
.cp-toast-region--bottom-start {
  inset-block-end: var(--cp-space-9);
  inset-inline-start: var(--cp-space-9);
  flex-direction: column-reverse;
}
.cp-toast-region--top-center {
  inset-block-start: var(--cp-space-9);
  inset-inline-start: 50%;
  transform: translateX(-50%);
  align-items: center;
}
.cp-toast-region--bottom-center {
  inset-block-end: var(--cp-space-9);
  inset-inline-start: 50%;
  transform: translateX(-50%);
  align-items: center;
  flex-direction: column-reverse;
}

/* When the region is anchored to the start edge, slide in from the start. */
.cp-toast-region--top-start > .cp-toast,
.cp-toast-region--bottom-start > .cp-toast {
  --_cp-toast-enter-x: -100%;
}
.cp-toast-region--top-center > .cp-toast,
.cp-toast-region--bottom-center > .cp-toast {
  --_cp-toast-enter-x: 0;
}

/* ── Animations — respect prefers-reduced-motion ──────────────────────── */

@keyframes cp-toast-enter {
  from {
    opacity: 0;
    transform: translateX(var(--_cp-toast-enter-x)) translateY(calc(-1 * var(--cp-space-3)));
  }
  to {
    opacity: 1;
    transform: translateX(0) translateY(0);
  }
}

@keyframes cp-toast-leave {
  from {
    opacity: 1;
    transform: translateX(0);
    max-block-size: 200px;
    margin-block: 0;
  }
  to {
    opacity: 0;
    transform: translateX(var(--_cp-toast-enter-x));
    max-block-size: 0;
    margin-block: 0;
    padding-block: 0;
    border-block-width: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cp-toast {
    animation: none;
  }
  .cp-toast.cp-toast--leaving {
    animation: none;
    opacity: 0;
  }
}

/* ── Surfaces ─────────────────────────────────────────────────────────── */

.cp-toast[data-surface="mobile"] {
  --_cp-toast-pad-x: var(--cp-space-9);
  --_cp-toast-pad-y: var(--cp-space-8);
  --_cp-toast-gap: var(--cp-space-7);
  max-inline-size: 92vw;
  font-size: var(--cp-font-size-16);
}
.cp-toast[data-surface="mobile"] .cp-toast__title {
  font-size: var(--cp-font-size-16);
}
.cp-toast[data-surface="mobile"] .cp-toast__message {
  font-size: var(--cp-font-size-14);
}

.cp-toast[data-surface="embedded"] {
  --_cp-toast-border-width: var(--cp-border-hairline);
  --_cp-toast-shadow: none;
  --_cp-toast-radius: var(--cp-radius-xs);
  --_cp-toast-pad-x: var(--cp-space-5);
  --_cp-toast-pad-y: var(--cp-space-4);
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-11);
  letter-spacing: var(--cp-tracking-wide);
}
.cp-toast[data-surface="embedded"] .cp-toast__title,
.cp-toast[data-surface="embedded"] .cp-toast__message {
  font-family: var(--cp-font-family-mono);
}
.cp-toast[data-surface="embedded"] .cp-toast__title {
  font-size: var(--cp-font-size-12);
  text-transform: uppercase;
  letter-spacing: var(--cp-tracking-caps);
}
.cp-toast[data-surface="embedded"] .cp-toast__message {
  font-size: var(--cp-font-size-10);
}

/* ── end src/styles/components/toast.css ── */
/* ── inlined from src/styles/components/tooltip.css ── */
/* ─────────────────────────────────────────────────────────────────────────
   .cp-tooltip — a small floating label bubble.

   The glanceable micro-surface: it explains a truncated cell, expands an
   abbreviation, hints "copy this UUID", or reveals what "+3 more" hides.
   Two patterns ship in this file:

   (A) .cp-tooltip — a styled bubble the consumer positions and toggles
       (with JS / a popover lib). It's an elevated bordered surface so it
       reads on BOTH themes purely through semantic tokens — no inverted
       dark/light swap needed. Placement modifiers `--top` (default)
       `--bottom` `--start` `--end` only document the intended offset
       direction AND rotate the optional `__arrow` part to point back at
       the anchor; the consumer still sets the actual coordinates.

   (B) .cp-tooltip-anchor[data-cp-tooltip="…"] — a pure-CSS, zero-JS
       hover/focus tooltip. The bubble is rendered from the attribute via
       `::after` (above the anchor), reusing pattern (A)'s look, and fades
       in on :hover / :focus-visible. Good for the simple cases where you
       don't want a popover engine.

   Architecture mirrors button.css (the canonical reference):
   - `.cp-tooltip` is the root; `__arrow` is its one part.
   - Component-private `--_cp-tooltip-*` custom properties are set at the
     root and overridden by modifiers / surfaces (the local-property
     cascade). No new public `--cp-*` knobs.
   - Semantic tokens only; light + dark flip automatically.
   - `data-surface="embedded"` mono-fies + hairlines for OLED panels.

   Accessibility:
   - Pattern (A): the consumer must wire `aria-describedby` from the
     anchoring control to the bubble's `id`, and the bubble carries
     `role="tooltip"`. The bubble itself is never focusable.
   - Pattern (B): `::after` content is decorative and is NOT announced by
     screen readers — so ALSO set a real `title` or `aria-label` on the
     anchor carrying the same text.
   - Numeric content (IDs, counts, voltages) gets tabular figures.
   ───────────────────────────────────────────────────────────────────────── */

.cp-tooltip {
  /* Component-local custom properties. Placement / surface modifiers
     override these without rewriting the layout block below. */
  --_cp-tooltip-bg: var(--cp-color-bg-elevated);
  --_cp-tooltip-fg: var(--cp-color-fg);
  --_cp-tooltip-border: var(--cp-color-border-subtle);
  --_cp-tooltip-border-width: var(--cp-border-hairline);
  --_cp-tooltip-radius: var(--cp-radius-sm);
  --_cp-tooltip-pad-x: var(--cp-space-5);
  --_cp-tooltip-pad-y: var(--cp-space-3);
  --_cp-tooltip-font-size: var(--cp-font-size-11);
  --_cp-tooltip-font-family: var(--cp-font-family-body);
  --_cp-tooltip-shadow: var(--cp-shadow-2);
  --_cp-tooltip-max-inline: 240px;
  /* The arrow is a rotated square; this is how far it pokes past the edge. */
  --_cp-tooltip-arrow-size: var(--cp-space-4);

  display: inline-flex;
  align-items: center;
  gap: var(--cp-space-3);
  inline-size: max-content;
  max-inline-size: var(--_cp-tooltip-max-inline);
  padding: var(--_cp-tooltip-pad-y) var(--_cp-tooltip-pad-x);

  font-family: var(--_cp-tooltip-font-family);
  font-size: var(--_cp-tooltip-font-size);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  letter-spacing: var(--cp-tracking-normal);
  text-align: start;
  /* Long IDs / messages wrap inside the max-inline-size instead of
     overflowing the viewport. break-word keeps unbroken UUIDs contained. */
  white-space: normal;
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;

  color: var(--_cp-tooltip-fg);
  background: var(--_cp-tooltip-bg);
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  border-radius: var(--_cp-tooltip-radius);
  box-shadow: var(--_cp-tooltip-shadow);

  /* Establish a containing block so .cp-tooltip__arrow (position:absolute)
     anchors to the bubble itself by default. A consumer that floats the bubble
     can override this with position:absolute/fixed — that still positions the
     arrow correctly; this only guards the case where they forget. */
  position: relative;

  /* The bubble floats above everything, including toasts/modals. The
     consumer's positioning (absolute/fixed) is layered here. */
  z-index: var(--cp-z-tooltip);

  /* The bubble is presentational chrome; it should never eat clicks meant
     for the content beneath it. */
  pointer-events: none;
  user-select: none;
}

/* Mono variant — for UUIDs, hashes, param names, file paths. Opt in by
   adding `.cp-tooltip--mono`; the default stays body type for prose. */
.cp-tooltip--mono {
  --_cp-tooltip-font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
}

/* ── Arrow part ───────────────────────────────────────────────────────────
   A rotated square that inherits the bubble's fill + border so it reads as
   a beak. Two of its borders are hidden per placement so only the outward
   pair shows. Default placement is --top (bubble above the anchor → beak
   points down, on the bubble's bottom edge). */

.cp-tooltip__arrow {
  position: absolute;
  inline-size: var(--_cp-tooltip-arrow-size);
  block-size: var(--_cp-tooltip-arrow-size);
  background: var(--_cp-tooltip-bg);
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  /* Inherit only the two outward-facing borders; the inward ones tuck
     under the bubble body so the seam disappears. */
  border-block-start: 0;
  border-inline-start: 0;
  transform: rotate(45deg);
  pointer-events: none;
}

/* ── Placement modifiers ──────────────────────────────────────────────────
   These document the INTENDED offset direction and rotate the arrow so it
   points back at the anchor. The consumer / popover engine still sets the
   actual top/left coordinates — these classes don't position the bubble. */

/* Top (default) — bubble above the anchor; beak hangs off the bottom edge. */
.cp-tooltip--top .cp-tooltip__arrow {
  inset-block-end: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  inset-inline-start: 50%;
  /* Shift back by half the diagonal so the rotated square stays centred. */
  margin-inline-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  transform: rotate(45deg);
}

/* Bottom — bubble below the anchor; beak sits on the top edge. */
.cp-tooltip--bottom .cp-tooltip__arrow {
  inset-block-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  inset-inline-start: 50%;
  margin-inline-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  /* Show the top-left border pair instead. */
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  border-block-end: 0;
  border-inline-end: 0;
  transform: rotate(45deg);
}

/* Start — bubble to the inline-start of the anchor; beak on the end edge. */
.cp-tooltip--start .cp-tooltip__arrow {
  inset-inline-end: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  inset-block-start: 50%;
  margin-block-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  border-block-start: 0;
  border-inline-end: 0;
  transform: rotate(45deg);
}

/* End — bubble to the inline-end of the anchor; beak on the start edge. */
.cp-tooltip--end .cp-tooltip__arrow {
  inset-inline-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  inset-block-start: 50%;
  margin-block-start: calc(-0.5 * var(--_cp-tooltip-arrow-size));
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  border-block-end: 0;
  border-inline-start: 0;
  transform: rotate(45deg);
}

/* ── Tone modifiers — for status hints (validation, danger reveals) ───────
   Tint the border + give the text the status colour. Soft fill keeps the
   bubble readable on busy surfaces. */

.cp-tooltip--success {
  --_cp-tooltip-border: var(--cp-color-status-success);
  --_cp-tooltip-fg: var(--cp-color-status-success);
}
.cp-tooltip--warning {
  --_cp-tooltip-border: var(--cp-color-status-warning);
  --_cp-tooltip-fg: var(--cp-color-status-warning);
}
.cp-tooltip--danger {
  --_cp-tooltip-border: var(--cp-color-status-danger);
  --_cp-tooltip-fg: var(--cp-color-status-danger);
}
.cp-tooltip--info {
  --_cp-tooltip-border: var(--cp-color-status-info);
  --_cp-tooltip-fg: var(--cp-color-status-info);
}

/* ── Tabular numeric content (counts, IDs) ───────────────────────────────
   Already on at the root, but expose a part hook so consumers can keep a
   "+N" / count cluster crisp even inside otherwise-prose tooltips. */
.cp-tooltip__num {
  font-variant-numeric: tabular-nums;
  font-weight: var(--cp-font-weight-semibold);
}

/* ── Surfaces ─────────────────────────────────────────────────────────────
   Embedded — OLED / terminal panels. Mono type, hairline border, no
   shadow, tighter chrome, slimmer arrow. (No "mobile" surface: a hover
   tooltip has no meaning on touch; mobile uses tap-revealed popovers.) */

.cp-tooltip[data-surface="embedded"] {
  --_cp-tooltip-font-family: var(--cp-font-family-mono);
  --_cp-tooltip-font-size: var(--cp-font-size-10);
  --_cp-tooltip-border-width: var(--cp-border-hairline);
  --_cp-tooltip-radius: var(--cp-radius-xs);
  --_cp-tooltip-pad-x: var(--cp-space-4);
  --_cp-tooltip-pad-y: var(--cp-space-2);
  --_cp-tooltip-shadow: none;
  --_cp-tooltip-arrow-size: var(--cp-space-3);
  letter-spacing: var(--cp-tracking-wide);
}

/* ─────────────────────────────────────────────────────────────────────────
   (B) .cp-tooltip-anchor — pure-CSS hover/focus tooltip.

   Put the class on a REAL interactive element (<button>, <a>, <summary>)
   and the text in `data-cp-tooltip`. The bubble is rendered via ::after,
   above the anchor, reusing pattern (A)'s styling. Set `title`/`aria-label`
   too, because ::after content is not announced.
   ───────────────────────────────────────────────────────────────────────── */

.cp-tooltip-anchor {
  /* Locals reused by ::before (arrow) + ::after (bubble) so the look stays
     in lock-step with pattern (A). */
  --_cp-tooltip-bg: var(--cp-color-bg-elevated);
  --_cp-tooltip-fg: var(--cp-color-fg);
  --_cp-tooltip-border: var(--cp-color-border-subtle);
  --_cp-tooltip-border-width: var(--cp-border-hairline);
  --_cp-tooltip-arrow-size: var(--cp-space-4);
  /* Gap between the anchor's top edge and the bubble's bottom edge. */
  --_cp-tooltip-offset: var(--cp-space-5);

  position: relative;
}

/* The bubble. Hidden until hover/focus; pulled from the attribute. */
.cp-tooltip-anchor[data-cp-tooltip]::after {
  content: attr(data-cp-tooltip);
  position: absolute;
  inset-block-end: calc(100% + var(--_cp-tooltip-offset));
  inset-inline-start: 50%;
  transform: translateX(-50%) translateY(var(--cp-space-2));

  inline-size: max-content;
  max-inline-size: 240px;
  padding: var(--cp-space-3) var(--cp-space-5);

  font-family: var(--cp-font-family-body);
  font-size: var(--cp-font-size-11);
  font-weight: var(--cp-font-weight-medium);
  line-height: var(--cp-line-height-snug);
  letter-spacing: var(--cp-tracking-normal);
  text-align: center;
  white-space: normal;
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;

  color: var(--_cp-tooltip-fg);
  background: var(--_cp-tooltip-bg);
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  border-radius: var(--cp-radius-sm);
  box-shadow: var(--cp-shadow-2);

  z-index: var(--cp-z-tooltip);
  pointer-events: none;

  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--cp-duration-fast) var(--cp-ease-out),
    transform var(--cp-duration-fast) var(--cp-ease-out),
    visibility var(--cp-duration-fast) var(--cp-ease-out);
}

/* The beak — a rotated square hanging just below the bubble. */
.cp-tooltip-anchor[data-cp-tooltip]::before {
  content: "";
  position: absolute;
  inset-block-end: calc(100% + var(--_cp-tooltip-offset)
                        - 0.5 * var(--_cp-tooltip-arrow-size));
  inset-inline-start: 50%;
  inline-size: var(--_cp-tooltip-arrow-size);
  block-size: var(--_cp-tooltip-arrow-size);
  transform: translateX(-50%) rotate(45deg) translateY(var(--cp-space-2));

  background: var(--_cp-tooltip-bg);
  border: var(--_cp-tooltip-border-width) solid var(--_cp-tooltip-border);
  /* Only the bottom-right pair shows (beak points down toward the anchor). */
  border-block-start: 0;
  border-inline-start: 0;

  z-index: var(--cp-z-tooltip);
  pointer-events: none;

  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--cp-duration-fast) var(--cp-ease-out),
    transform var(--cp-duration-fast) var(--cp-ease-out),
    visibility var(--cp-duration-fast) var(--cp-ease-out);
}

/* Reveal on hover + keyboard focus. `.cp-is-hover` is the docs force-state
   hook so the bubble can be shown in a static snapshot. */
.cp-tooltip-anchor[data-cp-tooltip]:hover::after,
.cp-tooltip-anchor[data-cp-tooltip]:focus-visible::after,
.cp-tooltip-anchor[data-cp-tooltip].cp-is-hover::after {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}
.cp-tooltip-anchor[data-cp-tooltip]:hover::before,
.cp-tooltip-anchor[data-cp-tooltip]:focus-visible::before,
.cp-tooltip-anchor[data-cp-tooltip].cp-is-hover::before {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) rotate(45deg) translateY(0);
}

/* Don't reveal an empty bubble if the attribute is blank. */
.cp-tooltip-anchor[data-cp-tooltip=""]::after,
.cp-tooltip-anchor[data-cp-tooltip=""]::before {
  content: none;
}

/* Mono anchor — UUID / hash hints render the bubble in mono. */
.cp-tooltip-anchor--mono[data-cp-tooltip]::after {
  font-family: var(--cp-font-family-mono);
  letter-spacing: var(--cp-tracking-wide);
}

/* Embedded surface for the pure-CSS anchor too. */
.cp-tooltip-anchor[data-surface="embedded"] {
  --_cp-tooltip-arrow-size: var(--cp-space-3);
}
.cp-tooltip-anchor[data-surface="embedded"][data-cp-tooltip]::after {
  font-family: var(--cp-font-family-mono);
  font-size: var(--cp-font-size-10);
  border-radius: var(--cp-radius-xs);
  box-shadow: none;
  letter-spacing: var(--cp-tracking-wide);
}
.cp-tooltip-anchor[data-surface="embedded"][data-cp-tooltip]::before {
  box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
  .cp-tooltip-anchor[data-cp-tooltip]::after,
  .cp-tooltip-anchor[data-cp-tooltip]::before {
    transition: none;
  }
}

/* ── end src/styles/components/tooltip.css ── */