/**
 * Citrine design tokens.
 *
 * Generated by packages/tokens/scripts/build-tokens.mjs from the JSON under packages/tokens/tokens/.
 * DO NOT EDIT BY HAND — edit the JSON and rebuild.
 *
 * Set data-theme="light" | "dark" on <html> (omit to follow the operating system).
 * The eleven accent/data roles below are Graphite fallbacks. Consumers are expected to override
 * all eleven in their own stylesheet loaded after citrine.css.
 */

@custom-media --citrine-bp-xs (max-width: 768px);
@custom-media --citrine-bp-sm (max-width: 820px);
@custom-media --citrine-bp-md (max-width: 900px);
@custom-media --citrine-bp-lg (max-width: 980px);
@custom-media --citrine-bp-xl (max-width: 1000px);
@custom-media --citrine-bp-2xl (max-width: 1080px);
@custom-media --citrine-bp-3xl (max-width: 1180px);
@custom-media --citrine-bp-4xl (max-width: 1200px);
@custom-media --citrine-bp-5xl (max-width: 1280px);

:root {
  color-scheme: light;

  /* ── Primitives — the raw ladders. Theme-independent: never overridden below. ── */
  --citrine-bp-xs: 768px;
  --citrine-bp-sm: 820px;
  --citrine-bp-md: 900px;
  --citrine-bp-lg: 980px;
  --citrine-bp-xl: 1000px;
  --citrine-bp-2xl: 1080px;
  --citrine-bp-3xl: 1180px;
  --citrine-bp-4xl: 1200px;
  --citrine-bp-5xl: 1280px;
  --citrine-color-neutral-light-canvas: #f5f6f9;
  --citrine-color-neutral-light-surface: #ffffff;
  --citrine-color-neutral-light-surface-2: #fafbfc;
  --citrine-color-neutral-light-surface-3: #f1f3f7;
  --citrine-color-neutral-light-line: #e8eaf0;
  --citrine-color-neutral-light-line-2: #8b94b2;
  --citrine-color-neutral-light-side: #21242c;
  --citrine-color-neutral-light-side-2: #2a2e38;
  --citrine-color-neutral-light-side-ink: #9aa1b3;
  --citrine-color-neutral-light-side-ink-2: #ffffff;
  --citrine-color-neutral-light-ink: #14171f;
  --citrine-color-neutral-light-ink-2: #474d5e;
  --citrine-color-neutral-light-ink-3: #5c6273;
  --citrine-color-neutral-light-ink-4: #656f88;
  --citrine-color-neutral-dark-canvas: #13151a;
  --citrine-color-neutral-dark-surface: #1b1e25;
  --citrine-color-neutral-dark-surface-2: #20242c;
  --citrine-color-neutral-dark-surface-3: #272b34;
  --citrine-color-neutral-dark-line: #2a2f39;
  --citrine-color-neutral-dark-line-2: #5e697d;
  --citrine-color-neutral-dark-side: #0e1014;
  --citrine-color-neutral-dark-side-2: #191c22;
  --citrine-color-neutral-dark-side-ink: #828a9c;
  --citrine-color-neutral-dark-side-ink-2: #ffffff;
  --citrine-color-neutral-dark-ink: #eef0f5;
  --citrine-color-neutral-dark-ink-2: #b7bdcb;
  --citrine-color-neutral-dark-ink-3: #9aa2b4;
  --citrine-color-neutral-dark-ink-4: #8b929f;
  --citrine-color-status-light-ok: #17a172;
  --citrine-color-status-light-ok-wash: #e2f6ef;
  --citrine-color-status-light-ok-line: #b6e5d3;
  --citrine-color-status-light-ok-deep: #0f6e4d;
  --citrine-color-status-light-warn: #c98a06;
  --citrine-color-status-light-warn-wash: #fdf1d8;
  --citrine-color-status-light-warn-line: #f2ddaa;
  --citrine-color-status-light-warn-deep: #8a5e00;
  --citrine-color-status-light-bad: #e2483f;
  --citrine-color-status-light-bad-wash: #fde9e8;
  --citrine-color-status-light-bad-line: #f7c6c3;
  --citrine-color-status-light-bad-deep: #b0271f;
  --citrine-color-status-light-info: #3479d6;
  --citrine-color-status-light-info-wash: #e6eefb;
  --citrine-color-status-light-info-line: #c2d7f4;
  --citrine-color-status-light-info-deep: #1f59a8;
  --citrine-color-status-dark-ok: #3fc79a;
  --citrine-color-status-dark-ok-wash: #11291f;
  --citrine-color-status-dark-ok-line: #1f4a38;
  --citrine-color-status-dark-ok-deep: #6fdab3;
  --citrine-color-status-dark-warn: #e0ac48;
  --citrine-color-status-dark-warn-wash: #2e2410;
  --citrine-color-status-dark-warn-line: #4e3f1e;
  --citrine-color-status-dark-warn-deep: #f0c46e;
  --citrine-color-status-dark-bad: #f4736a;
  --citrine-color-status-dark-bad-wash: #331d1c;
  --citrine-color-status-dark-bad-line: #54312e;
  --citrine-color-status-dark-bad-deep: #f79a93;
  --citrine-color-status-dark-info: #6aa5f0;
  --citrine-color-status-dark-info-wash: #16233a;
  --citrine-color-status-dark-info-line: #2a3f60;
  --citrine-color-status-dark-info-deep: #94bff5;
  --citrine-color-static-white: #ffffff;
  --citrine-duration-140: 0.14s;
  --citrine-duration-150: 0.15s;
  --citrine-easing-standard: ease;
  --citrine-z-raised: 2;
  --citrine-z-sticky: 20;
  --citrine-shadow-light-sm: 0 1px 2px rgba(20, 23, 31, 0.04), 0 4px 16px -8px rgba(20, 23, 31, 0.12);
  --citrine-shadow-light-lg: 0 8px 20px -6px rgba(20, 23, 31, 0.12), 0 30px 60px -30px rgba(20, 23, 31, 0.3);
  --citrine-shadow-dark-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 16px -8px rgba(0, 0, 0, 0.6);
  --citrine-shadow-dark-lg: 0 8px 20px -6px rgba(0, 0, 0, 0.5), 0 30px 60px -30px rgba(0, 0, 0, 0.9);
  --citrine-space-1: 1px;
  --citrine-space-2: 2px;
  --citrine-space-3: 3px;
  --citrine-space-4: 4px;
  --citrine-space-5: 5px;
  --citrine-space-6: 6px;
  --citrine-space-7: 7px;
  --citrine-space-8: 8px;
  --citrine-space-9: 9px;
  --citrine-space-10: 10px;
  --citrine-space-11: 11px;
  --citrine-space-12: 12px;
  --citrine-space-13: 13px;
  --citrine-space-14: 14px;
  --citrine-space-16: 16px;
  --citrine-space-18: 18px;
  --citrine-space-20: 20px;
  --citrine-space-22: 22px;
  --citrine-space-24: 24px;
  --citrine-space-26: 26px;
  --citrine-space-28: 28px;
  --citrine-space-32: 32px;
  --citrine-space-52: 52px;
  --citrine-space-78: 78px;
  --citrine-radius-2: 2px;
  --citrine-radius-5: 5px;
  --citrine-radius-6: 6px;
  --citrine-radius-8: 8px;
  --citrine-radius-9: 9px;
  --citrine-radius-10: 10px;
  --citrine-radius-12: 12px;
  --citrine-radius-14: 14px;
  --citrine-radius-full: 999px;
  --citrine-size-row: 52px;
  --citrine-font-family-sans: 'Segoe UI Variable Text', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --citrine-font-family-mono: ui-monospace, 'Cascadia Mono', 'SF Mono', Consolas, 'Liberation Mono', monospace;
  --citrine-font-size-10: 10px;
  --citrine-font-size-11: 11px;
  --citrine-font-size-12: 12px;
  --citrine-font-size-13: 13px;
  --citrine-font-size-14: 14px;
  --citrine-font-size-15: 15px;
  --citrine-font-size-16: 16px;
  --citrine-font-size-17: 17px;
  --citrine-font-size-18: 18px;
  --citrine-font-size-20: 20px;
  --citrine-font-size-22: 22px;
  --citrine-font-size-24: 24px;
  --citrine-font-size-26: 26px;
  --citrine-font-size-28: 28px;
  --citrine-font-size-32: 32px;
  --citrine-font-size-9p5: 9.5px;
  --citrine-font-size-10p5: 10.5px;
  --citrine-font-size-11p5: 11.5px;
  --citrine-font-size-12p5: 12.5px;
  --citrine-font-size-13p5: 13.5px;
  --citrine-font-weight-400: 400;
  --citrine-font-weight-500: 500;
  --citrine-font-weight-600: 600;
  --citrine-font-weight-650: 650;
  --citrine-font-weight-700: 700;
  --citrine-font-weight-750: 750;
  --citrine-font-weight-800: 800;
  --citrine-line-height-1: 1;
  --citrine-line-height-1p15: 1.15;
  --citrine-line-height-1p2: 1.2;
  --citrine-line-height-1p25: 1.25;
  --citrine-line-height-1p55: 1.55;

  /* ── Semantic roles. Accent/data values are the Graphite fallback; consumers override. ── */
  --citrine-appshell-column-max: 1180px;
  --citrine-appshell-column-share: 0.84;
  --citrine-appshell-navdrawer-width: var(--citrine-sidebar-width);
  --citrine-appshell-navdrawer-max: 86vw;
  --citrine-app-bottom-inset: 0px;
  --citrine-app-bottom-rise: 0px;
  --citrine-button-quiet-weight: var(--citrine-font-weight-400);
  --citrine-canvas-frame-min-height: 420px;
  --citrine-canvas-frame-reserve: 160px;
  --citrine-canvas-frame-radius: var(--citrine-r-sm);
  --citrine-canvas-frame-edge: var(--citrine-line);
  --citrine-canvas-frame-edge-width: var(--citrine-space-1);
  --citrine-canvas-frame-bg: var(--citrine-surface);
  --citrine-card-spine-width: var(--citrine-section-spine-width);
  --citrine-card-spine: var(--citrine-section-spine);
  --citrine-card-selected-ring-width: var(--citrine-space-3);
  --citrine-chart-line-width: var(--citrine-space-2);
  --citrine-chart-area-opacity: 0.22;
  --citrine-chart-tick-size: var(--citrine-font-size-11);
  --citrine-chart-bar-radius: var(--citrine-radius-6);
  --citrine-chat-wallpaper: var(--citrine-surface-2);
  --citrine-chat-pad: var(--citrine-space-16);
  --citrine-chat-gap: var(--citrine-space-8);
  --citrine-chat-radius: var(--citrine-radius-14);
  --citrine-chat-bubble-in: var(--citrine-surface);
  --citrine-chat-bubble-out: var(--citrine-accent-wash);
  --citrine-chat-bubble-ink: var(--citrine-ink);
  --citrine-chat-bubble-pad-y: var(--citrine-space-8);
  --citrine-chat-bubble-pad-x: var(--citrine-space-12);
  --citrine-chat-bubble-max: 80%;
  --citrine-chat-meta-ink: var(--citrine-ink-3);
  --citrine-chat-meta-gap: var(--citrine-space-4);
  --citrine-chat-tick-read: var(--citrine-info-deep);
  --citrine-checkbox-chip-h: 22px;
  --citrine-checkbox-chip-pad-x: var(--citrine-space-8);
  --citrine-checkbox-chip-pad-y: var(--citrine-space-2);
  --citrine-checkbox-chip-gap: var(--citrine-space-8);
  --citrine-checkbox-chip-inner-gap: var(--citrine-space-4);
  --citrine-checkbox-chip-radius: 4px;
  --citrine-checkbox-chip-size: var(--citrine-font-size-12);
  --citrine-checkbox-chip-disabled-opacity: 0.55;
  --citrine-checkbox-chip-bg: var(--citrine-surface);
  --citrine-checkbox-chip-line: var(--citrine-line-2);
  --citrine-checkbox-chip-ink: var(--citrine-ink-2);
  --citrine-checkbox-chip-bg-on: var(--citrine-accent-wash);
  --citrine-checkbox-chip-line-on: var(--citrine-accent);
  --citrine-checkbox-chip-ink-on: var(--citrine-ink);
  --citrine-checkbox-chip-focus: var(--citrine-focus-ring);
  --citrine-canvas: var(--citrine-color-neutral-light-canvas);
  --citrine-surface: var(--citrine-color-neutral-light-surface);
  --citrine-surface-2: var(--citrine-color-neutral-light-surface-2);
  --citrine-surface-3: var(--citrine-color-neutral-light-surface-3);
  --citrine-line: var(--citrine-color-neutral-light-line);
  --citrine-line-2: var(--citrine-color-neutral-light-line-2);
  --citrine-side: var(--citrine-color-neutral-light-side);
  --citrine-side-2: var(--citrine-color-neutral-light-side-2);
  --citrine-side-ink: var(--citrine-color-neutral-light-side-ink);
  --citrine-side-ink-2: var(--citrine-color-neutral-light-side-ink-2);
  --citrine-ink: var(--citrine-color-neutral-light-ink);
  --citrine-ink-2: var(--citrine-color-neutral-light-ink-2);
  --citrine-ink-3: var(--citrine-color-neutral-light-ink-3);
  --citrine-ink-4: var(--citrine-color-neutral-light-ink-4);
  --citrine-ok: var(--citrine-color-status-light-ok);
  --citrine-ok-wash: var(--citrine-color-status-light-ok-wash);
  --citrine-ok-line: var(--citrine-color-status-light-ok-line);
  --citrine-ok-deep: var(--citrine-color-status-light-ok-deep);
  --citrine-warn: var(--citrine-color-status-light-warn);
  --citrine-warn-wash: var(--citrine-color-status-light-warn-wash);
  --citrine-warn-line: var(--citrine-color-status-light-warn-line);
  --citrine-warn-deep: var(--citrine-color-status-light-warn-deep);
  --citrine-bad: var(--citrine-color-status-light-bad);
  --citrine-bad-wash: var(--citrine-color-status-light-bad-wash);
  --citrine-bad-line: var(--citrine-color-status-light-bad-line);
  --citrine-bad-deep: var(--citrine-color-status-light-bad-deep);
  --citrine-info: var(--citrine-color-status-light-info);
  --citrine-info-wash: var(--citrine-color-status-light-info-wash);
  --citrine-info-line: var(--citrine-color-status-light-info-line);
  --citrine-info-deep: var(--citrine-color-status-light-info-deep);
  --citrine-accent: #334155;
  --citrine-accent-2: #26303f;
  --citrine-accent-ink: #ffffff;
  --citrine-accent-wash: #eceef2;
  --citrine-accent-line: #ccd3dc;
  --citrine-accent-deep: #334155;
  --citrine-data: #4c7df0;
  --citrine-data-2: #3a68d8;
  --citrine-data-fill: var(--citrine-data);
  --citrine-data-wash: #e9f0fd;
  --citrine-data-line: #c6d9f9;
  --citrine-data-ink: #ffffff;
  --citrine-sh: var(--citrine-shadow-light-sm);
  --citrine-sh-lg: var(--citrine-shadow-light-lg);
  --citrine-ink-on-solid: var(--citrine-color-static-white);
  --citrine-drawer-sheet-max: 75vh;
  --citrine-drawer-sheet-radius: var(--citrine-r);
  --citrine-entrance-art-share: 55%;
  --citrine-entrance-form-width: 420px;
  --citrine-entrance-brand-bg: var(--citrine-surface);
  --citrine-entrance-plate-bg: var(--citrine-color-neutral-light-surface);
  --citrine-entrance-plate-pad-block: var(--citrine-space-4);
  --citrine-entrance-plate-pad-inline: var(--citrine-space-10);
  --citrine-entrance-plate-radius: var(--citrine-space-16);
  --citrine-entrance-plate-pad-wide: var(--citrine-space-32);
  --citrine-entrance-plate-radius-wide: var(--citrine-space-28);
  --citrine-entrance-control-height: 52px;
  --citrine-entrance-control-height-roomy: 48px;
  --citrine-entrance-radius: var(--citrine-radius-12);
  --citrine-entrance-button-weight: var(--citrine-font-weight-600);
  --citrine-floatstack-width: 300px;
  --citrine-floatstack-gap: var(--citrine-space-12);
  --citrine-floatstack-bar-clearance: 62px;
  --citrine-floatstack-button-clearance: 56px;
  --citrine-floatstack-top-clearance: var(--citrine-topbar-height);
  --citrine-grid-split-start: 9fr;
  --citrine-grid-split-end: 11fr;
  --citrine-grouphead-band-bg: color-mix(in srgb, var(--citrine-ink) 5%, transparent);
  --citrine-grouphead-band-line: var(--citrine-line);
  --citrine-grouphead-band-pad-y: var(--citrine-space-10);
  --citrine-grouphead-band-pad-x: var(--citrine-space-16);
  --citrine-grouphead-band-gap-y: var(--citrine-space-4);
  --citrine-grouphead-band-gap-x: var(--citrine-space-10);
  --citrine-grouphead-band-title-size: var(--citrine-font-size-14);
  --citrine-grouphead-band-caption-ink: var(--citrine-ink-3);
  --citrine-grow-rule-basis: var(--citrine-space-32);
  --citrine-grow-rule-min: var(--citrine-space-16);
  --citrine-grow-rule-width: var(--citrine-space-1);
  --citrine-grow-rule-color: var(--citrine-line);
  --citrine-launcher-shadow: 0 1px 1px color-mix(in srgb, currentColor 14%, transparent), 0 3px 3px color-mix(in srgb, currentColor 10%, transparent), 0 8px 12px -4px color-mix(in srgb, currentColor 16%, transparent), 0 20px 34px -18px color-mix(in srgb, currentColor 40%, transparent);
  --citrine-launcher-shadow-hover: 0 2px 2px color-mix(in srgb, currentColor 14%, transparent), 0 6px 8px color-mix(in srgb, currentColor 12%, transparent), 0 18px 26px -8px color-mix(in srgb, currentColor 22%, transparent), 0 34px 54px -20px color-mix(in srgb, currentColor 44%, transparent);
  --citrine-launcher-cell-shadow: 0 1px 1px color-mix(in srgb, currentColor 14%, transparent), 0 4px 8px -3px color-mix(in srgb, currentColor 22%, transparent);
  --citrine-launcher-radius: var(--citrine-radius-8);
  --citrine-launcher-spine-width: var(--citrine-space-4);
  --citrine-launcher-quiet-mix: 45%;
  --citrine-launcher-lift: translateY(-3px) rotate(-0.2deg);
  --citrine-launcher-lift-cell: translateY(-3px);
  --citrine-launcher-duration: 0.18s;
  --citrine-launcher-duration-cell: 0.16s;
  --citrine-launcher-easing: cubic-bezier(0.16, 1, 0.3, 1);
  --citrine-launcher-plain-edge-mix: 35%;
  --citrine-launcher-plain-hover-mix: 4%;
  --citrine-launcher-compact-cell-min: 118px;
  --citrine-launcher-compact-cell-height: 65px;
  --citrine-launcher-compact-cell-height-narrow: 62px;
  --citrine-launcher-compact-cell-figure-size: var(--citrine-font-size-22);
  --citrine-launcher-compact-cell-figure-leading: 22px;
  --citrine-launcher-compact-cell-figure-size-narrow: var(--citrine-font-size-20);
  --citrine-launcher-compact-cell-label-size: var(--citrine-font-size-12);
  --citrine-launcher-compact-cell-label-leading: 14px;
  --citrine-launcher-compact-door-figure-size: 40px;
  --citrine-launcher-compact-door-figure-leading: 38px;
  --citrine-launcher-compact-door-gap: var(--citrine-space-10);
  --citrine-launcher-compact-gap: 9px;
  --citrine-launcher-compact-cell-gap: var(--citrine-space-4);
  --citrine-launcher-compact-cell-pad-block: var(--citrine-space-4);
  --citrine-launcher-compact-tag-gap: var(--citrine-space-6);
  --citrine-launcher-compact-door-pad-top: var(--citrine-space-20);
  --citrine-launcher-compact-door-pad-bottom: var(--citrine-space-18);
  --citrine-launcher-compact-text-leading: 1.55;
  --citrine-launcher-compact-title-weight: var(--citrine-font-weight-600);
  --citrine-launcher-compact-title-gap: var(--citrine-space-10);
  --citrine-ledger-figure: 10ch;
  --citrine-ledger-sheet-bg: var(--citrine-surface);
  --citrine-ledger-sheet-edge: var(--citrine-line);
  --citrine-ledger-sheet-radius: var(--citrine-radius-8);
  --citrine-ledger-entry-pad-x: var(--citrine-space-14);
  --citrine-ledger-entry-pad-top: var(--citrine-space-4);
  --citrine-ledger-entry-pad-bottom: var(--citrine-space-10);
  --citrine-ledger-entry-gap: var(--citrine-space-4);
  --citrine-ledger-entry-reach: 44px;
  --citrine-ledger-entry-foot-size: var(--citrine-font-size-12p5);
  --citrine-ledger-entry-foot-gap-y: var(--citrine-space-4);
  --citrine-ledger-entry-foot-gap-x: var(--citrine-space-8);
  --citrine-ledger-totals-cell-min: 150px;
  --citrine-ledger-totals-gap-y: var(--citrine-space-12);
  --citrine-ledger-totals-gap-x: var(--citrine-space-24);
  --citrine-list-divided-pad: var(--citrine-space-12);
  --citrine-list-divided-line: var(--citrine-line);
  --citrine-list-item-tone-pad: var(--citrine-space-10);
  --citrine-list-item-tone-radius: var(--citrine-radius-8);
  --citrine-list-item-bad-ground: var(--citrine-bad-wash);
  --citrine-list-item-warn-ground: var(--citrine-warn-wash);
  --citrine-measure: 768px;
  --citrine-measure-field: 320px;
  --citrine-note-quiet-rule: var(--citrine-line);
  --citrine-note-quiet-rule-width: var(--citrine-space-1);
  --citrine-note-quiet-gap: var(--citrine-space-12);
  --citrine-note-quiet-icon-size: var(--citrine-font-size-14);
  --citrine-note-quiet-icon-gap: var(--citrine-space-10);
  --citrine-note-quiet-title-size: var(--citrine-font-size-14);
  --citrine-note-quiet-title-weight: var(--citrine-font-weight-700);
  --citrine-note-quiet-title-line: 1.55;
  --citrine-note-quiet-title-gap: var(--citrine-space-2);
  --citrine-page-max: 1480px;
  --citrine-pagehead-band-radius: 16px;
  --citrine-pagehead-band-bleed: var(--citrine-space-28);
  --citrine-pagehead-band-air: var(--citrine-space-14);
  --citrine-pagehead-band-pad-top: var(--citrine-space-22);
  --citrine-pagehead-band-pad-bottom: var(--citrine-space-20);
  --citrine-pagehead-band-hairline: var(--citrine-space-1);
  --citrine-pagehead-band-pad-top-narrow: var(--citrine-space-18);
  --citrine-pagehead-band-pad-x-narrow: var(--citrine-space-16);
  --citrine-pagehead-band-pad-bottom-narrow: var(--citrine-space-16);
  --citrine-page: var(--citrine-canvas);
  --citrine-field-line: var(--citrine-line);
  --citrine-control-edge: var(--citrine-ink-3);
  --citrine-focus-ring: var(--citrine-accent);
  --citrine-focus-ring-soft: var(--citrine-accent-line);
  --citrine-outline-ink: initial;
  --citrine-side-edge: var(--citrine-line-2);
  --citrine-side-hover: color-mix(in srgb, var(--citrine-side-ink-2) 6%, transparent);
  --citrine-side-current: color-mix(in srgb, var(--citrine-side-ink-2) 14%, transparent);
  --citrine-side-head: var(--citrine-side-ink);
  --citrine-side-row: color-mix(in srgb, var(--citrine-side-ink-2) 45%, var(--citrine-side-ink));
  --citrine-side-hero: var(--citrine-side-ink-2);
  --citrine-side-hero-ink: var(--citrine-side);
  --citrine-side-hero-2: color-mix(in srgb, var(--citrine-side-hero-ink) 7%, var(--citrine-side-hero));
  --citrine-side-ring: color-mix(in srgb, var(--citrine-side-ink-2) 40%, var(--citrine-side-ink));
  --citrine-h0: var(--citrine-accent);
  --citrine-h0-wash: var(--citrine-accent-wash);
  --citrine-h0-line: var(--citrine-accent-line);
  --citrine-h0-deep: var(--citrine-accent-deep);
  --citrine-h0-btn: var(--citrine-accent);
  --citrine-h0-btn-ink: var(--citrine-accent-ink);
  --citrine-h0-btn-2: var(--citrine-accent-2);
  --citrine-h1: var(--citrine-accent);
  --citrine-h1-wash: var(--citrine-accent-wash);
  --citrine-h1-line: var(--citrine-accent-line);
  --citrine-h1-deep: var(--citrine-accent-deep);
  --citrine-h1-btn: var(--citrine-accent);
  --citrine-h1-btn-ink: var(--citrine-accent-ink);
  --citrine-h1-btn-2: var(--citrine-accent-2);
  --citrine-h2: var(--citrine-accent);
  --citrine-h2-wash: var(--citrine-accent-wash);
  --citrine-h2-line: var(--citrine-accent-line);
  --citrine-h2-deep: var(--citrine-accent-deep);
  --citrine-h2-btn: var(--citrine-accent);
  --citrine-h2-btn-ink: var(--citrine-accent-ink);
  --citrine-h2-btn-2: var(--citrine-accent-2);
  --citrine-h3: var(--citrine-accent);
  --citrine-h3-wash: var(--citrine-accent-wash);
  --citrine-h3-line: var(--citrine-accent-line);
  --citrine-h3-deep: var(--citrine-accent-deep);
  --citrine-h3-btn: var(--citrine-accent);
  --citrine-h3-btn-ink: var(--citrine-accent-ink);
  --citrine-h3-btn-2: var(--citrine-accent-2);
  --citrine-h4: var(--citrine-accent);
  --citrine-h4-wash: var(--citrine-accent-wash);
  --citrine-h4-line: var(--citrine-accent-line);
  --citrine-h4-deep: var(--citrine-accent-deep);
  --citrine-h4-btn: var(--citrine-accent);
  --citrine-h4-btn-ink: var(--citrine-accent-ink);
  --citrine-h4-btn-2: var(--citrine-accent-2);
  --citrine-h5: var(--citrine-accent);
  --citrine-h5-wash: var(--citrine-accent-wash);
  --citrine-h5-line: var(--citrine-accent-line);
  --citrine-h5-deep: var(--citrine-accent-deep);
  --citrine-h5-btn: var(--citrine-accent);
  --citrine-h5-btn-ink: var(--citrine-accent-ink);
  --citrine-h5-btn-2: var(--citrine-accent-2);
  --citrine-h6: var(--citrine-accent);
  --citrine-h6-wash: var(--citrine-accent-wash);
  --citrine-h6-line: var(--citrine-accent-line);
  --citrine-h6-deep: var(--citrine-accent-deep);
  --citrine-h6-btn: var(--citrine-accent);
  --citrine-h6-btn-ink: var(--citrine-accent-ink);
  --citrine-h6-btn-2: var(--citrine-accent-2);
  --citrine-h7: var(--citrine-accent);
  --citrine-h7-wash: var(--citrine-accent-wash);
  --citrine-h7-line: var(--citrine-accent-line);
  --citrine-h7-deep: var(--citrine-accent-deep);
  --citrine-h7-btn: var(--citrine-accent);
  --citrine-h7-btn-ink: var(--citrine-accent-ink);
  --citrine-h7-btn-2: var(--citrine-accent-2);
  --citrine-h8: var(--citrine-accent);
  --citrine-h8-wash: var(--citrine-accent-wash);
  --citrine-h8-line: var(--citrine-accent-line);
  --citrine-h8-deep: var(--citrine-accent-deep);
  --citrine-h8-btn: var(--citrine-accent);
  --citrine-h8-btn-ink: var(--citrine-accent-ink);
  --citrine-h8-btn-2: var(--citrine-accent-2);
  --citrine-side-h0-dot: var(--citrine-side-ink);
  --citrine-side-h0-ink: var(--citrine-side-ink-2);
  --citrine-side-h0-current: var(--citrine-side-current);
  --citrine-side-h0-current-line: transparent;
  --citrine-side-h0-hero: var(--citrine-side-hero);
  --citrine-side-h0-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h0-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h1-dot: var(--citrine-side-ink);
  --citrine-side-h1-ink: var(--citrine-side-ink-2);
  --citrine-side-h1-current: var(--citrine-side-current);
  --citrine-side-h1-current-line: transparent;
  --citrine-side-h1-hero: var(--citrine-side-hero);
  --citrine-side-h1-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h1-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h2-dot: var(--citrine-side-ink);
  --citrine-side-h2-ink: var(--citrine-side-ink-2);
  --citrine-side-h2-current: var(--citrine-side-current);
  --citrine-side-h2-current-line: transparent;
  --citrine-side-h2-hero: var(--citrine-side-hero);
  --citrine-side-h2-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h2-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h3-dot: var(--citrine-side-ink);
  --citrine-side-h3-ink: var(--citrine-side-ink-2);
  --citrine-side-h3-current: var(--citrine-side-current);
  --citrine-side-h3-current-line: transparent;
  --citrine-side-h3-hero: var(--citrine-side-hero);
  --citrine-side-h3-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h3-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h4-dot: var(--citrine-side-ink);
  --citrine-side-h4-ink: var(--citrine-side-ink-2);
  --citrine-side-h4-current: var(--citrine-side-current);
  --citrine-side-h4-current-line: transparent;
  --citrine-side-h4-hero: var(--citrine-side-hero);
  --citrine-side-h4-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h4-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h5-dot: var(--citrine-side-ink);
  --citrine-side-h5-ink: var(--citrine-side-ink-2);
  --citrine-side-h5-current: var(--citrine-side-current);
  --citrine-side-h5-current-line: transparent;
  --citrine-side-h5-hero: var(--citrine-side-hero);
  --citrine-side-h5-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h5-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h6-dot: var(--citrine-side-ink);
  --citrine-side-h6-ink: var(--citrine-side-ink-2);
  --citrine-side-h6-current: var(--citrine-side-current);
  --citrine-side-h6-current-line: transparent;
  --citrine-side-h6-hero: var(--citrine-side-hero);
  --citrine-side-h6-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h6-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h7-dot: var(--citrine-side-ink);
  --citrine-side-h7-ink: var(--citrine-side-ink-2);
  --citrine-side-h7-current: var(--citrine-side-current);
  --citrine-side-h7-current-line: transparent;
  --citrine-side-h7-hero: var(--citrine-side-hero);
  --citrine-side-h7-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h7-hero-2: var(--citrine-side-hero-2);
  --citrine-side-h8-dot: var(--citrine-side-ink);
  --citrine-side-h8-ink: var(--citrine-side-ink-2);
  --citrine-side-h8-current: var(--citrine-side-current);
  --citrine-side-h8-current-line: transparent;
  --citrine-side-h8-hero: var(--citrine-side-hero);
  --citrine-side-h8-hero-ink: var(--citrine-side-hero-ink);
  --citrine-side-h8-hero-2: var(--citrine-side-hero-2);
  --citrine-scrollbox-sm: 192px;
  --citrine-scrollbox-md: 320px;
  --citrine-scrollbox-lg: 480px;
  --citrine-section-spine-width: var(--citrine-space-3);
  --citrine-section-spine: var(--citrine-accent);
  --citrine-showcase-font-display: var(--citrine-font-family-sans);
  --citrine-showcase-font-body: var(--citrine-font-family-sans);
  --citrine-showcase-min-height: 100vh;
  --citrine-showcase-steady-height: 100dvh;
  --citrine-showcase-steady-pad: clamp(16px, min(4cqw, 6dvh), 56px);
  --citrine-showcase-steady-logo-gap: 22px;
  --citrine-showcase-steady-gap: 48px;
  --citrine-showcase-steady-columns: minmax(0, 1.2fr) minmax(0, 400px);
  --citrine-showcase-steady-max-width: 1200px;
  --citrine-showcase-steady-left-gap: clamp(10px, 2.2dvh, 22px);
  --citrine-showcase-steady-orbit-size: min(100%, 400px, 34dvh);
  --citrine-showcase-steady-band: 6.5dvh;
  --citrine-showcase-steady-chart-height: min(120px, 14dvh);
  --citrine-showcase-canvas: var(--citrine-canvas);
  --citrine-showcase-canvas-trend: var(--citrine-surface);
  --citrine-showcase-ink: var(--citrine-ink);
  --citrine-showcase-ink-2: var(--citrine-ink-3);
  --citrine-showcase-surface: var(--citrine-surface);
  --citrine-showcase-surface-2: var(--citrine-surface-2);
  --citrine-showcase-line: var(--citrine-line);
  --citrine-showcase-wash: var(--citrine-accent-wash);
  --citrine-showcase-start: var(--citrine-accent);
  --citrine-showcase-start-2: var(--citrine-accent-2);
  --citrine-showcase-mid: var(--citrine-data-2);
  --citrine-showcase-end-deep: var(--citrine-data);
  --citrine-showcase-end: var(--citrine-data-fill);
  --citrine-showcase-won-ink: var(--citrine-data);
  --citrine-showcase-on-solid: var(--citrine-ink-on-solid);
  --citrine-showcase-ring: var(--citrine-accent-line);
  --citrine-showcase-chip-shadow: 0 8px 18px -10px rgba(10, 17, 40, 0.4);
  --citrine-showcase-card-shadow: none;
  --citrine-showcase-card-radius: 18px;
  --citrine-showcase-card-pad: var(--citrine-space-32);
  --citrine-showcase-card-pad-narrow: var(--citrine-space-24);
  --citrine-showcase-board-radius: 16px;
  --citrine-showcase-tile-radius: var(--citrine-radius-14);
  --citrine-showcase-logo-height: 48px;
  --citrine-showcase-title-size: clamp(30px, 4.4cqw, 54px);
  --citrine-showcase-title-size-funnel: clamp(28px, 3.8cqw, 46px);
  --citrine-showcase-figure-size: clamp(24px, 3cqw, 34px);
  --citrine-showcase-fade: 0.5s;
  --citrine-showcase-rise: var(--citrine-space-12);
  --citrine-showcase-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
  --citrine-showcase-eyebrow-ping: 2s;
  --citrine-showcase-bar-slide: 3s;
  --citrine-showcase-board-dwell: 1.4s;
  --citrine-showcase-board-move: 0.8s;
  --citrine-showcase-board-easing: cubic-bezier(0.7, 0, 0.3, 1);
  --citrine-showcase-board-min-height: 230px;
  --citrine-showcase-funnel-step: 0.9s;
  --citrine-showcase-funnel-band: 62px;
  --citrine-showcase-funnel-dim: 0.55;
  --citrine-showcase-orbit-spin: 40s;
  --citrine-showcase-orbit-spin-2: 26s;
  --citrine-showcase-orbit-glow: 8s;
  --citrine-showcase-orbit-beat: 2.6s;
  --citrine-showcase-trend-draw: 2.2s;
  --citrine-showcase-trend-count: 1.6s;
  --citrine-showcase-tiles-max: 480px;
  --citrine-showcase-tiles-title-leading: 1.08;
  --citrine-showcase-tiles-title-tracking: -0.02em;
  --citrine-showcase-pane-pad: 48px;
  --citrine-showcase-pane-form-pad: 40px;
  --citrine-showcase-pane-form-width: 380px;
  --citrine-sidebar-width: 236px;
  --citrine-sidebar-row-height: 36px;
  --citrine-sidebar-glyph-size: 16px;
  --citrine-sidebar-tile-size: 24px;
  --citrine-sidebar-tile-radius: var(--citrine-radius-6);
  --citrine-sidebar-tile-glyph-size: 14px;
  --citrine-sidebar-tile-bg: var(--citrine-side-2);
  --citrine-sidebar-tile-ink: var(--citrine-side-ink-2);
  --citrine-sidebar-guide: var(--citrine-side-edge);
  --citrine-sidebar-guide-width: var(--citrine-space-1);
  --citrine-sidebar-guide-perforated-width: 1.5px;
  --citrine-sidebar-guide-dash: var(--citrine-space-4);
  --citrine-sidebar-guide-gap: var(--citrine-space-3);
  --citrine-sidebar-compact-row-height: 32px;
  --citrine-sidebar-compact-row-pad: var(--citrine-space-6);
  --citrine-sidebar-compact-row-gap: 0px;
  --citrine-sidebar-compact-group-gap: var(--citrine-sp-2);
  --citrine-sidebar-compact-fold-gap: var(--citrine-sp-1);
  --citrine-sidebar-row-size: var(--citrine-font-size-14);
  --citrine-sidebar-child-row-size: var(--citrine-font-size-13);
  --citrine-signin-font: var(--citrine-font-family-sans);
  --citrine-signin-display: var(--citrine-font-family-sans);
  --citrine-signin-ink: var(--citrine-ink);
  --citrine-signin-muted: var(--citrine-ink-3);
  --citrine-signin-bg: var(--citrine-surface);
  --citrine-signin-border: var(--citrine-field-line);
  --citrine-signin-border-width: 1px;
  --citrine-signin-accent: var(--citrine-accent);
  --citrine-signin-accent-ink: var(--citrine-accent-ink);
  --citrine-signin-button: var(--citrine-accent);
  --citrine-signin-button-shadow: none;
  --citrine-signin-link: var(--citrine-accent);
  --citrine-signin-ring: rgba(51, 65, 85, 0.2);
  --citrine-signin-keyword-start: var(--citrine-accent);
  --citrine-signin-keyword-end: var(--citrine-data-fill);
  --citrine-signin-radius: 10px;
  --citrine-signin-button-radius: var(--citrine-signin-radius);
  --citrine-signin-card-radius: var(--citrine-radius-14);
  --citrine-signin-error: var(--citrine-bad);
  --citrine-signin-ok: var(--citrine-ok);
  --citrine-signin-surface: var(--citrine-surface);
  --citrine-signin-overlay: rgba(6, 10, 28, 0.62);
  --citrine-signin-dialog-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.6);
  --citrine-signin-title-size: 28px;
  --citrine-signin-rise: 0.5s;
  --citrine-signin-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
  --citrine-signin-spin: 0.7s;
  --citrine-signin-eyebrow-tracking: 0.13em;
  --citrine-signin-middle-leading: 22px;
  --citrine-signin-middle-button-height: 40px;
  --citrine-signin-middle-button-pad: 15px;
  --citrine-r: var(--citrine-radius-14);
  --citrine-r-sm: var(--citrine-radius-10);
  --citrine-r-xs: var(--citrine-radius-8);
  --citrine-r-pill: var(--citrine-radius-full);
  --citrine-sp-1: var(--citrine-space-4);
  --citrine-sp-2: var(--citrine-space-8);
  --citrine-sp-3: var(--citrine-space-12);
  --citrine-sp-4: var(--citrine-space-16);
  --citrine-sp-5: var(--citrine-space-20);
  --citrine-sp-6: var(--citrine-space-24);
  --citrine-sp-7: var(--citrine-space-28);
  --citrine-sp-8: var(--citrine-space-32);
  --citrine-row: var(--citrine-size-row);
  --citrine-target-min: 44px;
  --citrine-stackedmeter-height: var(--citrine-space-10);
  --citrine-stackedmeter-height-sm: var(--citrine-space-6);
  --citrine-stackedmeter-radius: var(--citrine-radius-full);
  --citrine-stackedmeter-track: var(--citrine-surface-2);
  --citrine-stackedmeter-track-line: var(--citrine-line);
  --citrine-stackedmeter-track-line-width: var(--citrine-space-1);
  --citrine-stackedmeter-neutral: var(--citrine-ink-4);
  --citrine-stagestrip-radius: var(--citrine-radius-12);
  --citrine-stagestrip-radius-narrow: var(--citrine-radius-8);
  --citrine-stagestrip-hairline: var(--citrine-line);
  --citrine-stagestrip-money-face: var(--citrine-data-wash);
  --citrine-stagestrip-label-tracking: 0.06em;
  --citrine-stagestrip-boxes-label-height: var(--citrine-space-18);
  --citrine-stagestrip-boxes-cell-min-width: 84px;
  --citrine-stagestrip-boxes-cell-min-height: var(--citrine-space-52);
  --citrine-stagestrip-boxes-cell-min-width-narrow: 70px;
  --citrine-stagestrip-boxes-cell-min-height-narrow: 48px;
  --citrine-stepshell-from: var(--citrine-accent);
  --citrine-stepshell-to: var(--citrine-data);
  --citrine-stepshell-lit-from: var(--citrine-stepshell-from);
  --citrine-stepshell-lit-to: var(--citrine-stepshell-to);
  --citrine-stepshell-done: var(--citrine-ok);
  --citrine-stepshell-tick: var(--citrine-surface);
  --citrine-stepshell-rest: color-mix(in srgb, var(--citrine-ink-3) 78%, var(--citrine-surface));
  --citrine-stepshell-dot: var(--citrine-space-18);
  --citrine-stepshell-dot-narrow: var(--citrine-space-16);
  --citrine-stepshell-dot-grow: var(--citrine-space-6);
  --citrine-stepshell-ring: var(--citrine-space-2);
  --citrine-stepshell-ring-now: var(--citrine-space-3);
  --citrine-stepshell-halo: var(--citrine-space-4);
  --citrine-stepshell-link: var(--citrine-space-2);
  --citrine-stepshell-link-gap: var(--citrine-space-3);
  --citrine-stepshell-tick-size: var(--citrine-space-11);
  --citrine-stepshell-name-size: var(--citrine-font-size-12);
  --citrine-stepshell-name-gap: var(--citrine-space-7);
  --citrine-stepshell-name-inset: var(--citrine-space-4);
  --citrine-stepshell-seat-min: 104px;
  --citrine-stepshell-seat-max: 176px;
  --citrine-stepshell-head-gap: var(--citrine-space-10);
  --citrine-stepshell-spine-gap: var(--citrine-space-18);
  --citrine-stepshell-tail-gap: var(--citrine-space-16);
  --citrine-stepshell-solo-gap: var(--citrine-space-18);
  --citrine-stepshell-seg-on: var(--citrine-accent);
  --citrine-stepshell-seg-rest: var(--citrine-field-line);
  --citrine-stepshell-seg-w: var(--citrine-space-22);
  --citrine-stepshell-seg-min: var(--citrine-space-12);
  --citrine-stepshell-seg-h: var(--citrine-space-4);
  --citrine-stepshell-seg-r: var(--citrine-radius-2);
  --citrine-stepshell-seg-gap: var(--citrine-space-6);
  --citrine-stepshell-top-gap: var(--citrine-space-12);
  --citrine-stepshell-top-space: var(--citrine-space-14);
  --citrine-stepshell-mark: var(--citrine-space-16);
  --citrine-stepshell-mark-ring: 1.5px;
  --citrine-stepshell-mark-gap: var(--citrine-space-12);
  --citrine-stepshell-mark-tick-size: var(--citrine-space-10);
  --citrine-stepshell-mark-rest: var(--citrine-stepshell-rest);
  --citrine-stepshell-mark-done: var(--citrine-stepshell-done);
  --citrine-table-compact-cell-pad-y: var(--citrine-space-10);
  --citrine-table-compact-cell-pad-x: var(--citrine-space-14);
  --citrine-table-card-gap: var(--citrine-space-12);
  --citrine-table-card-pad: var(--citrine-space-14);
  --citrine-table-card-radius: var(--citrine-r-sm);
  --citrine-table-card-edge: var(--citrine-line);
  --citrine-table-card-edge-width: var(--citrine-space-1);
  --citrine-table-card-cell-gap: var(--citrine-space-10);
  --citrine-table-card-label-size: var(--citrine-font-size-12);
  --citrine-table-card-label-ink: var(--citrine-ink-3);
  --citrine-table-row-highlight: var(--citrine-accent-wash);
  --citrine-tenantswitch-pill-height: 36px;
  --citrine-tenantswitch-pill-glyph-size: 28px;
  --citrine-tenantswitch-search-width: 300px;
  --citrine-tenantswitch-list-max: 320px;
  --citrine-tenantswitch-option-height: 36px;
  --citrine-toolbar-sticky-ground: var(--citrine-page);
  --citrine-toolbar-sticky-pad: var(--citrine-space-12);
  --citrine-topbar-height: 66px;
  --citrine-topbar-compact-height: 56px;
  --citrine-topbar-compact-height-narrow: 52px;
  --citrine-topbar-user-gap: var(--citrine-space-10);
  --citrine-topbar-user-name-size: var(--citrine-font-size-13p5);
  --citrine-sans: var(--citrine-font-family-sans);
  --citrine-mono: var(--citrine-font-family-mono);
}

/* No explicit choice — follow the operating system. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    color-scheme: dark;
    --citrine-canvas: var(--citrine-color-neutral-dark-canvas);
    --citrine-surface: var(--citrine-color-neutral-dark-surface);
    --citrine-surface-2: var(--citrine-color-neutral-dark-surface-2);
    --citrine-surface-3: var(--citrine-color-neutral-dark-surface-3);
    --citrine-line: var(--citrine-color-neutral-dark-line);
    --citrine-line-2: var(--citrine-color-neutral-dark-line-2);
    --citrine-side: var(--citrine-color-neutral-dark-side);
    --citrine-side-2: var(--citrine-color-neutral-dark-side-2);
    --citrine-side-ink: var(--citrine-color-neutral-dark-side-ink);
    --citrine-side-ink-2: var(--citrine-color-neutral-dark-side-ink-2);
    --citrine-ink: var(--citrine-color-neutral-dark-ink);
    --citrine-ink-2: var(--citrine-color-neutral-dark-ink-2);
    --citrine-ink-3: var(--citrine-color-neutral-dark-ink-3);
    --citrine-ink-4: var(--citrine-color-neutral-dark-ink-4);
    --citrine-ok: var(--citrine-color-status-dark-ok);
    --citrine-ok-wash: var(--citrine-color-status-dark-ok-wash);
    --citrine-ok-line: var(--citrine-color-status-dark-ok-line);
    --citrine-ok-deep: var(--citrine-color-status-dark-ok-deep);
    --citrine-warn: var(--citrine-color-status-dark-warn);
    --citrine-warn-wash: var(--citrine-color-status-dark-warn-wash);
    --citrine-warn-line: var(--citrine-color-status-dark-warn-line);
    --citrine-warn-deep: var(--citrine-color-status-dark-warn-deep);
    --citrine-bad: var(--citrine-color-status-dark-bad);
    --citrine-bad-wash: var(--citrine-color-status-dark-bad-wash);
    --citrine-bad-line: var(--citrine-color-status-dark-bad-line);
    --citrine-bad-deep: var(--citrine-color-status-dark-bad-deep);
    --citrine-info: var(--citrine-color-status-dark-info);
    --citrine-info-wash: var(--citrine-color-status-dark-info-wash);
    --citrine-info-line: var(--citrine-color-status-dark-info-line);
    --citrine-info-deep: var(--citrine-color-status-dark-info-deep);
    --citrine-accent: #93a3b8;
    --citrine-accent-2: #adbaca;
    --citrine-accent-ink: #0d1219;
    --citrine-accent-wash: #1b2330;
    --citrine-accent-line: #303c4d;
    --citrine-accent-deep: #adbaca;
    --citrine-data: #7fa6f5;
    --citrine-data-2: #9dbcf8;
    --citrine-data-wash: #152039;
    --citrine-data-line: #27395c;
    --citrine-data-ink: #0d1219;
    --citrine-sh: var(--citrine-shadow-dark-sm);
    --citrine-sh-lg: var(--citrine-shadow-dark-lg);
    --citrine-launcher-shadow: 0 1px 1px rgba(0, 0, 0, 0.6), 0 4px 6px rgba(0, 0, 0, 0.45), 0 14px 24px -8px rgba(0, 0, 0, 0.7), 0 28px 44px -20px rgba(0, 0, 0, 0.9);
    --citrine-launcher-shadow-hover: 0 2px 2px rgba(0, 0, 0, 0.65), 0 8px 12px rgba(0, 0, 0, 0.5), 0 22px 34px -8px rgba(0, 0, 0, 0.75), 0 40px 60px -20px rgba(0, 0, 0, 0.95);
    --citrine-launcher-cell-shadow: 0 1px 1px rgba(0, 0, 0, 0.55), 0 6px 12px -4px rgba(0, 0, 0, 0.7);
    --citrine-stepshell-lit-from: oklch(from var(--citrine-stepshell-from) 0.66 c h);
    --citrine-stepshell-lit-to: oklch(from var(--citrine-stepshell-to) 0.78 c h);
    --citrine-stepshell-tick: var(--citrine-canvas);
  }
}

:root[data-theme='light'] {
  color-scheme: light;
  --citrine-canvas: var(--citrine-color-neutral-light-canvas);
  --citrine-surface: var(--citrine-color-neutral-light-surface);
  --citrine-surface-2: var(--citrine-color-neutral-light-surface-2);
  --citrine-surface-3: var(--citrine-color-neutral-light-surface-3);
  --citrine-line: var(--citrine-color-neutral-light-line);
  --citrine-line-2: var(--citrine-color-neutral-light-line-2);
  --citrine-side: var(--citrine-color-neutral-light-side);
  --citrine-side-2: var(--citrine-color-neutral-light-side-2);
  --citrine-side-ink: var(--citrine-color-neutral-light-side-ink);
  --citrine-side-ink-2: var(--citrine-color-neutral-light-side-ink-2);
  --citrine-ink: var(--citrine-color-neutral-light-ink);
  --citrine-ink-2: var(--citrine-color-neutral-light-ink-2);
  --citrine-ink-3: var(--citrine-color-neutral-light-ink-3);
  --citrine-ink-4: var(--citrine-color-neutral-light-ink-4);
  --citrine-ok: var(--citrine-color-status-light-ok);
  --citrine-ok-wash: var(--citrine-color-status-light-ok-wash);
  --citrine-ok-line: var(--citrine-color-status-light-ok-line);
  --citrine-ok-deep: var(--citrine-color-status-light-ok-deep);
  --citrine-warn: var(--citrine-color-status-light-warn);
  --citrine-warn-wash: var(--citrine-color-status-light-warn-wash);
  --citrine-warn-line: var(--citrine-color-status-light-warn-line);
  --citrine-warn-deep: var(--citrine-color-status-light-warn-deep);
  --citrine-bad: var(--citrine-color-status-light-bad);
  --citrine-bad-wash: var(--citrine-color-status-light-bad-wash);
  --citrine-bad-line: var(--citrine-color-status-light-bad-line);
  --citrine-bad-deep: var(--citrine-color-status-light-bad-deep);
  --citrine-info: var(--citrine-color-status-light-info);
  --citrine-info-wash: var(--citrine-color-status-light-info-wash);
  --citrine-info-line: var(--citrine-color-status-light-info-line);
  --citrine-info-deep: var(--citrine-color-status-light-info-deep);
  --citrine-sh: var(--citrine-shadow-light-sm);
  --citrine-sh-lg: var(--citrine-shadow-light-lg);
  --citrine-launcher-shadow: 0 1px 1px color-mix(in srgb, currentColor 14%, transparent), 0 3px 3px color-mix(in srgb, currentColor 10%, transparent), 0 8px 12px -4px color-mix(in srgb, currentColor 16%, transparent), 0 20px 34px -18px color-mix(in srgb, currentColor 40%, transparent);
  --citrine-launcher-shadow-hover: 0 2px 2px color-mix(in srgb, currentColor 14%, transparent), 0 6px 8px color-mix(in srgb, currentColor 12%, transparent), 0 18px 26px -8px color-mix(in srgb, currentColor 22%, transparent), 0 34px 54px -20px color-mix(in srgb, currentColor 44%, transparent);
  --citrine-launcher-cell-shadow: 0 1px 1px color-mix(in srgb, currentColor 14%, transparent), 0 4px 8px -3px color-mix(in srgb, currentColor 22%, transparent);
  --citrine-stepshell-lit-from: var(--citrine-stepshell-from);
  --citrine-stepshell-lit-to: var(--citrine-stepshell-to);
  --citrine-stepshell-tick: var(--citrine-surface);
}

:root[data-theme='dark'] {
  color-scheme: dark;
  /* Graphite accent/data fallback; consumers override all eleven slots. */
  --citrine-canvas: var(--citrine-color-neutral-dark-canvas);
  --citrine-surface: var(--citrine-color-neutral-dark-surface);
  --citrine-surface-2: var(--citrine-color-neutral-dark-surface-2);
  --citrine-surface-3: var(--citrine-color-neutral-dark-surface-3);
  --citrine-line: var(--citrine-color-neutral-dark-line);
  --citrine-line-2: var(--citrine-color-neutral-dark-line-2);
  --citrine-side: var(--citrine-color-neutral-dark-side);
  --citrine-side-2: var(--citrine-color-neutral-dark-side-2);
  --citrine-side-ink: var(--citrine-color-neutral-dark-side-ink);
  --citrine-side-ink-2: var(--citrine-color-neutral-dark-side-ink-2);
  --citrine-ink: var(--citrine-color-neutral-dark-ink);
  --citrine-ink-2: var(--citrine-color-neutral-dark-ink-2);
  --citrine-ink-3: var(--citrine-color-neutral-dark-ink-3);
  --citrine-ink-4: var(--citrine-color-neutral-dark-ink-4);
  --citrine-ok: var(--citrine-color-status-dark-ok);
  --citrine-ok-wash: var(--citrine-color-status-dark-ok-wash);
  --citrine-ok-line: var(--citrine-color-status-dark-ok-line);
  --citrine-ok-deep: var(--citrine-color-status-dark-ok-deep);
  --citrine-warn: var(--citrine-color-status-dark-warn);
  --citrine-warn-wash: var(--citrine-color-status-dark-warn-wash);
  --citrine-warn-line: var(--citrine-color-status-dark-warn-line);
  --citrine-warn-deep: var(--citrine-color-status-dark-warn-deep);
  --citrine-bad: var(--citrine-color-status-dark-bad);
  --citrine-bad-wash: var(--citrine-color-status-dark-bad-wash);
  --citrine-bad-line: var(--citrine-color-status-dark-bad-line);
  --citrine-bad-deep: var(--citrine-color-status-dark-bad-deep);
  --citrine-info: var(--citrine-color-status-dark-info);
  --citrine-info-wash: var(--citrine-color-status-dark-info-wash);
  --citrine-info-line: var(--citrine-color-status-dark-info-line);
  --citrine-info-deep: var(--citrine-color-status-dark-info-deep);
  --citrine-accent: #93a3b8;
  --citrine-accent-2: #adbaca;
  --citrine-accent-ink: #0d1219;
  --citrine-accent-wash: #1b2330;
  --citrine-accent-line: #303c4d;
  --citrine-accent-deep: #adbaca;
  --citrine-data: #7fa6f5;
  --citrine-data-2: #9dbcf8;
  --citrine-data-wash: #152039;
  --citrine-data-line: #27395c;
  --citrine-data-ink: #0d1219;
  --citrine-sh: var(--citrine-shadow-dark-sm);
  --citrine-sh-lg: var(--citrine-shadow-dark-lg);
  --citrine-launcher-shadow: 0 1px 1px rgba(0, 0, 0, 0.6), 0 4px 6px rgba(0, 0, 0, 0.45), 0 14px 24px -8px rgba(0, 0, 0, 0.7), 0 28px 44px -20px rgba(0, 0, 0, 0.9);
  --citrine-launcher-shadow-hover: 0 2px 2px rgba(0, 0, 0, 0.65), 0 8px 12px rgba(0, 0, 0, 0.5), 0 22px 34px -8px rgba(0, 0, 0, 0.75), 0 40px 60px -20px rgba(0, 0, 0, 0.95);
  --citrine-launcher-cell-shadow: 0 1px 1px rgba(0, 0, 0, 0.55), 0 6px 12px -4px rgba(0, 0, 0, 0.7);
  --citrine-stepshell-lit-from: oklch(from var(--citrine-stepshell-from) 0.66 c h);
  --citrine-stepshell-lit-to: oklch(from var(--citrine-stepshell-to) 0.78 c h);
  --citrine-stepshell-tick: var(--citrine-canvas);
}
/**
 * @reliablesofttech/citrine-react — every component stylesheet, in one file.
 *
 * Generated by scripts/build-css.mjs. DO NOT EDIT BY HAND: edit the .css beside the component.
 *
 * Import it once, after the token sheet:
 *
 *     import '@reliablesofttech/citrine-tokens/citrine.css';
 *     import '@reliablesofttech/citrine-react/reset.css';   // optional — see below
 *     import '@reliablesofttech/citrine-react/styles.css';
 *
 * The middle line is the kit's OPT-IN global layer: body margin and canvas, the type floor
 * unclassed text falls to, the family form controls inherit, heading and paragraph margins.
 * Skip it and an application that writes no CSS of its own has no document layer at all — antd
 * injects component styles at runtime but never a `body` rule — so text falls to the browser's
 * default serif over a transparent canvas. Take it unless you already set those yourself.
 * `src/reset.css` states exactly what it does and what it deliberately leaves alone.
 *
 * Every colour, space, radius, shadow and type value below resolves through a --citrine-*
 * custom property, so restyling the kit means overriding tokens, never editing this file.
 *
 * Class names are `citrine-*` prefixed as of 0.3.0 — they are part of the public surface that
 * consuming stylesheets may target.
 */

/* ── base ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — the rules that belong to no single component.
 *
 * WHY THIS FILE EXISTS
 *
 * Citrine shipped no reset, so every hand-built (`Group B`) element inherited whatever
 * `box-sizing` the consuming application happened to set. Two consumers disagreed:
 *
 *   - one sets `*, *::before, *::after { box-sizing: border-box }` in its global stylesheet.
 *   - the other sets nothing — it deleted all 110 of its stylesheets and keeps one 65-line file.
 *
 * So the SAME component, given the SAME declared box, resolved to different sizes in the two
 * apps. Measured on the second at citrine 2.0.0-rc.7, over 228 route screenshots: `.citrine-page`
 * (`width: 100%` + 24px padding, no reset) overflowed its column by a constant **+40px at 1280
 * and +24px at 390, on 68 of 114 desktop and 71 of 114 mobile screens** — roughly 60% of the
 * application. The first app's own stylesheet carries the same bug written up from the other side.
 *
 * WHY IT IS SCOPED, AND NOT A UNIVERSAL RESET
 *
 * This mirrors what Ant Design itself does, verified in the installed source rather than the
 * docs. `antd/es/style/index.js:86-108` (`genCommonStyle`) emits, for every component:
 *
 *     [class^="ant-form"], [class*=" ant-form"] { box-sizing: border-box }   (+ ::before/::after)
 *
 * — border-box for antd's OWN elements, applied by default, and never a universal selector. A
 * render probe confirms it: after mounting Button, Input and Checkbox, antd's injected sheet
 * carries 66 `border-box` declarations across ~1,859 rules and **zero** universal-selector rules.
 * antd's universal reset (`antd/dist/reset.css`) is a separate, opt-in file.
 *
 * A library has no business changing the box model of markup it did not render, so the selector
 * below matches only elements that carry a `citrine-*` class — the kit's own DOM, plus any
 * element a consumer has deliberately given a Citrine class. It cannot reach a host app's
 * layout, and it is a no-op in an app that already sets border-box globally.
 *
 * WHAT IT CHANGES
 *
 * In an app with a global border-box: nothing. In one without: the 16 kit rules that pair a
 * declared size with padding or a border on the same axis now resolve to their declared box instead
 * of overflowing it. The loudest are `.citrine-page` (−48px wide), `.citrine-company-switch`
 * (−24px), the collapsed `.citrine-sidehero` (62px → 38px square), `.citrine-wfnode` (−34px) and
 * `.citrine-bottombar` (−21px tall). Every one of those is the value the rule already declares.
 *
 * `.citrine-btn` and `.citrine-spinner` already declared `box-sizing: border-box` themselves and
 * are unaffected; so are the classes Citrine puts onto real antd elements (`.citrine-ctrl`,
 * `.citrine-check-box`, `.citrine-radio-dotring`, `.citrine-iconbtn`), which antd had already
 * made border-box. This rule is what covers the same classes when a CONSUMER puts them on bare
 * markup — one consumer does exactly that with `className="citrine-ctrl"` on two raw `<input>`s.
 *
 * `scripts/build-css.mjs` concatenates this file FIRST, ahead of every component sheet, so a
 * component that wants a different box model can still say so and win on source order. */

[class^='citrine-'],
[class*=' citrine-'],
[class^='citrine-']::before,
[class^='citrine-']::after,
[class*=' citrine-']::before,
[class*=' citrine-']::after {
  box-sizing: border-box;
}

/* THE TRIGGER ANCHOR — shared by Menu, Popover and Tooltip (`src/utils/triggerAnchor.tsx`).
 *
 * The span those three wrap around a trigger child that cannot take a ref, so the popup has a DOM
 * node to align to. It must draw a real box: `display: contents` would leave it with no rect, which
 * is the very failure it exists to close (antd parks a popup with no target at -1000vw). `inline-flex`
 * gives it the box of the control it holds and keeps that control on the text baseline, where it sat
 * before it was wrapped. It is never emitted around a child whose ref reaches the DOM — every Citrine
 * control forwards one — so it changes no existing layout. */
.citrine-trigger-anchor {
  display: inline-flex;
  max-width: 100%;
}

/* ── AppShell ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — AppShell. Source: artifact `.app` + `.main` blocks (citrine-reference.css).
   Structure only below — the one colour (`--citrine-app-canvas`) is seeded inline from
   `theme.useToken()` in AppShell.tsx, not declared here. See the note there. */

.citrine-app {
  display: flex;
  min-height: 100vh;
  background: var(--citrine-app-canvas);
}

/* THE SIDE PADDING IS A CONTRACT: `--citrine-app-gutter`. Anything that has to span the full width
   of the page area from inside it — a sticky action bar — bleeds by it with
   `margin-inline: calc(-1 * var(--citrine-app-gutter, 0px))`. It must reach a descendant as a
   LENGTH, which is why it is registered: an unregistered custom property inherits as text, and a
   percentage or a container unit in it would re-resolve against whatever element reads it. As a
   `<length>` it is computed where it is set, once, to pixels.

   Whoever pads the page's sides sets it. In the default `layout="full"` that is `Page` (see
   `Page.css`), and `<main>` itself leaves it at 0. In `layout="column"` it is `<main>`, below. */
@property --citrine-app-gutter {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

.citrine-app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.citrine-app-content {
  flex: 1;
  min-width: 0;
}

/* ── layout="column": THE CENTRED COLUMN (opt-in) ──────────────────────────────────────────────
   On a desk the page area is a column, min(column-max, column-share of the content width), centred
   — and the content element's own PADDING is the column, rather than every child centring itself. A
   child that centres itself with `margin-inline: auto` does nothing if it is inline-level: an
   `inline-flex` row of tabs would sit to the left of every block sibling. Inside the padding, block,
   inline and floated alike are in the column because the column is the content box.

   The width it is computed from is the frame's: `.citrine-app-main` becomes the `citrine-app`
   container, the content area beside the rail. Only in this layout — a size container is also a
   containing block for fixed-position descendants, which the default layout does not impose.

   The phone is untouched: no column, and a gutter of 0 here (`Page` supplies its own below the
   breakpoint). */
.citrine-app-main:has(> .citrine-app-content-column) {
  container: citrine-app / inline-size;
}

.citrine-app-content-column {
  --citrine-app-gutter: calc(
    (100cqi - min(var(--citrine-appshell-column-max), var(--citrine-appshell-column-share) * 100cqi)) / 2
  );
  padding-inline: var(--citrine-app-gutter);
}

@media (max-width: 900px) {
  .citrine-app-content-column {
    --citrine-app-gutter: 0px;
  }
}

/* Inside the column, on a desk, a `Page` adds no side padding and no cap of its own — two would
   stack and a heading would miss the column's edge — and its gutter is the column's. Said through
   `Page`'s own hooks rather than a selector for its class, so each sheet styles only its own classes.
   `var(--citrine-app-gutter)` resolves HERE, to the column's pixels, and reaches the page as that. */
@media not all and (max-width: 900px) {
  .citrine-app-content-column {
    --citrine-page-pad-inline: 0px;
    --citrine-page-gutter: var(--citrine-app-gutter);
    --citrine-page-max: none;
  }
}

/* The skip link's target, not a Tab stop. Focused only by the skip link, where a ring around the
   whole page would say nothing the move itself has not. */
.citrine-app-content:focus {
  outline: none;
}

/* ── The skip link ────────────────────────────────────────────────────────────────────────────
   Off screen until it has keyboard focus, then a solid action-ink pill in the top-left corner, above
   the sticky rail and top bar. Moved with a transform rather than clipped, so it reads as arriving
   rather than as appearing from nowhere. */
.citrine-skiplink {
  position: fixed;
  top: var(--citrine-sp-3);
  left: var(--citrine-sp-3);
  z-index: calc(var(--citrine-z-sticky) + 2);
  /* It lands on the rail's brand row, so it is as wide as that row — the rail
     (`--citrine-sidebar-width`) less its two 12px insets — and as tall as the hero. Shorter, it left
     the tail of the brand name showing past its edge like a stray glyph (finish review, 2026-09-11). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: calc(var(--citrine-sidebar-width) - 2 * var(--citrine-sp-3));
  min-height: 40px;
  padding: var(--citrine-sp-2) var(--citrine-sp-4);
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-accent);
  color: var(--citrine-accent-ink);
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-600);
  text-decoration: none;
  transform: translateY(calc(-100% - var(--citrine-sp-4)));
}

/* The ring sits INSIDE the pill, in the pill's own ink, so it shows wherever the pill lands — on the
   navy rail, where an accent ring outside it was navy on navy, and on a white top bar alike. */
.citrine-skiplink:focus-visible {
  transform: none;
  outline: 2px solid var(--citrine-accent-ink);
  outline-offset: -5px;
}

/* PINNED TO THE BOTTOM OF THE SCREEN, not to the bottom of the page.
   As a plain flex item this sat below the content — on a long page that put it a screen and a half
   under the fold, so the only navigation a phone has was reachable only by scrolling to the very end
   of whatever you were reading. Measured on a 844px viewport: the bar's top was at y=2073.

   `sticky`, NOT `fixed`. A fixed bar leaves the flow, so it hangs OVER the last band of content and
   every page then owes it a matching `padding-bottom` — a debt each consumer pays separately and one
   of them always forgets. Sticky keeps the element's box in the flow (the space is reserved, nothing
   is ever covered at the end of a page) while painting it against the viewport for the rest.

   The containing block is `.citrine-app-main`, which spans the whole page, so it is stuck from the
   first pixel of scroll to the last. Nothing between here and there may set `overflow` to anything
   but `visible` — an ancestor scrollport would re-anchor the stickiness to itself and silently undo
   this. That is why `.citrine-app-content` has no overflow of its own.

   Same z as `Topbar`, which is the other chrome pinned against the viewport. It never competes with
   the sidebar's `+1`: the bar is only shown where the rail is not (see the swap below). */
.citrine-app-mobile-nav {
  display: none;
  flex: none;
  position: sticky;
  bottom: 0;
  z-index: var(--citrine-z-sticky);
}

/* ── THE SWAP: `navSwitch` ──────────────────────────────────────────────────────────────────────
   The rail and the small-screen bar are this frame's slots, so the frame decides which is on screen;
   neither `Sidebar` nor `BottomBar` hides itself. At the switch width the rail's slot goes and the
   bar's slot comes, so exactly one of the two is on screen at any width:

     navSwitch="md"    (default) at `--citrine-bp-md`, 900px
     navSwitch="sm"    at `--citrine-bp-sm`, 820px
     navSwitch="never" no swap: the rail stays at every width, and `mobileNav` is never rendered

   A CONTRACT: a consumer's regression helper picks which navigation to drive by which one is visible
   at a given width, so the width each value swaps at is an interface. Media queries cannot read
   `var()`, so each value is its own block over the build-time `@custom-media` breakpoint.

   The rail's slot is `display: contents`, so the rail stays a flex item of `.citrine-app` exactly as
   if it had no wrapper — its sticky position and width are its own.

   THE HERO'S RISE IS RESERVED TOO (finish review, 2026-09-11). Sticky reserves the bar's own box and
   nothing above it, but `BottomBar`'s hero stands out of the top of that box: its foot 29px up (2.1),
   60px tall and a 5px ring, against a bar about 65px tall — 24px of circle above the bar, 29px with
   the ring. At the end of a short page it landed on the last card. A top MARGIN reserves the rise:
   never painted and never catching a tap, so while the bar is stuck mid-scroll it costs nothing. Only
   where the bar is shown, and only when it carries a hero. FITTING CONSTANT — it follows
   `.citrine-bottombar-hero`; re-measure if that rule's `bottom`, `height` or ring change.

   THE BAR'S MEASURE, HANDED OVER (for `StepShell tailOffset="bottombar"`). Anything in the page that
   pins itself to the foot of the screen has to stand on the bar, not under it, and it must not learn
   that from a selector on the bar's classes. So the frame exposes two declared tokens, both 0px by
   default and wherever the bar is not shown:
     `--citrine-app-bottom-inset` — the bar slot's whole box, measured in `AppShell.tsx` (why
                                   measured: see `useBottomInset`), safe-area padding included;
     `--citrine-app-bottom-rise`  — the hero's rise above that box: the SAME reserve as the margin
                                   below, set here on the frame so it reaches `<main>`, the slot's
                                   sibling (custom properties only flow down). */
.citrine-app-rail {
  display: contents;
}

@media (max-width: 900px) {
  .citrine-app-nav-md > .citrine-app-rail {
    display: none;
  }

  .citrine-app-nav-md .citrine-app-mobile-nav {
    display: block;
  }

  .citrine-app-nav-md .citrine-app-mobile-nav:has(.citrine-bottombar-hero) {
    margin-top: var(--citrine-bottombar-hero-rise, var(--citrine-sp-8));
  }

  .citrine-app-nav-md:has(> .citrine-app-main > .citrine-app-mobile-nav .citrine-bottombar-hero) {
    --citrine-app-bottom-rise: var(--citrine-bottombar-hero-rise, var(--citrine-sp-8));
  }
}

@media (max-width: 820px) {
  .citrine-app-nav-sm > .citrine-app-rail {
    display: none;
  }

  .citrine-app-nav-sm .citrine-app-mobile-nav {
    display: block;
  }

  .citrine-app-nav-sm .citrine-app-mobile-nav:has(.citrine-bottombar-hero) {
    margin-top: var(--citrine-bottombar-hero-rise, var(--citrine-sp-8));
  }

  .citrine-app-nav-sm:has(> .citrine-app-main > .citrine-app-mobile-nav .citrine-bottombar-hero) {
    --citrine-app-bottom-rise: var(--citrine-bottombar-hero-rise, var(--citrine-sp-8));
  }
}

/* A `BottomBar hideOnScroll` that has tucked itself away is not standing at the foot of the screen,
   so nothing pinned there (`StepShell tailOffset="bottombar"`) should keep standing on it. The bar's
   box has not changed — it slid by a transform — so the measured inset would otherwise hold the tail
   a bar's height above an empty edge. `!important` because `useBottomInset` writes the inset INLINE
   on the frame; a stylesheet rule beats an inline one only that way. Focus inside the bar brings it
   back, and the tail with it. */
.citrine-app-nav-md:has(
  > .citrine-app-main > .citrine-app-mobile-nav .citrine-bottombar.citrine-is-tucked:not(:focus-within)
),
.citrine-app-nav-sm:has(
  > .citrine-app-main > .citrine-app-mobile-nav .citrine-bottombar.citrine-is-tucked:not(:focus-within)
) {
  --citrine-app-bottom-inset: 0px !important;
  --citrine-app-bottom-rise: 0px !important;
}

/* ── The phone drawer (`navDrawerOpen`) ───────────────────────────────────────────────────────
   The drawer's body is a full-height column, so the rail inside fills it top to bottom and its own
   body band is the one thing that scrolls. The drawer's width is `--citrine-appshell-navdrawer-width`
   (the rail's) capped at `-max` of the window, set on antd's panel by `AppShell.tsx`. */
.citrine-app-navdrawer-body {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* ── Avatar ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Avatar styles. Source: artifact `.avatar` / `.av` / `.rowav` (citrine-reference.css
   167, 187, 204, 407). Colours are set inline per-instance (deterministic-from-name), so this
   file only carries shape/typography. */
.citrine-avatar {
  font-weight: var(--citrine-font-weight-700);
  font-size: var(--citrine-font-size-12);
}
/* Group A audit (RULING D11, 2026-09-10): `.citrine-avatar-square` used to carry
   `border-radius: var(--citrine-r-sm) !important`, and its own comment said what that was for —
   an `!important` written so "our 10px Citrine radius wins" over antd's own square-shape rule.
   That is a brand radius painted straight onto the antd element, the same species D10 removed from
   `Segmented`, so it is gone: `shape="rounded-square"` now renders antd's square avatar at antd's
   own radius. The class is still emitted — it is a public styling hook and `Avatar.test.tsx:22`
   asserts its presence — it simply carries no declaration of its own any more.

   The two rules above are NOT in the same category and were left alone deliberately: they set the
   initials' weight and size, which antd derives from the avatar's pixel size, so removing them
   changes how initials fit inside every avatar size. That is a judgement call for the owner, and
   it is written up in the Group A audit rather than guessed at here. */

/* ── Badge ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Badge styles. Source: artifact `.badge` family (citrine-reference.css 368-370). */
.citrine-badge {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--citrine-space-6);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-data);
  color: var(--citrine-data-ink);
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-700);
  font-variant-numeric: tabular-nums;
}
.citrine-badge-accent {
  background: var(--citrine-accent);
  color: var(--citrine-accent-ink);
}
.citrine-badge-quiet {
  background: var(--citrine-surface-3);
  color: var(--citrine-ink-3);
}

/* ── BottomBar ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — BottomBar. Source: the navigation artifact `.nb-bar` block.

   Group C (2.0): structure unchanged — a raised-hero mobile tab bar has no antd equivalent.
   Colours and metrics come from antd's live theme via `antdTheme.useToken()` in `BottomBar.tsx`,
   handed down as `--citrine-bottombar-*` custom properties. Every fallback is the Citrine token the
   rule used to read.

   2.1: a `seats` layout (five fixed seats, the hero always centred), the hero INSIDE its cell so the
   DOM reads in screen order and the circle rises with a notched phone's inset, a pill behind the
   current slot's glyph, and a focus ring. See the header of `BottomBar.tsx`.

   ── What deliberately did NOT move ──────────────────────────────────────────────────────────────

   · Where the bar shows. The bar never hides itself; the app frame shows its slot below the
     `AppShell navSwitch` width (900px by default) and hides the rail there. That width is a
     CONTRACT — a consumer's regression helper picks which navigation to drive by which one is
     visible — and it lives in `AppShell.css`, which owns both slots. Treat it as an interface.
   · `min-height: 44px` on a slot — WCAG 2.5.5 target size. This is the one surface that is only
     ever touched.
   · `line-height: 1.25` on a slot. Here the line box is not typography, it is the RESERVED HEIGHT
     that keeps a bar mixing labelled and unlabelled slots on one baseline.
   · The hero's geometry — a 60px circle whose foot sits 26px above the bar's bottom edge, the 5px
     cut-out ring, the 27px glyph — and the bar's own 62px minimum and 24px icon box. All shape, not
     brand. AppShell reserves the circle's rise from these numbers (`AppShell.css`).

   ── The three type sizes are FITTING CONSTANTS, not brand — owner ruling, 2026-09-10 ────────────

   10.5px on the labels and 9.5px on the badge are NOT a Citrine type choice. They are the sizes at
   which this bar's text fits the cells the bar computes, and antd has no token that small — its
   floor is `fontSizeSM`, 12px stock. A 16-character label is ~93px of text at 10.5px and ~101px at
   12px against a 98px cell. The badge is tighter still: a 16px pill with a 1.6px ring leaves ~12.8px
   of inner box. PLEASE DO NOT "FIX" THEM ONTO A TOKEN. Where a value exists to make text fit a
   computed cell, it is a constraint, not a skin. */

/* Always drawn where it is placed: whether it is on screen at a given width is the app frame's
   decision (`AppShell navSwitch`), not the bar's. */
.citrine-bottombar {
  display: block;
  position: relative;
  background: var(--citrine-bottombar-bg, var(--citrine-surface));
  border-top: var(--citrine-bottombar-line-width, 1px) solid
    var(--citrine-bottombar-line, var(--citrine-line));
  flex: 0 0 auto;
  padding: var(--citrine-bottombar-pad-top, var(--citrine-sp-2)) 0
    var(--citrine-bottombar-pad-bottom, var(--citrine-sp-3));
  min-height: 62px;
  /* Clears the home indicator / gesture bar on a notched phone. Falls back to 0 everywhere else. The
     cells sit ABOVE this padding, so the hero, placed against its cell, rises with it. */
  padding-bottom: calc(
    var(--citrine-bottombar-pad-bottom, var(--citrine-sp-3)) + env(safe-area-inset-bottom, 0px)
  );
}

/* ── `hideOnScroll` ─────────────────────────────────────────────────────────────────────────────
   Tucked, the bar slides down by its own height PLUS the hero's rise, so the raised circle leaves
   the screen with it rather than being left hanging at the edge. A transform, never a change of box:
   the bar's sticky box stays in the flow, so the page underneath does not jump as it goes.

   Keyboard focus inside the bar always brings it back — a control you are on must be on screen.
   Under reduced motion the bar still tucks, without the slide. */
.citrine-bottombar--hides {
  transition: transform calc(var(--citrine-duration-140, 0.14s) + 80ms) var(--citrine-easing-standard, ease);
  will-change: transform;
}

.citrine-bottombar--hides.citrine-is-tucked {
  transform: translateY(calc(100% + var(--citrine-bottombar-hero-rise, var(--citrine-sp-8, 32px))));
}

.citrine-bottombar--hides.citrine-is-tucked:focus-within {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .citrine-bottombar--hides {
    transition: none;
  }
}

/* The row the cells sit in. `seats` gathers into a 600px row in the middle of a wide bar (a tablet, a
   narrow window) so the seats keep their phone spacing instead of spreading to 180px cells; `cells`
   spans the bar as it always has. */
.citrine-bottombar-row {
  display: flex;
  align-items: flex-end;
  width: 100%;
}

.citrine-bottombar--seats .citrine-bottombar-row {
  max-width: 600px;
  margin-inline: auto;
}

.citrine-bottombar-slot {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  /* FITTING CONSTANT — see the header note. */
  font-size: 10.5px;
  color: var(--citrine-bottombar-ink, var(--citrine-ink-3));
  /* Structural, not typographic — see the header note. */
  line-height: var(--citrine-line-height-1p25);
  background: none;
  border: 0;
  border-radius: 10px;
  font-family: inherit;
  text-decoration: none;
  padding: 0;
  position: relative;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* 44px is the WCAG 2.5.5 target size. This is the one surface that is only ever touched. */
  min-height: 44px;
}

.citrine-bottombar-slot:active {
  background: var(--citrine-bottombar-press, var(--citrine-surface-2));
}

/* Keyboard focus takes the current pill's shape: a 2px ring in the action colour around the glyph's
   56×30 pill, not a box around the whole cell (at 900 wide a cell is 120×44 — a rectangle round three
   dots — and a box round a current slot boxed its pill). Never the browser's own ring, which is
   orange in Chromium and belongs to no palette. An outline, so forced-colours mode still draws it. */
.citrine-bottombar-slot:focus-visible {
  outline: none;
}

.citrine-bottombar-slot:focus-visible .citrine-bottombar-icon::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 56px;
  height: 30px;
  margin: -15px 0 0 -28px;
  border-radius: 15px;
  outline: 2px solid var(--citrine-bottombar-focus, var(--citrine-accent));
  outline-offset: 1px;
  pointer-events: none;
}

.citrine-bottombar-icon {
  display: block;
  position: relative;
  /* The current slot's pill is painted behind the glyph as a pseudo-element at z-index -1; this
     keeps it inside the icon box's own stacking context rather than behind the bar. */
  isolation: isolate;
  width: 24px;
  height: 24px;
  margin: 0 auto var(--citrine-bottombar-icon-gap, var(--citrine-sp-1));
}

.citrine-bottombar-icon svg {
  width: 24px;
  height: 24px;
}

.citrine-bottombar-slot.citrine-is-selected {
  color: var(--citrine-bottombar-ink-selected, var(--citrine-accent-deep));
  font-weight: var(--citrine-bottombar-weight-selected, var(--citrine-font-weight-700));
}

/* The current destination is told three ways, never by colour alone: the colour and weight above, a
   thicker glyph, and a pill in the accent wash behind it. */
.citrine-bottombar-slot.citrine-is-selected .citrine-bottombar-icon svg {
  stroke-width: 2.2;
}

.citrine-bottombar-slot.citrine-is-selected .citrine-bottombar-icon::before {
  content: '';
  position: absolute;
  z-index: -1;
  left: 50%;
  top: 50%;
  width: 56px;
  height: 30px;
  margin: -15px 0 0 -28px;
  border-radius: 15px;
  background: var(--citrine-bottombar-pill, var(--citrine-accent-wash));
}

.citrine-bottombar-badge {
  position: absolute;
  top: -4px;
  right: -9px;
  min-width: 16px;
  height: 16px;
  /* A pill is a SHAPE, and antd has no pill radius token. */
  border-radius: 999px;
  background: var(--citrine-bottombar-badge-bg, var(--citrine-bad));
  color: var(--citrine-bottombar-badge-ink, var(--citrine-ink-on-solid));
  /* FITTING CONSTANT — see the header note. */
  font-size: 9.5px;
  font-weight: var(--citrine-bottombar-weight-selected, var(--citrine-font-weight-700));
  line-height: 16px;
  text-align: center;
  padding: 0 var(--citrine-bottombar-label-pad, var(--citrine-sp-1));
  /* A ring in the bar's own colour, so the badge reads as lifted off the icon. */
  border: 1.6px solid var(--citrine-bottombar-bg, var(--citrine-surface));
}

.citrine-bottombar-label {
  display: block;
  padding: 0 var(--citrine-bottombar-label-pad, var(--citrine-sp-1));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `visibility`, NOT `display` — see BottomBar.tsx. */
.citrine-bottombar-label.citrine-is-hidden {
  visibility: hidden;
}

/* The cell the hero floats over, and an empty seat. Both take a share of the row like a slot. */
.citrine-bottombar-gap,
.citrine-bottombar-seat {
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
}

.citrine-bottombar-gap {
  position: relative;
}

/* ── The hero ─────────────────────────────────────────────────────────────────────────────────
   Positioned out of flow inside its own cell, so it cannot squeeze the slots, and raised above the
   bar's edge so it reads as sitting ON the bar rather than in it.

   Its foot is 29px above the bar's bottom edge — 3px higher than 2.0's 26px, so a focus ring hugging
   the circle clears the caption under it (at 26px the ring crossed the caption's letters). The cell's
   foot is the bar's bottom padding above that edge, so the offset from the cell is 29px less that
   padding — and when a notched phone adds its inset to the padding, the cell and the circle rise
   together. AppShell's 32px rise reserve still covers it (24px of circle + the 5px ring). */
.citrine-bottombar-hero {
  position: absolute;
  left: 50%;
  bottom: calc(29px - var(--citrine-bottombar-pad-bottom, var(--citrine-sp-3)));
  transform: translateX(-50%);
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: var(--citrine-bottombar-hero-bg, var(--citrine-accent));
  color: var(--citrine-bottombar-hero-ink, var(--citrine-accent-ink));
  display: grid;
  place-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Two shadows doing two jobs. The first is the lift, so the circle sits above the bar; the second
     is a RING in the bar's own colour, which cuts the circle out of the bar's top border. */
  box-shadow:
    var(--citrine-bottombar-hero-shadow, var(--citrine-sh-lg)),
    0 0 0 5px var(--citrine-bottombar-bg, var(--citrine-surface));
}

.citrine-bottombar-hero:hover {
  background: var(--citrine-bottombar-hero-bg-hover, var(--citrine-accent-deep));
}

/* Drawn ON the 5px cut-out ring (2px out from the circle), where the bar's own colour makes it read
   cleanly in both themes and it stays clear of the caption below. */
.citrine-bottombar-hero:focus-visible {
  outline: 2px solid var(--citrine-bottombar-focus, var(--citrine-accent));
  outline-offset: 2px;
}

/* On the hero's own page: an inner ring in the hero's ink — the circle says "you are here" without
   changing its colour, which would read as disabled. */
.citrine-bottombar-hero.citrine-is-selected {
  box-shadow:
    inset 0 0 0 3px var(--citrine-bottombar-hero-bg, var(--citrine-accent)),
    inset 0 0 0 5px var(--citrine-bottombar-hero-ink, var(--citrine-accent-ink)),
    var(--citrine-bottombar-hero-shadow, var(--citrine-sh-lg)),
    0 0 0 5px var(--citrine-bottombar-bg, var(--citrine-surface));
}

.citrine-bottombar-heroicon {
  display: flex;
}

.citrine-bottombar-heroicon svg {
  width: 27px;
  height: 27px;
  stroke-width: 2.4;
}

.citrine-bottombar-herolabel {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* Clamped to the one cell it owns: every slot caption beside it is `nowrap`, and two nowrap texts
     on one line overlap in SILENCE. This turns that into an ellipsis. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* 8px above the bar's bottom edge, measured from the cell's foot like the circle. */
  bottom: calc(
    var(--citrine-bottombar-herolabel-bottom, var(--citrine-sp-2)) -
      var(--citrine-bottombar-pad-bottom, var(--citrine-sp-3))
  );
  /* FITTING CONSTANT — see the header note. */
  font-size: 10.5px;
  font-weight: var(--citrine-bottombar-weight-selected, var(--citrine-font-weight-700));
  color: var(--citrine-bottombar-ink-selected, var(--citrine-accent-deep));
  pointer-events: none;
}

.citrine-bottombar-herolabel.citrine-is-selected {
  font-weight: var(--citrine-font-weight-700, 700);
}

/* ── Destinations that wear a hue ────────────────────────────────────────────────────────────────
   The glyph takes its section's deep, and the current destination its deep and wash — the comp's phone
   bar (`.pbar-ic`). The bar's own inline `--citrine-bottombar-ink-selected` is resolved on the BAR, so it
   cannot see a hue set on one slot; these rules name the hue directly. */
.citrine-bottombar-slot.citrine-hued .citrine-bottombar-icon {
  color: var(--citrine-hue-deep);
}
.citrine-bottombar-slot.citrine-hued.citrine-is-selected {
  color: var(--citrine-hue-deep);
}
.citrine-bottombar-slot.citrine-hued.citrine-is-selected .citrine-bottombar-icon::before {
  background: var(--citrine-hue-wash);
}
/* The hero circle as its section's primary button: the pair, and the derived hover. */
.citrine-bottombar-hero.citrine-hued {
  background: var(--citrine-hue-btn);
  color: var(--citrine-hue-btn-ink);
}
.citrine-bottombar-hero.citrine-hued:hover {
  background: var(--citrine-hue-btn-2);
}

/* ── Breadcrumbs ───────────────────────────────────────────────────────────────────────── */
/* UI kit — Breadcrumbs. antd's Breadcrumb now draws the <nav>/<ol>/<li> shell and the separator
   layout; what is left is the trigger buttons (fold "…" and per-item sibling chevron) and the
   current-page emphasis, neither of which has an antd slot. Tokens only. */

.citrine-crumbs a,
.citrine-crumb-link,
.citrine-crumb-menu-trigger {
  text-decoration: none;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

.citrine-crumbs a:hover,
.citrine-crumb-link:hover,
.citrine-crumb-menu-trigger:hover {
  color: var(--citrine-data);
}

.citrine-crumb-step {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-space-4);
}

.citrine-crumb-menu-trigger {
  line-height: var(--citrine-line-height-1);
}

.citrine-crumb-sep {
  color: var(--citrine-ink-4);
}

.citrine-crumbs .citrine-is-here {
  color: var(--citrine-ink-2);
  font-weight: var(--citrine-font-weight-600);
}

/* A crumb's glyph (`BreadcrumbItem icon`), on the text's centre line. */
.citrine-crumb-icon {
  display: inline-flex;
  align-items: center;
  vertical-align: -0.15em;
  margin-inline-end: 4px;
}
.citrine-crumb-icon:has(+ .citrine-visually-hidden) {
  margin-inline-end: 0;
}

/* ── Briefing ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Briefing. A page of plain questions, each answered in one sentence.

   Narrow first: one card per answer, the action full width at 44px for a thumb. From 720px of the
   Briefing's OWN width (a container query, so a rail beside it counts):

   - `wide="cards"` (default, `.citrine-briefing-wide-cards`) — the cards stay cards and scale up: more
     room inside, a larger answer, the action at its own width (still 44px tall).
   - `wide="sheet"` (opt-in, `.citrine-briefing-wide-sheet`) — the cards merge into one ruled sheet
     with the question, the answer and the action in three columns. */

.citrine-briefing {
  container: citrine-briefing / inline-size;
  margin: 0;
}

.citrine-briefing-sheet {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-3, 12px);
}

.citrine-briefing-item {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-2, 8px);
  min-width: 0;
  padding: var(--citrine-sp-4, 16px);
  background: var(--citrine-surface, #ffffff);
  border: 1px solid var(--citrine-line, #e3e8f0);
  border-radius: var(--citrine-r, 12px);
}

.citrine-briefing-question {
  margin: 0;
  font-size: var(--citrine-font-size-14, 14px);
  font-weight: var(--citrine-font-weight-600, 600);
  line-height: 1.4;
  color: var(--citrine-accent);
  text-wrap: balance;
}

.citrine-briefing-body {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-1, 4px);
  min-width: 0;
}

.citrine-briefing-answer {
  font-size: var(--citrine-font-size-20, 20px);
  font-weight: var(--citrine-font-weight-500, 500);
  line-height: 1.35;
  color: var(--citrine-ink, #17223a);
  text-wrap: balance;
}
.citrine-briefing-quiet .citrine-briefing-answer {
  color: var(--citrine-ink-2, #45516a);
}

/* A figure inside an answer is a settled entry: the data colour, bold, tabular. */
.citrine-briefing-answer .citrine-tnum {
  color: var(--citrine-data);
  font-weight: var(--citrine-font-weight-700, 700);
}

.citrine-briefing-note {
  margin: 0;
  max-width: 70ch;
  font-size: var(--citrine-font-size-14, 14px);
  line-height: 1.5;
  color: var(--citrine-ink-3, #6b768b);
}

/* Narrow: the one action fills the card, at a thumb's height. */
.citrine-briefing-action {
  display: flex;
  margin-top: var(--citrine-sp-1, 4px);
}
.citrine-briefing-action > * {
  flex: 1 1 auto;
}
.citrine-briefing-action .ant-btn {
  height: 44px;
}

.citrine-briefing-detail {
  min-width: 0;
}

/* ── Wide, default: the cards scale up ──────────────────────────────────────────────────────────── */
@container citrine-briefing (min-width: 720px) {
  .citrine-briefing-wide-cards .citrine-briefing-item {
    gap: var(--citrine-sp-3);
    padding: var(--citrine-sp-6);
  }
  .citrine-briefing-wide-cards .citrine-briefing-question {
    font-size: var(--citrine-font-size-15);
  }
  .citrine-briefing-wide-cards .citrine-briefing-answer {
    max-width: 36ch;
    font-size: var(--citrine-font-size-24);
  }
  /* The action keeps its thumb height and gives up the full width: across a desk-wide card a
     stretched button reads as a banner, not an action. */
  .citrine-briefing-wide-cards .citrine-briefing-action > * {
    flex: none;
  }
}

/* ── Wide, opt-in: one ruled sheet in three columns ─────────────────────────────────────────────── */
@container citrine-briefing (min-width: 720px) {
  .citrine-briefing-wide-sheet .citrine-briefing-sheet {
    gap: 0;
    background: var(--citrine-surface, #ffffff);
    border: 1px solid var(--citrine-line, #e3e8f0);
    border-radius: var(--citrine-r, 12px);
  }
  .citrine-briefing-wide-sheet .citrine-briefing-item {
    display: grid;
    grid-template-columns: minmax(160px, 240px) minmax(0, 1fr) auto;
    column-gap: var(--citrine-sp-8, 32px);
    row-gap: var(--citrine-sp-4, 16px);
    align-items: start;
    padding: 26px var(--citrine-sp-8, 32px);
    background: none;
    border: 0;
    border-radius: 0;
  }
  .citrine-briefing-wide-sheet .citrine-briefing-item + .citrine-briefing-item {
    border-top: 1px solid var(--citrine-line, #e3e8f0);
  }
  .citrine-briefing-wide-sheet .citrine-briefing-question {
    grid-column: 1;
    grid-row: 1;
    padding-top: 6px;
    font-size: var(--citrine-font-size-15, 15px);
  }
  .citrine-briefing-wide-sheet .citrine-briefing-body {
    grid-column: 2;
    grid-row: 1;
  }
  .citrine-briefing-wide-sheet .citrine-briefing-answer {
    max-width: 36ch;
    font-size: var(--citrine-font-size-24, 24px);
  }
  .citrine-briefing-wide-sheet .citrine-briefing-action {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    margin-top: 0;
  }
  .citrine-briefing-wide-sheet .citrine-briefing-action > * {
    flex: none;
  }
  .citrine-briefing-wide-sheet .citrine-briefing-action .ant-btn {
    height: 40px;
  }
  .citrine-briefing-wide-sheet .citrine-briefing-detail {
    grid-column: 2 / -1;
    grid-row: 2;
  }
}

/* ── Button ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Button.
 *
 * WHY THIS FILE EXISTS. `Button` has always emitted `citrine-btn-icon` for `iconOnly`
 * (`Button.tsx`), and until now **nothing styled it** — the class was a name with no rule behind
 * it, the same shape as `tone="ended"` on StageStrip before 2.3.0. That was invisible while
 * consumers carried their own stylesheets. One consumer deleted all 110 of its stylesheets on
 * 2026-09-10 and now has **zero** `.css` files, so the rule that used to hold the hit area open
 * went with them, and a comment in its help-mark component — "the hit area is kept at 44px by the
 * stylesheet" — has been describing a file that no longer exists ever since.
 *
 * MEASURED, not assumed: across seven screens at four widths in both themes, every one of ~38 help
 * marks drew **24×24 with a 25px reach** (measured on a consuming app).
 *
 * THE FIX IS REACH, NOT SIZE. Making the button itself 44px would draw a 44px circle wherever the
 * variant has a border or a fill — which is precisely what the owner rejected on 2026-09-05
 * ("it is not looking good") when the help mark drew as a full-size bordered circle beside every
 * label. So the target is opened with a pseudo-element that paints nothing and occupies no space in
 * the flow: the icon stays the size it is drawn, and the press area around it grows to 44px.
 *
 * This is antd's own pattern, checked in the installed source rather than assumed — antd holds a
 * Switch's target open with a `::before` on the handle (`antd/es/switch/style/index.js:123`) — and
 * the pattern Citrine already used for the Switch track in 2.2.0.
 *
 * `max()` rather than a flat 44px: a LARGE icon button is already bigger than 44, and shrinking its
 * target to 44 would be a regression. The pseudo never goes below the button's own box. */

.citrine-btn-icon {
  position: relative;
}

.citrine-btn-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--citrine-target-min, 44px));
  height: max(100%, var(--citrine-target-min, 44px));
  transform: translate(-50%, -50%);
  /* Paints nothing, and never eats a press meant for the icon itself. */
  background: none;
  border-radius: inherit;
}

/* A disabled control should not hold a 44px hole open over its neighbours — there is nothing to
   press, and the enlarged area would only block whatever sits behind it. */
.citrine-btn-icon:disabled::after,
.citrine-btn-icon[aria-disabled='true']::after {
  content: none;
}

/* ── The icon sits on the label's centre line ────────────────────────────────────────────────────
   The glyph's wrapper is a flex box, not an inline span: an inline box sits on a text line, and the
   glyph rested on the baseline 1.5px above the label's centre (measured beside 14px labels). */
.citrine-btn-glyph {
  display: flex;
  align-items: center;
}

/* ── An icon-only button draws its icon at 16px ─────────────────────────────────────────────────
   With no words beside it the icon IS the control, so it keeps the kit's 16px icon size at every
   button size. It used to follow the button's text size (12px at `sm`), and a "…" row-menu trigger
   came out as three dots too small to find. Citrine icons are drawn at `1em`, so the font size is
   the icon size; a button with words keeps its icon matched to them. */
.citrine-btn.citrine-btn-icon .citrine-btn-glyph {
  font-size: 16px;
}

/* ── The quiet variants keep their own weight (5.0.0) ───────────────────────────────────────────
   A `ghost` or `link` button (a header's "Sign out", a row's "Edit") reads as words, not as an action
   block, so it takes `--citrine-button-quiet-weight` (400) whatever weight the theme gives buttons.
   Stock antd is 400 already, so by default nothing changes; the rule matters when an app raises
   button weight through `CitrineProvider antdTheme` — Ant Design's theme has no per-type weight, so
   without this every quiet button would follow. Scoped to Button's own classes: two classes beat
   antd's `:where(…).ant-btn` weight whatever order the sheets load in. */
.citrine-btn.citrine-btn-ghost,
.citrine-btn.citrine-btn-link,
.citrine-btn.citrine-btn-danger-text,
.citrine-btn.citrine-btn-danger-link {
  font-weight: var(--citrine-button-quiet-weight);
}

/* ── `CitrineProvider buttonWeights`: a weight per variant (opt-in) ─────────────────────────────
   After the quiet rule, at the same two-class weight, so an app that names a quiet variant's weight
   gets it. Emitted only for a variant the provider names. */
.citrine-btn.citrine-btn-w400 {
  font-weight: 400;
}
.citrine-btn.citrine-btn-w500 {
  font-weight: 500;
}
.citrine-btn.citrine-btn-w600 {
  font-weight: 600;
}
.citrine-btn.citrine-btn-w700 {
  font-weight: 700;
}

/* ── CanvasFrame ───────────────────────────────────────────────────────────────────────── */
/* UI kit — CanvasFrame. A full-width frame with an explicit height for an interactive canvas. Tokens only. */

.citrine-canvas-frame {
  /* The default height: what the window leaves below the top bar and above the small-screen bar
     (--citrine-app-bottom-inset, 0px wherever no bar shows), less the page's padding and heading. */
  --citrine-canvas-frame-fill: calc(
    100dvh - var(--citrine-topbar-height) - var(--citrine-app-bottom-inset) -
      var(--citrine-canvas-frame-reserve)
  );

  position: relative;
  box-sizing: border-box;
  display: block;
  width: 100%;
  height: var(--citrine-canvas-frame-height, var(--citrine-canvas-frame-fill));
  min-height: var(--citrine-canvas-frame-min-height);
  border: var(--citrine-canvas-frame-edge-width) solid var(--citrine-canvas-frame-edge);
  border-radius: var(--citrine-canvas-frame-radius);
  background: var(--citrine-canvas-frame-bg);
  overflow: hidden;
}

/* touchGestures="canvas": the frame AND every descendant, because touch-action does not inherit
   past a scroll container and canvas libraries nest one. */
.citrine-canvas-frame-is-touch,
.citrine-canvas-frame-is-touch * {
  touch-action: none;
}

.citrine-canvas-frame-is-touch {
  overscroll-behavior: contain;
}

/* ── Card ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Card. Group B wave 7a.
 *
 * The card SURFACE is drawn by antd: the root is a real `<AntCard variant="outlined">`, so the
 * background, border, radius and dark-mode behaviour come from `.ant-card` and are not repeated
 * here. What is left is the head/body/foot geometry antd has no slot for, and every value it uses
 * is seeded from `theme.useToken()` in `Card.tsx` — antd's own Card token values, not the brand
 * skin. `--citrine-surface`, `--citrine-line`, `--citrine-sh`, `--citrine-r`, `--citrine-sp-*`,
 * `--citrine-font-size-*` and `--citrine-ink-3` are all gone from this file.
 *
 * `.citrine-card`, `.citrine-card-tinted` and `.citrine-card-stretch` are kept as SELECTOR HOOKS
 * with no skin attached: consumer stylesheets and test files name them, and removing the
 * name would break those for nothing. The look they used to carry is what went. */

/* citrine-ok: Card must not impose sibling layout spacing; approved by the kit owner on 2026-08-24. */

/* rc.7 — Gap 4, re-expressed for the antd body element that now sits between the root and its
   children. `.citrine-card-shell` is antd's own body node, named through the supported
   `classNames.body` slot rather than by reaching for `.ant-card-body`. The chain is: root is a
   flex column filling its grid row, the shell absorbs the slack, the body absorbs the slack inside
   the shell, and `.citrine-card-foot` is pushed to the true bottom by its own `margin-top: auto`. */
.citrine-card-stretch {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.citrine-card-stretch > .citrine-card-shell {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
}

.citrine-card-stretch > .citrine-card-shell > .citrine-card-body {
  flex: 1 1 auto;
}

.citrine-card-stretch > .citrine-card-shell > .citrine-card-foot {
  margin-top: auto;
}

.citrine-card-head {
  padding: var(--citrine-card-pad-y) var(--citrine-card-pad-x) var(--citrine-card-pad-y-sm);
  display: flex;
  align-items: flex-start;
  gap: var(--citrine-card-gap);
  flex-wrap: wrap;
}

.citrine-card-head.citrine-has-rule {
  border-bottom: 1px solid var(--citrine-card-line);
}

/* THE TEXT YIELDS, THE ACTIONS STAY ON ITS LINE. A wrapping flex row breaks its lines on each
   item's flex BASIS, not on the size it would shrink to, so the text block's basis is 0: it claims
   no width when the row decides where to break, then grows into whatever the actions leave. Its
   `min-width` stays `auto` (the longest word), so the actions drop under the heading only when the
   longest word and the actions cannot share one line — never by squeezing the title into
   overflowing words. */
.citrine-card-head-text {
  flex: 1 1 0;
}

/* …except a title the caller asked to cut (`<Truncate>`). Its one unbroken line is the block's whole
   min-content, so `auto` held the block at the full title and it ran past the card. Here the block
   may shrink to nothing and the title ends in an ellipsis at the head's edge. */
.citrine-card-head-text-yields {
  min-width: 0;
}

.citrine-card-title {
  margin: 0;
  font-size: var(--citrine-card-title-size);
  font-weight: var(--citrine-card-title-weight);
}

.citrine-card-sub {
  margin: var(--citrine-card-sub-gap) 0 0;
  font-size: var(--citrine-card-sub-size);
  color: var(--citrine-card-sub-color);
  max-width: 72ch;
}

.citrine-card-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--citrine-card-gap);
  flex-wrap: wrap;
}

.citrine-card-body {
  padding: var(--citrine-card-pad-y) var(--citrine-card-pad-x);
}

.citrine-card-body.citrine-is-flush {
  padding: 0;
}

/* A flush body exists so a table can run edge to edge. A Toolbar above that table (a filter, a
   "show only…" switch, the bulk-delete strip) is still words and controls, so it keeps the card's
   own inset — set directly in the flush body or one wrapper (a `Stack`) down. Bottom stays 0: the
   wrapper's gap, or the table's own top rule, separates bar and table. */
.citrine-card-body.citrine-is-flush > .citrine-toolbar,
.citrine-card-body.citrine-is-flush > * > .citrine-toolbar {
  padding: var(--citrine-card-pad-y) var(--citrine-card-pad-x) 0;
}

.citrine-card-foot {
  padding: var(--citrine-card-pad-y-sm) var(--citrine-card-pad-x);
  border-top: 1px solid var(--citrine-card-line);
  background: var(--citrine-card-foot-bg);
  display: flex;
  align-items: center;
  gap: var(--citrine-card-gap);
  flex-wrap: wrap;
}

/* A single action under a full-width card reads as stranded on the left. The comps put it at the
   end; `justify-content` is the whole of it, opt-in so no existing foot moves. */
.citrine-card-foot-end {
  justify-content: flex-end;
}
/* `align="between"`: the first item at the start, the last at the end. */
.citrine-card-foot-between {
  justify-content: space-between;
}
/* `surface`: the rule stays, the tint goes — the foot sits on the card's own ground. */
.citrine-card-foot.citrine-card-foot-surface {
  background: transparent;
}

/* DENSITY. Retunes this file's own padding variables rather than adding a competing padding rule,
   so a card's spacing keeps one source of truth and every part — head, body, foot — moves together.
   The parts inherit these from the root; under a tight Card they do not seed their own inline
   (`useCardTokens` in `Card.tsx`), since an inline seed would win over an inherited value.
   Deliberately not antd's `size`: this is not antd's Card, and antd's scale stops at `small`. */
.citrine-card-tight {
  --citrine-card-pad-x: var(--citrine-sp-3);
  --citrine-card-pad-y: var(--citrine-sp-3);
  --citrine-card-pad-y-sm: var(--citrine-sp-2);
}

/* The leading slot: a sibling of the heading, never a child of it. `flex: none` so an avatar keeps
   its size when the title wraps, and the head's own `align-items: flex-start` keeps it level with
   the first line of the title rather than centred against a two-line one. */
.citrine-card-head-lead {
  flex: none;
  display: inline-flex;
  align-items: center;
}

/* A selection tick as the lead. The Checkbox's press row holds a 44px target floor, so in a slot
   sized by its content the 16px box sat centred 22px down — about 9px below the middle of the
   title's line. Sized to exactly one title line instead (same font size, so `1lh` is the title's
   line box), the tick centres on that line and its taller press row overflows evenly into the
   head's padding, still pressable. Scoped to a checkbox: an avatar keeps its top-aligned place. */
.citrine-card-head-lead:has(.citrine-check) {
  font-size: var(--citrine-card-title-size);
  block-size: 1lh;
}

/* FILL. Two actions as equal halves, at every width. No existing primitive does this: `Grid cols={2}`
   collapses below 820px so a foot pair would stack at 390 anyway, `Button block` stacks by
   definition, and `ButtonGroup` is a radio group. `min-width: 0` lets a long label shrink rather
   than forcing the pair wider than the card. */
.citrine-card-foot-fill {
  flex-wrap: nowrap;
}

.citrine-card-foot-fill > * {
  flex: 1 1 0;
  min-width: 0;
}

/* STACK. Full width below the phone line, side by side above it. The media query is the whole point:
   a foot that stacked at every width would be a different component, not a variant. 767.98px rather
   than 768px so it cannot overlap the `min-width: 768px` the theme reads for the phone control
   floor — a device landing exactly on the boundary must match one rule, never both. */
@media (max-width: 767.98px) {
  .citrine-card-foot-stack {
    flex-direction: column;
    align-items: stretch;
  }

  .citrine-card-foot-stack > * {
    width: 100%;
  }
}

/* SPINE. A full-height leading border down the whole card, head, body and foot alike — the same
   mark as `Section spine`. It is drawn OVER antd's own box (whose outline, surface and radius stay as
   they are) by a pseudo-element on this component's own root, so it never reaches into antd's
   classes. `.ant-card` is already `position: relative`; the pseudo-element is pulled out by the
   box's border width (`--citrine-card-edge`) so it lies over the outer edge as the Section spine
   lies over its box's, is as wide as the box's corner radius, is rounded on its leading corners
   exactly as the box is, and is painted only in its first `--citrine-card-spine-width` — so the
   strip follows the box's curve rather than poking out of it. Radius and edge are antd's
   `borderRadiusLG` and `lineWidth`, which `Card.tsx` hands over only when `spine` is on. It sits
   above the foot's tint; `pointer-events: none` keeps everything under it clickable. */
.citrine-card-spine::before {
  --citrine-card-spine-ink: var(--citrine-card-spine);
  content: '';
  position: absolute;
  inset-block: calc(-1 * var(--citrine-card-edge));
  inset-inline-start: calc(-1 * var(--citrine-card-edge));
  z-index: 1;
  width: max(var(--citrine-card-radius, var(--citrine-r-xs)), var(--citrine-card-spine-width));
  border-start-start-radius: var(--citrine-card-radius, var(--citrine-r-xs));
  border-end-start-radius: var(--citrine-card-radius, var(--citrine-r-xs));
  background: linear-gradient(
    to right,
    var(--citrine-card-spine-ink) var(--citrine-card-spine-width),
    transparent var(--citrine-card-spine-width)
  );
  pointer-events: none;
}

/* TOP SPINE (`spineEdge="top"`). The same strip turned through a right angle: across the top edge,
   rounded on the top corners, painted only in its first `--citrine-card-spine-width`. */
.citrine-card-spine.citrine-card-spine-top::before {
  inset-block: calc(-1 * var(--citrine-card-edge)) auto;
  inset-inline: calc(-1 * var(--citrine-card-edge));
  width: auto;
  height: max(var(--citrine-card-radius, var(--citrine-r-xs)), var(--citrine-card-spine-width));
  border-start-start-radius: var(--citrine-card-radius, var(--citrine-r-xs));
  border-start-end-radius: var(--citrine-card-radius, var(--citrine-r-xs));
  border-end-start-radius: 0;
  background: linear-gradient(
    to bottom,
    var(--citrine-card-spine-ink) var(--citrine-card-spine-width),
    transparent var(--citrine-card-spine-width)
  );
}

/* With a hue — its own, or the nearest HueScope's — the spine is the hue's MID. */
.citrine-card-spine.citrine-hued::before {
  --citrine-card-spine-ink: var(--citrine-hue);
}

/* PLAIN. Both the rule and the tint come off together: either one left behind still reads as a foot,
   which defeats the purpose of pinning a bare status line to the bottom of a stretched card. */
.citrine-card-foot-plain {
  border-top: 0;
  background: transparent;
}

/* `onClick` — a choice card. The pointer over a card that can be chosen; the default arrow over one
   whose choice is fixed (`locked`); not-allowed over one that cannot be chosen now (`disabled`). A
   keyboard card shows the kit's focus ring. Opt-in: a card without `onClick` carries none of these. */
.citrine-card-clickable {
  cursor: pointer;
}

.citrine-card-clickable.citrine-card-locked {
  cursor: default;
}

.citrine-card-clickable.citrine-card-disabled {
  cursor: not-allowed;
}

.citrine-card-clickable:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-2);
}

/* ── ChatPreview ───────────────────────────────────────────────────────────────────────── */
/* UI kit — ChatPreview and ChatBubble. A thread of bubbles on a wallpaper. Every value is a
   `--citrine-chat-*` token (`chat.json`); the wallpaper is repainted through the palette's
   `chatWallpaper` slot. */
.citrine-chat {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-chat-gap);
  inline-size: 100%;
  padding: var(--citrine-chat-pad);
  border-radius: var(--citrine-chat-radius);
  background: var(--citrine-chat-wallpaper);
}

.citrine-chat.citrine-chat-plain {
  padding: 0;
  border-radius: 0;
  background: none;
}

/* One message: its author line and its bubble, on its speaker's side. */
.citrine-chat-msg {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-chat-meta-gap);
  min-inline-size: 0;
}

.citrine-chat-msg-in {
  align-items: flex-start;
}

.citrine-chat-msg-out {
  align-items: flex-end;
}

.citrine-chat-author {
  font-size: var(--citrine-font-size-11p5);
  color: var(--citrine-chat-meta-ink);
}

.citrine-chat-bubble {
  max-inline-size: var(--citrine-chat-bubble-max);
  padding: var(--citrine-chat-bubble-pad-y) var(--citrine-chat-bubble-pad-x);
  border-radius: var(--citrine-chat-radius);
  color: var(--citrine-chat-bubble-ink);
}

/* The corner at the speaker's side stays square: the bubble's tail. */
.citrine-chat-bubble-in {
  background: var(--citrine-chat-bubble-in);
  border-start-start-radius: 0;
}

.citrine-chat-bubble-out {
  background: var(--citrine-chat-bubble-out);
  border-start-end-radius: 0;
}

.citrine-chat-bubble-ok {
  background: var(--citrine-ok-wash);
}
.citrine-chat-bubble-warn {
  background: var(--citrine-warn-wash);
}
.citrine-chat-bubble-bad {
  background: var(--citrine-bad-wash);
}
.citrine-chat-bubble-info {
  background: var(--citrine-info-wash);
}

/* The message keeps its own line breaks. */
.citrine-chat-text {
  white-space: pre-wrap;
}

.citrine-chat-meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--citrine-chat-meta-gap);
  margin-block-start: var(--citrine-chat-meta-gap);
  font-size: var(--citrine-font-size-11p5);
  color: var(--citrine-chat-meta-ink);
}

.citrine-chat-ticks {
  display: inline-flex;
}

.citrine-chat-ticks.citrine-chat-ticks-read {
  color: var(--citrine-chat-tick-read);
}

/* ── Checkbox ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Checkbox family styles.

   RULING D14 (Group A audit, finding F-A5, 2026-09-10): the box skin is gone. `.citrine-check-box`
   used to be 19×19 with `var(--citrine-radius-6)`, a `1.5px var(--citrine-line-2)` border and a
   `var(--citrine-surface)` fill, turning `var(--citrine-accent)` when checked; `.citrine-check` set
   its own `display: flex`, gap, font size and ink on antd's label wrapper, and `-label` and
   `-is-disabled` did the same for the text and for the dimmed state. antd draws every one of those
   itself, at 16px from `colorPrimary`.

   WHERE THOSE RULES ACTUALLY LANDED — the reason they had to go rather than be re-pointed. antd
   routes `classNames.icon` to `RcCheckbox`'s own `className` (`antd/es/checkbox/Checkbox.js:111,133`),
   so `.citrine-check-box` sat on the OUTER `.ant-checkbox` span, while the box a user sees is
   `.ant-checkbox-inner`, a CHILD of it (`@rc-component/checkbox/es/index.js:71`). A 19px bordered
   square was being drawn AROUND antd's own 16px one. `Checkbox.test.tsx` pins that landing point by
   render, so the next reader does not have to re-derive it.

   Stripped in the same commit as `Radio.css`, deliberately: the audit found a checked checkbox
   drawing navy while a checked radio drew green, in the same form. Moving one without the other
   leaves that family split.

   What is left below is Citrine's OWN markup and its own feature — the `columns` grid on
   `CheckboxGroup`, which antd has no equivalent of — plus one rule on antd's label wrapper that is
   not a skin but an accessibility floor. */

/* THE PRESS ROW HOLDS THE KIT'S TARGET FLOOR. The `<label>` antd renders is the whole press target
   (box, gap and words), and antd draws it at its line height: 22px. The floor is 44px and stays on
   for everyone. It is a real `min-height` rather than a painted-nothing `::after` (the way
   `Button.css` opens a small icon button) because checkboxes stack: a group puts them 8px apart, and
   a 44px overlay on a 22px row would reach 11px into the rows above and below, so two targets would
   claim the same point. Opening the row itself keeps each target its own. `align-items: center`
   because antd aligns the box and words to the baseline, which would leave them at the top of the
   taller row. */
.citrine-check {
  min-height: var(--citrine-target-min);
  align-items: center;
}

.citrine-checkbox-group {
  display: grid;
  gap: var(--citrine-sp-2) var(--citrine-sp-4);
  grid-template-columns: minmax(0, 1fr);
}

.citrine-checkbox-group-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.citrine-checkbox-group-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* The description block. Outside the <label>, indented to sit under the label's text rather than
   under the box, so the tick keeps its own column and the note reads as a second line of the same
   thought. `--citrine-check-desc-indent` is the box plus its gap. */
.citrine-check-row {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-1, 4px);
}

.citrine-check-desc {
  margin-inline-start: var(--citrine-check-desc-indent, var(--citrine-sp-6));
  color: var(--citrine-check-desc-ink, var(--citrine-ink-3));
  font-size: var(--citrine-check-desc-size, var(--citrine-font-size-12));
  line-height: 1.45;
}

/* ── `CheckboxGroup appearance="chips"` ─────────────────────────────────────────────────────────
   A wrapping row of toggleable chips, measured on the reference mockup: 22px tall, 0 8px padding,
   a 1px edge, a 4px radius, 12px type, 8px between chips. The row wraps inside whatever cell it is
   given and a single long label wraps inside its own chip, so nothing ever overflows a narrow
   column. Each chip is a <label> around a native checkbox stretched transparent over it: the
   platform owns the click, the focus and the name, and the chip only paints. */
.citrine-checkbox-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--citrine-checkbox-chip-gap);
  min-width: 0;
  max-width: 100%;
}

.citrine-checkbox-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-checkbox-chip-inner-gap);
  box-sizing: border-box;
  min-height: var(--citrine-checkbox-chip-h);
  max-width: 100%;
  margin: 0;
  padding: var(--citrine-checkbox-chip-pad-y) var(--citrine-checkbox-chip-pad-x);
  border: 1px solid var(--citrine-checkbox-chip-line);
  border-radius: var(--citrine-checkbox-chip-radius);
  background: var(--citrine-checkbox-chip-bg);
  color: var(--citrine-checkbox-chip-ink);
  font: inherit;
  font-size: var(--citrine-checkbox-chip-size);
  line-height: var(--citrine-line-height-1p25);
  cursor: pointer;
}

.citrine-checkbox-chip-is-on {
  border-color: var(--citrine-checkbox-chip-line-on);
  background: var(--citrine-checkbox-chip-bg-on);
  color: var(--citrine-checkbox-chip-ink-on);
}

.citrine-checkbox-chip-is-disabled {
  cursor: not-allowed;
  opacity: var(--citrine-checkbox-chip-disabled-opacity);
}

/* Transparent, not hidden: it stays in the tab order and under the pointer. */
.citrine-checkbox-chip-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: inherit;
}

.citrine-checkbox-chip-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

.citrine-checkbox-chip-mark {
  display: inline-flex;
  flex: none;
}

.citrine-checkbox-chip:has(.citrine-checkbox-chip-input:focus-visible),
.citrine-checkbox-chip-more:focus-visible {
  outline: 2px solid var(--citrine-checkbox-chip-focus);
  outline-offset: 2px;
}

/* ── Chip ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Chip. Group B wave 7a.
 *
 * `Chip` stays bespoke — antd `Tag`'s close control is a `<span onClick>` that no keyboard user can
 * reach, and retargeting would have regressed `closeLabel` (see `Chip.tsx`) — but every colour and
 * measurement it draws is now an antd token, seeded from `theme.useToken()`. Gone from this file:
 * `--citrine-ok*`, `--citrine-warn*`, `--citrine-bad*`, `--citrine-info*`, `--citrine-accent*`,
 * `--citrine-surface-3`, `--citrine-ink-2`, `--citrine-ink-3`, `--citrine-line`, `--citrine-line-2`,
 * `--citrine-r-pill`, `--citrine-space-*`, `--citrine-font-size-*` and `--citrine-font-weight-600`.
 *
 * The per-tone classes (`.citrine-chip-ok`, `-warn`, `-bad`, `-info`, `-data`, `-accent`, `-ghost`)
 * and `.citrine-chip-lg` are all still applied — Citrine's own tests and consumers both name them —
 * but the colour they used to carry now arrives on the element as a custom property, so one rule
 * paints every tone. */

.citrine-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-chip-gap);
  padding: var(--citrine-chip-pad-y) var(--citrine-chip-pad-x);
  border-radius: var(--citrine-chip-radius);
  font-size: var(--citrine-chip-size);
  background: var(--citrine-chip-bg);
  color: var(--citrine-chip-fg);
  border: 1px solid var(--citrine-chip-line);
  white-space: nowrap;
}

/* A chip that holds a `Truncate` may shrink to its container, so the `Truncate` (itself a
   `min-width: 0` block with its own overflow) can cut its text with an ellipsis. Scoped to that
   case on purpose: a chip with plain text keeps its automatic minimum size and lays out exactly as
   it always did. The close button is a sibling of the label, so `Truncate` stays a direct child. */
.citrine-chip:has(> .citrine-truncate) {
  max-width: 100%;
  min-width: 0;
}

.citrine-chip svg {
  width: var(--citrine-chip-icon);
  height: var(--citrine-chip-icon);
  flex: none;
}

/* A real button, not an icon with a click handler — this is the whole reason `Chip` did not become
   an antd `Tag`. It stays keyboard-reachable and keeps a visible focus ring. */
.citrine-chip-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--citrine-chip-icon);
  height: var(--citrine-chip-icon);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  border-radius: 50%;
}

/* THE WAY OFF A LENS, AND IT WAS 12px. Measured on a consumer's deals list with a stage chosen: the
   header's closable chip drew its × at **12×12 with a 12px reach**, at every width and in both
   themes — the smallest target on the screen, and on a phone the only way to clear the filter
   without hunting for the pressed cell again.

   Reach, not size, for the same reason as `.citrine-btn-icon`: a close mark inside a chip has to
   stay small to sit inside the chip, so the press area is opened with a pseudo-element that paints
   nothing and takes no space in the flow. The chip itself is not made taller and nothing moves.

   `max()` so a chip drawn at a larger size never has its target shrunk to 44. */
.citrine-chip-close {
  position: relative;
}

.citrine-chip-close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--citrine-target-min, 44px));
  height: max(100%, var(--citrine-target-min, 44px));
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

.citrine-chip-close:hover {
  background: var(--citrine-chip-close-hover);
}

.citrine-chip-close:focus-visible {
  outline: 2px solid var(--citrine-chip-focus);
  outline-offset: 2px;
}

/* ── CitrineProvider ───────────────────────────────────────────────────────────────────── */
/* Consumer body baseline: apply to the app root so unclassed text never falls to the UA 16px. */
.citrine-body {
  font-size: var(--citrine-font-size-14);
  line-height: var(--citrine-line-height-1p55);
  /* Keep unclassed consumer text on the artifact's core ink. */
  color: var(--citrine-ink);
}

/* ── Reduced motion: the loops antd's own kill switch cannot reach ────────────────────────────────
 *
 * `CitrineProvider` sets antd's `theme.token.motion = false` when the user has asked for reduced
 * motion, which zeroes `motionDurationFast`/`Mid`/`Slow` and with them every antd transition and
 * every rc-motion enter/leave. Seven INFINITE, decorative animations survive that, because each one
 * carries a duration hardcoded in antd's own style generator rather than reading a motion token.
 *
 * These rules are the only way to stop them. Not the tidiest way — the tidiest way would be a
 * per-component token override, and that was tried and MEASURED not to work: each duration is set
 * by `mergeToken(token, { …literal })` inside the style function, and `mergeToken` lets the later
 * argument win, so a `theme.components.Skeleton.skeletonLoadingMotionDuration` never reaches the
 * emitted CSS. See the comment in `src/adapters/antd/theme.ts`.
 *
 * EVERY SELECTOR BELOW WAS READ OUT OF THE CSS `@ant-design/cssinjs` ACTUALLY INJECTED, not guessed
 * from antd's class-naming conventions. That matters more here than anywhere else in this package: a
 * selector with a typo applies cleanly, matches nothing, throws no error, and leaves the whole suite
 * green while a user who asked for less motion keeps getting it. Two of them are exactly the kind of
 * near-miss that would have happened:
 *
 *   - the rotating element in `Spin` is `.ant-spin-dot-spin`, NOT `.ant-spin-dot`. A rule on
 *     `.ant-spin-dot` stops the four pulsing dots and leaves the ring turning — which is the
 *     `Table`/`Modal`/`Select` `loading` case, the one nothing else can reach.
 *   - `Progress`'s stripe is on `.ant-progress-track::after`, NOT `.ant-progress-bg`.
 *
 * `!important` is REQUIRED, not defensive. `@ant-design/cssinjs` runs at `hashPriority: 'low'`
 * (`es/StyleContext.js:47`), so its hash sits inside `:where()` (`es/util/index.js:145`) and adds no
 * specificity — but antd's own compound selectors (`.ant-spin .ant-spin-dot-item`) still out-rank a
 * single class, and cssinjs injects its `<style>` at runtime, after this sheet is already in
 * `<head>`. Nothing here would win on its own.
 *
 * Reachability today, stated plainly so nobody mistakes breadth for waste: the `.anticon-spin` and
 * `.ant-spin-*` rules are LIVE — `Modal`, `Select` and `Table` all render a `loading` spinner.
 * `.ant-badge-*`, `.ant-skeleton-*` and `.ant-progress-*` are NOT reachable through Citrine at the
 * moment (`Badge` and `Skeleton` are not in the adapter barrel, and `Meter` pins
 * `Progress status="normal"`, the one status that never paints the stripe). They are here anyway,
 * because the failure this whole guard exists to prevent is a later wave adding one of those
 * components and nobody noticing what came with it.
 */
@media (prefers-reduced-motion: reduce) {
  /* `loadingCircle`, on every spinning antd icon — @ant-design/icons/es/utils.js:100-105.
     This is what stops `Button loading`, `Modal confirmLoading` and `Select loading`. It also
     covers `antBadgeLoadingCircle`, whose rule is `.ant-badge-dot.anticon-spin`. */
  .anticon-spin,
  .anticon-spin::before,
  /* `antSpinMove` (1s) and `antRotate` (1.2s) inside `Spin` — antd/es/spin/style/index.js:190,218.
     `Table loading` renders a `Spin`, so this is the rule that reaches it. */
  .ant-spin-dot-item,
  .ant-spin-dot-spin,
  /* `antStatusProcessing` on `Badge status="processing"` — antd/es/badge/style/index.js:188. */
  .ant-badge-status-processing::after,
  /* `antProgressLTRActive` (2.4s) on `Progress status="active"` — antd/es/progress/style/index.js:94. */
  .ant-progress-status-active .ant-progress-track::after,
  /* `ant-skeleton-loading` (1.4s) — antd/es/skeleton/style/index.js:23. antd puts the shimmer on the
     seven child elements, never on `.ant-skeleton-active` itself, so all seven are named. */
  .ant-skeleton-active .ant-skeleton-title,
  .ant-skeleton-active .ant-skeleton-paragraph > li,
  .ant-skeleton-active .ant-skeleton-avatar,
  .ant-skeleton-active .ant-skeleton-button,
  .ant-skeleton-active .ant-skeleton-input,
  .ant-skeleton-active .ant-skeleton-node,
  .ant-skeleton-active .ant-skeleton-image {
    animation: none !important;
  }
}

/* ── CodeInput ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — CodeInput. The boxes are antd Inputs sized by antd tokens (see `CodeInput.tsx`); this
   sheet only lays them out as one row and centres a tabular digit in each. */

.citrine-codeinput {
  display: grid;
  grid-template-columns: repeat(var(--citrine-codeinput-length, 6), minmax(0, 1fr));
  gap: var(--citrine-sp-2, 8px);
}

/* Two classes so this beats antd's single-class `.ant-input` rule whatever order the sheets load in. */
.citrine-codeinput .citrine-codeinput-cell {
  min-width: 0;
  text-align: center;
  font-weight: var(--citrine-font-weight-600, 600);
  font-variant-numeric: tabular-nums;
  caret-color: var(--citrine-accent);
}

/* ── ColorPicker ───────────────────────────────────────────────────────────────────────── */
/* UI kit — ColorPicker and ColorSwatch styles. Tokens only.
   The trigger also carries the shared `.citrine-ctrl` class (border, radius, surface, focus ring,
   error and disabled looks — see `TextInput.css`); this file adds only the layout of its contents. */
.citrine-color-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  min-height: var(--citrine-space-32);
  text-align: start;
  font-size: var(--citrine-font-size-14);
  cursor: pointer;
}

.citrine-color-trigger-text {
  font-family: var(--citrine-font-family-mono);
}

.citrine-color-trigger-empty {
  color: var(--citrine-ink-4);
}

/* The dropdown arrow: the box opens a panel. Pushed to the far end, in the quiet ink. */
.citrine-color-trigger-arrow {
  flex: none;
  margin-inline-start: auto;
  width: var(--citrine-space-14);
  height: var(--citrine-space-14);
  color: var(--citrine-ink-3);
}

/* The colour is data: `--citrine-swatch-fill` is set inline by `ColorSwatch`. The fallback is a
   token so an unset fill paints the plain surface, never transparent. */
.citrine-swatch {
  display: inline-block;
  flex: none;
  border: 1px solid var(--citrine-line-2);
  border-radius: var(--citrine-radius-5);
  background: var(--citrine-swatch-fill, var(--citrine-surface));
  vertical-align: middle;
}

.citrine-swatch-sm {
  width: var(--citrine-space-12);
  height: var(--citrine-space-12);
  border-radius: var(--citrine-radius-2);
}

.citrine-swatch-md {
  width: var(--citrine-space-18);
  height: var(--citrine-space-18);
}

/* ── CompanySwitch ─────────────────────────────────────────────────────────────────────── */
/* UI kit — CompanySwitch. Group B wave 7a.
 *
 * Spacing, radius, type scale and weights are seeded from `theme.useToken()` in
 * `CompanySwitch.tsx` — `--citrine-sp-*`, `--citrine-r-sm`, `--citrine-font-size-9p5`,
 * `--citrine-font-size-12p5`, `--citrine-font-weight-700` and `--citrine-font-weight-600` have all
 * left this file.
 *
 * The FOUR RAIL COLOURS stay: `--citrine-side`, `--citrine-side-2`, `--citrine-side-ink` and
 * `--citrine-side-ink-2` are palette slots that `CitrineProvider` paints onto
 * `documentElement`, not the retired brand skin. Ant Design has no token that means "the navigation
 * rail's ground", and `Sidebar` draws this control's surroundings from the same four. */

.citrine-company-switch {
  /* The inset is used twice — as the side margin and inside the width — so the two cannot drift. */
  margin: 0 var(--citrine-cs-inset) var(--citrine-cs-gap);
  width: calc(100% - 2 * var(--citrine-cs-inset));
  display: flex;
  align-items: center;
  gap: var(--citrine-cs-pad-x);
  padding: var(--citrine-cs-pad-y) var(--citrine-cs-pad-x);
  background: var(--citrine-side-2);
  border: 0;
  border-radius: var(--citrine-cs-radius);
  cursor: pointer;
  text-align: left;
  color: var(--citrine-side-ink-2);
}

.citrine-company-switch:hover {
  background: var(--citrine-side);
}

.citrine-company-switch-text {
  min-width: 0;
}

.citrine-cs-label {
  display: block;
  font-size: var(--citrine-cs-label-size);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--citrine-side-ink);
  font-weight: var(--citrine-cs-weight);
}

.citrine-cs-name {
  display: block;
  font-size: var(--citrine-cs-name-size);
  font-weight: var(--citrine-cs-weight);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.citrine-company-switch-chevron {
  width: var(--citrine-cs-icon);
  height: var(--citrine-cs-icon);
  margin-left: auto;
  color: var(--citrine-side-ink);
  flex: none;
}

/* ── On a collapsed rail: an icon square ─────────────────────────────────────────────────────────
   The same inset on both sides, so in the 64px column the square is the column less two insets and
   centres under the brand mark. The words stay in the button as its accessible name, visually hidden;
   `CompanySwitch.tsx` shows them beside the rail on hover and on focus. */
.citrine-company-switch-collapsed {
  justify-content: center;
  padding: 0;
  aspect-ratio: 1;
}

.citrine-company-switch-glyph {
  flex: none;
  width: var(--citrine-cs-icon);
  height: var(--citrine-cs-icon);
  color: var(--citrine-side-ink-2);
}

.citrine-company-switch-collapsed .citrine-company-switch-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── DashGrid ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — DashGrid styles. Source: artifact `.dashgrid`. Structure only — the gap value is
   seeded inline from `theme.useToken()` in DashGrid.tsx, not declared here.

   An item's span is two properties set inline by `DashGridItem` — its desk span out of 12 and its
   narrow span out of 4 — and read here, so the breakpoint can pick between them without
   `!important`. An inline `grid-column` could only be beaten by one. */

.citrine-dashgrid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--citrine-dashgrid-gap);
}

.citrine-dashgrid-item {
  grid-column: span var(--citrine-dashgrid-span);
}

@media (max-width: 900px) {
  .citrine-dashgrid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  /* Below 900px an item spans its `spanNarrow` of the 4 columns — the full row unless it says
     otherwise, as the artifact's own `.dashgrid > * { grid-column: span 4 }` did. */
  .citrine-dashgrid > .citrine-dashgrid-item {
    grid-column: span var(--citrine-dashgrid-span-narrow);
  }
}

/* ── DatePicker ────────────────────────────────────────────────────────────────────────── */
/* UI kit — DatePicker styles. The reference reuses the plain `.ctrl` look for date fields, with
   no dedicated date-picker class of its own.
   `.citrine-ctrl` base (including `.citrine-ctrl.citrine-is-error`) lives in `TextInput/TextInput.css`.

   Ant Design's DatePicker renders a wrapper element as its root (like Select/InputNumber), so —
   same note as NumberInput/Select — the calendar popup's own chrome comes from `adapters/antd/theme.ts`,
   not from this file. `.citrine-ctrl-date` is our own addition (not a literal reference class) to make
   the picker fill its field's width, since antd's default DatePicker width is content-sized. */
.citrine-ctrl-date {
  width: 100%;
}

/* Touch screens (DatePicker.tsx, "WHAT YOU SEE IS NOT THE NATIVE INPUT"): a read-only antd Input
   shows the DD-MM-YYYY value and the calendar glyph, and the real date input lies over it,
   transparent, to take the tap and open the phone's own picker. */
.citrine-date-native {
  position: relative;
  display: block;
  width: 100%;
}

.citrine-date-native-single {
  display: block;
  width: 100%;
}

/* With `showTime` on a touch screen: the date and a 12-hour time field share one row. */
.citrine-date-native-pair {
  display: flex;
  gap: 8px;
  width: 100%;
}

.citrine-date-native-pair > .citrine-date-native {
  flex: 3 1 0;
  min-width: 0;
}

.citrine-date-native-pair > .citrine-ctrl-time {
  flex: 2 1 0;
  min-width: 0;
}

.citrine-date-native-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
  /* Chrome sizes a date input to its own content and ignores width without this. */
  appearance: none;
  -webkit-appearance: none;
}

.citrine-date-native-input:disabled {
  cursor: not-allowed;
}

/* The focus lives on the transparent input, so the visible field borrows the focused `.citrine-ctrl`
   look (TextInput.css) to show where the keyboard or reader is. */
.citrine-date-native:focus-within .citrine-ctrl {
  border-color: var(--citrine-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--citrine-accent) 22%, transparent);
}

.citrine-date-native:focus-within .citrine-ctrl.citrine-is-error {
  border-color: var(--citrine-bad);
  box-shadow: 0 0 0 3px var(--citrine-bad-wash);
}

/* ── Dot ───────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Dot styles. Group B wave 3: colour no longer comes from `--citrine-ok`/`-warn`/`-bad`/
   `-info`/`-ink-4` — Dot.tsx reads antd's own derived status tokens via `theme.useToken()` and
   sets `background-color` inline (see Dot.tsx for why it stays bespoke rather than an antd
   `Badge`). The `.citrine-dot-*` tone classes are kept only as stable selectors for tests and
   downstream consumers; they carry no colour of their own any more. */
.citrine-dot {
  position: relative;
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
}
.citrine-dot-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Drawer ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Drawer. Source: artifact `.drawer`, `.ov-head`, `.ov-title`, `.ov-body`, `.ov-foot`
   (citrine-reference.css). Tokens only. Scoped under our own `.citrine-drawer` class (which lands on
   antd's Drawer root node via the `className` prop) so it never collides with Modal's copy of
   the same `.citrine-ov-*` class names. */

.citrine-drawer .citrine-ov-head {
  padding: var(--citrine-sp-5) var(--citrine-sp-6) var(--citrine-sp-3);
}

.citrine-drawer .citrine-ov-title {
  font-size: var(--citrine-font-size-17);
  font-weight: var(--citrine-font-weight-700);
  letter-spacing: -0.01em;
}

.citrine-drawer .citrine-ov-body {
  padding: var(--citrine-sp-2) var(--citrine-sp-6) var(--citrine-sp-5);
}

.citrine-drawer .citrine-ov-foot {
  padding: var(--citrine-sp-4) var(--citrine-sp-6);
  border-top: 1px solid var(--citrine-line);
  background: var(--citrine-surface-2);
  display: flex;
  gap: var(--citrine-sp-2);
  justify-content: flex-end;
}

/* `footerPlain`. Both the rule and the tint come off together, as `.citrine-card-foot-plain`:
   either one left behind still reads as a foot. */
.citrine-drawer .citrine-ov-foot-plain {
  border-top: 0;
  background: transparent;
}

/* `placement="bottom"`: a bottom sheet. `className` lands on antd's panel section and
   `classNames.wrapper` on the wrapper that carries its shadow; both get the rounded top corners.
   The height is the content's (`size="auto"`), capped here; past the cap antd's body — already a
   scrolling flex child of the section — scrolls. The section's own padding clears the home
   indicator on a notched phone, on the drawer's ground. */
.citrine-drawer-sheet-wrap,
.citrine-drawer.citrine-drawer-sheet {
  border-start-start-radius: var(--citrine-drawer-sheet-radius);
  border-start-end-radius: var(--citrine-drawer-sheet-radius);
}

.citrine-drawer.citrine-drawer-sheet {
  max-height: var(--citrine-drawer-sheet-max);
  overflow: hidden;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* ── DropZone ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — DropZone. Source: artifact `.dropzone` (citrine-reference.css).

   Group C (2.0): the STRUCTURE below is unchanged — this is still a hand-built @dnd-kit drop
   target and there is no antd component to hand it to. What changed is where every colour and
   metric comes from: `DropZone.tsx` reads antd's live theme with `antdTheme.useToken()` and hands
   the values down as `--citrine-dropzone-*` custom properties. Each fallback is the Citrine token
   the rule used to read, so an un-hooked render still draws the 1.x look. */

.citrine-dropzone {
  /* 2px dashed is the drop target's SHAPE — a dashed outline is how a drop target reads. Only the
     colour tracks the theme. */
  border: 2px dashed var(--citrine-dropzone-line, var(--citrine-accent-line));
  border-radius: var(--citrine-dropzone-radius, var(--citrine-r-sm));
  background: var(--citrine-dropzone-bg, var(--citrine-accent-wash));
  padding: var(--citrine-dropzone-pad, var(--citrine-sp-5));
  text-align: center;
  font-size: var(--citrine-dropzone-font-size, var(--citrine-font-size-12p5));
  color: var(--citrine-dropzone-ink, var(--citrine-accent-deep));
  font-weight: var(--citrine-dropzone-weight, var(--citrine-font-weight-600));
}

.citrine-dropzone[role='button'] {
  cursor: pointer;
}

.citrine-dropzone:focus-visible {
  outline: var(--citrine-dropzone-focus-width, 2px) solid
    var(--citrine-dropzone-focus-ink, var(--citrine-data));
  outline-offset: 2px;
}

.citrine-dropzone.citrine-is-active {
  border-color: var(--citrine-dropzone-line-active, var(--citrine-accent));
  background: var(--citrine-dropzone-bg-active, var(--citrine-accent-wash));
  /* A 3px ring in the same wash, so the zone reads as lifted while a drag is over it. */
  box-shadow: 0 0 0 3px var(--citrine-dropzone-bg, var(--citrine-accent-wash));
}

.citrine-dropzone-label {
  display: inline-block;
}

/* ── Empty ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Empty. Source: artifact `.empty` block (citrine-reference.css). Tokens only. */

.citrine-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--citrine-sp-3);
  padding: calc(var(--citrine-sp-8) * 2) var(--citrine-sp-6);
  text-align: center;
  color: var(--citrine-ink-3);
}

/* `framed` — opt-in. AN EMPTY REGION IS STILL A REGION: without an edge it can be
   indistinguishable from the page behind it, and a reader sees "nothing loaded" where the screen
   means "nothing here yet". Dashed, on transparent ground, is the shape this kit already uses to
   say a space is real but unfilled — an ended StageStrip group, and an empty stage cell. A solid
   border would make it a card, which would claim more of the eye than an absence deserves.
   Not the default: an `Empty` usually sits inside something that already has an edge (a Table's
   bordered wrapper, a Card), where a second frame doubles up. */
.citrine-empty-framed {
  border: 1px dashed var(--citrine-line);
  border-radius: var(--citrine-r-xs);
}

.citrine-empty-ico {
  width: 120px;
  height: 90px;
  color: var(--citrine-ink-4);
}

.citrine-empty-title {
  /* Zeroed because `as` can make this an `h1`–`h6`, which arrive with the browser's own heading
     margins where the default `div` has none. `font-size` and `font-weight` below are already
     explicit, so they beat the UA sheet too: a titled `Empty` looks identical whichever tag it is.
     Same treatment as `.citrine-card-title` and `.citrine-pagetitle`. */
  margin: 0;
  font-size: var(--citrine-font-size-16);
  font-weight: var(--citrine-font-weight-700);
  color: var(--citrine-ink-2);
}

.citrine-empty-desc {
  margin: 0;
  font-size: var(--citrine-font-size-12p5);
  color: var(--citrine-ink-3);
  max-width: 44ch;
}

.citrine-empty-action {
  margin-top: var(--citrine-sp-1);
}

/* ── Entrance ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Entrance. The signed-out page: the brand side and the form side.

   Colours come from antd's live theme via `antdTheme.useToken()` in `Entrance.tsx`, handed down as
   `--citrine-entrance-*` custom properties; each rule falls back to the Citrine token it would
   otherwise read. Control SIZES are not here at all — they are antd tokens set by the component's
   scoped ConfigProvider (see its header). This sheet is layout, type, and the few parts antd has no
   component for: the "or" rule, the text-action row, the help box.

   Breakpoints are literal because the kit's custom media are max-width queries: 768px is
   `--citrine-bp-xs` (a phone layout stops), 1080px is `--citrine-bp-2xl` (room for two halves). */

.citrine-entrance {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--citrine-entrance-canvas, var(--citrine-canvas));
  color: var(--citrine-entrance-ink, var(--citrine-ink));
}

/* ── the brand side ───────────────────────────────────────────────────────────────────────────── */

.citrine-entrance-brand {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 12px 16px;
  /* Neutral by default (the surface). An app that wants its rail colour here sets the token. */
  background: var(--citrine-entrance-brand-bg);
  border-bottom: 1px solid var(--citrine-entrance-line, var(--citrine-line));
}

.citrine-entrance-art {
  display: block;
  width: min(176px, 50vw);
  height: auto;
}

/* `brandSurface="side"`, dark only: the rail colour behind the artwork, and no hairline between
   the halves (the colour change is the edge). */
:root[data-theme='dark'] .citrine-entrance--brand-side .citrine-entrance-brand {
  background: var(--citrine-side);
  border-color: transparent;
}

/* `artPlate`, dark only: the artwork on a white plate, for artwork drawn on a white ground whose dark
   lettering cannot sit on a dark page. Off by default. */
:root[data-theme='dark'] .citrine-entrance--plate .citrine-entrance-art {
  box-sizing: border-box;
  width: min(188px, 54vw);
  padding: var(--citrine-entrance-plate-pad-block) var(--citrine-entrance-plate-pad-inline);
  border-radius: var(--citrine-entrance-plate-radius);
  background: var(--citrine-entrance-plate-bg);
}

/* ── the form side ────────────────────────────────────────────────────────────────────────────── */

.citrine-entrance-side {
  flex: 1;
  display: flex;
  justify-content: center;
  padding: 22px 20px 28px;
}

.citrine-entrance-col {
  width: 100%;
  max-width: var(--citrine-entrance-form-width);
}

.citrine-entrance-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 6px;
}

.citrine-entrance-mark {
  display: block;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 22%;
}

.citrine-entrance-title {
  margin: 0;
  font-size: 26px;
  line-height: 1.25;
  font-weight: var(--citrine-font-weight-600, 600);
  letter-spacing: -0.015em;
  text-wrap: balance;
  color: var(--citrine-entrance-ink, var(--citrine-ink));
}

.citrine-entrance-lede {
  margin: 0 0 20px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--citrine-entrance-ink-2, var(--citrine-ink-3));
}

.citrine-entrance-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  margin: 14px 0 0;
}

.citrine-entrance-lede + .citrine-entrance-body {
  margin-top: 0;
}

.citrine-entrance-foot {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--citrine-entrance-ink-2, var(--citrine-ink-3));
}

.citrine-entrance-foot svg {
  flex: none;
  width: 15px;
  height: 15px;
  margin-top: 2px;
}

/* A field's label reads as the thing to answer, not as a caption. */
.citrine-entrance .citrine-flabel {
  font-weight: var(--citrine-font-weight-600, 600);
}

/* The field frame. `.citrine-ctrl` (TextInput.css) pins a small radius and inline padding for dense
   forms, and focuses in the data green; here the frame follows the scoped tokens
   (`--citrine-entrance-radius`, 16px in) and focus is the action colour — on this page a field is
   the thing you act in. */
.citrine-entrance .citrine-ctrl {
  padding-inline: 16px;
  border-radius: var(--citrine-entrance-radius);
}

/* One surface: a field is filled like the page's other controls (#141414 in dark), not the kit's surface-2. */
.citrine-entrance .citrine-ctrl {
  background: var(--citrine-entrance-surface, var(--citrine-surface));
}

.citrine-entrance .citrine-ctrl:focus,
.citrine-entrance .citrine-ctrl:focus-within {
  border-color: var(--citrine-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--citrine-accent) 22%, transparent);
}

/* The show-password toggle is a 44px target here, hugging the field's right edge. The field's content
   box is 26.8px at both sizes (antd's padding absorbs the 48/52 step), so a -9px block margin lets
   the 44px button overhang into the padding instead of stretching the field. */
.citrine-entrance .citrine-ctrl-reveal {
  width: 44px;
  height: 44px;
  margin-block: -9px;
  margin-inline-end: -12px;
  border-radius: 10px;
  font-size: 20px;
}

/* A prefix such as "+91" is part of the number, set off by a hairline. */
.citrine-entrance .ant-input-prefix {
  margin-inline-end: 12px;
  padding-inline-end: 12px;
  border-inline-end: 1px solid var(--citrine-entrance-line, var(--citrine-line));
  color: var(--citrine-entrance-ink-2, var(--citrine-ink-2));
  font-size: 17px;
  font-weight: var(--citrine-font-weight-500, 500);
}

/* A checkbox row is a thumb target like every other control here. */
.citrine-entrance .ant-checkbox-wrapper {
  min-height: 44px;
  align-items: center;
  font-size: 15px;
}

/* The second way in: a full-size button that still reads as second — accent words on a soft accent
   line, where antd's default would give it grey ink. */
.citrine-entrance .ant-btn.ant-btn-color-default.ant-btn-variant-outlined {
  color: var(--citrine-accent);
  border-color: var(--citrine-accent-line);
}

.citrine-entrance .ant-btn.ant-btn-color-default.ant-btn-variant-outlined:not(:disabled):hover {
  border-color: var(--citrine-accent);
}

/* ── the parts antd has no component for ──────────────────────────────────────────────────────── */

.citrine-entrance-or {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: var(--citrine-entrance-ink-2, var(--citrine-ink-3));
}

.citrine-entrance-or::before,
.citrine-entrance-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--citrine-entrance-line, var(--citrine-line));
}

/* Text actions are 44px tall hit areas with their words at 14px — the scoped Button height (48/52)
   is for real buttons, not for these. Three classes so this outranks antd's own button rules. */
.citrine-entrance .citrine-entrance-link.ant-btn {
  height: auto;
  min-height: 44px;
  padding-inline: 2px;
  font-size: 14px;
  font-weight: var(--citrine-font-weight-600, 600);
  /* The action colour, not a second link colour beside it. */
  color: var(--citrine-accent);
}

.citrine-entrance .citrine-entrance-link.ant-btn:not(:disabled):hover {
  color: var(--citrine-accent-2);
}

/* A disabled text action still carries information — "Resend in 24s" is a countdown — so it stays
   readable at the quiet ink instead of antd's 25% disabled grey. */
.citrine-entrance .citrine-entrance-link.ant-btn:disabled {
  color: var(--citrine-entrance-ink-2, var(--citrine-ink-3));
  font-weight: var(--citrine-font-weight-500, 500);
}

.citrine-entrance-linkrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 18px;
}

/* Tucked under the field above it, so it reads as that field's own escape. */
.citrine-entrance-linkrow--end {
  justify-content: flex-end;
  margin-top: -8px;
}

.citrine-entrance-help {
  margin-top: -8px;
}

.citrine-entrance-helpbox {
  margin: 4px 0 0;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--citrine-accent-wash);
  color: var(--citrine-entrance-ink, var(--citrine-ink));
  font-size: 14px;
  line-height: 1.5;
}

.citrine-entrance-helpbox[hidden] {
  display: none;
}

/* ── a desk ───────────────────────────────────────────────────────────────────────────────────── */

@media (min-width: 768px) {
  .citrine-entrance-title {
    font-size: 30px;
  }

  .citrine-entrance-body {
    margin-top: 18px;
  }

  .citrine-entrance-foot {
    margin-top: 18px;
  }
}

@media (min-width: 1080px) {
  .citrine-entrance {
    flex-direction: row;
  }

  .citrine-entrance-brand {
    flex: 0 0 var(--citrine-entrance-art-share);
    padding: 4vw;
    border-bottom: 0;
    border-inline-end: 1px solid var(--citrine-entrance-line, var(--citrine-line));
  }

  /* `artSide="end"`: the artwork on the far side. `row-reverse` follows the writing direction, and
     the hairline moves to the edge that now faces the form. */
  .citrine-entrance--art-end {
    flex-direction: row-reverse;
  }

  .citrine-entrance--art-end .citrine-entrance-brand {
    border-inline-end: 0;
    border-inline-start: 1px solid var(--citrine-entrance-line, var(--citrine-line));
  }

  .citrine-entrance-art,
  :root[data-theme='dark'] .citrine-entrance--plate .citrine-entrance-art {
    width: clamp(360px, 36vw, 640px);
  }

  :root[data-theme='dark'] .citrine-entrance--plate .citrine-entrance-art {
    padding: var(--citrine-entrance-plate-pad-wide);
    border-radius: var(--citrine-entrance-plate-radius-wide);
  }

  .citrine-entrance-side {
    align-items: center;
    padding: 48px;
  }
}

/* ── Field ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Field driver styles. `Field` mostly delegates its look to whichever sibling
   component it renders (`TextInput`, `Select`, `Checkbox`, …), each of which carries its own
   CSS. The one thing `Field` renders itself is the wrapper around an inline control
   (`checkbox` / `switch` / `tristate`) when it composes `FieldShell` on that control's behalf.

   Group B wave 7b: the `min-height: 42px` that used to be here is gone. 42 was Citrine 1.x's
   `controlHeight`, hard-coded — a number the owner has since reverted to antd's stock 32 (spec
   D9), so it was already a pixel value contradicting the theme. It needs no replacement:
   `FieldShell` now mounts an antd `Form.Item`, and `.ant-form-item-control-input` is
   `min-height: controlHeight` (`form/style/index.js:55-67`), so the row this wrapper sits in takes
   its height from the same token every other control in the kit does. */

.citrine-field-inline-control {
  display: flex;
  align-items: center;
}

/* ── FieldCard ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — FieldCard. Source: artifact `.fieldcard`, `.fieldcard:hover`, `.fieldcard.is-required`,
   `.fieldcard.is-system`, `.fieldcard-name`, `.grip` (citrine-reference.css).

   Group C (2.0): structure unchanged — there is no antd reorderable field row. Colours and metrics
   now come from antd's live theme via `antdTheme.useToken()` in `FieldCard.tsx`, handed down as
   `--citrine-fieldcard-*` custom properties. Every fallback is the Citrine token the rule used to
   read, so an un-hooked render degrades to the 1.x look rather than to nothing.

   The 4px required rail, the grip's 15px box and the remove button's 28px box are STRUCTURAL: they
   are the row's geometry, not its brand. They stay literal. */

.citrine-fieldcard {
  display: flex;
  align-items: center;
  gap: var(--citrine-fieldcard-gap, var(--citrine-sp-3));
  padding: var(--citrine-fieldcard-pad-y, 11px) var(--citrine-fieldcard-pad-x, var(--citrine-sp-4));
  border: var(--citrine-fieldcard-line-width, 1px) solid var(--citrine-fieldcard-line, var(--citrine-line));
  border-radius: var(--citrine-fieldcard-radius, var(--citrine-r-sm));
  background: var(--citrine-fieldcard-bg, var(--citrine-surface));
  cursor: grab;
}

.citrine-fieldcard:hover {
  border-color: var(--citrine-fieldcard-hover-line, var(--citrine-data-line));
  box-shadow: var(--citrine-fieldcard-shadow, var(--citrine-sh));
}

.citrine-fieldcard:focus-visible {
  outline: var(--citrine-fieldcard-focus-width, 2px) solid
    var(--citrine-fieldcard-selected-line, var(--citrine-data));
  outline-offset: 2px;
}

/* Source-accurate: the original FieldCard turns its left border 4px danger when required. The 4px is
   the rail's WIDTH — a deliberate, readable-at-a-glance stripe — and stays literal. */
.citrine-fieldcard.citrine-is-required {
  border-left: 4px solid var(--citrine-fieldcard-danger, var(--citrine-bad));
}

.citrine-fieldcard.citrine-is-system {
  background: var(--citrine-fieldcard-system-bg, var(--citrine-surface-2));
}

.citrine-fieldcard.citrine-is-selected {
  border-color: var(--citrine-fieldcard-selected-line, var(--citrine-data));
  box-shadow: 0 0 0 2px var(--citrine-fieldcard-selected-ring, var(--citrine-data-wash));
}

.citrine-fieldcard.citrine-is-dragging {
  opacity: 0.6;
}

.citrine-grip {
  color: var(--citrine-fieldcard-grip-ink, var(--citrine-ink-4));
  width: 15px;
  height: 15px;
  flex: none;
}

.citrine-grip svg {
  width: 100%;
  height: 100%;
}

.citrine-fieldcard-body {
  min-width: 0;
  flex: 1;
}

.citrine-fieldcard-name {
  font-weight: var(--citrine-fieldcard-name-weight, var(--citrine-font-weight-650));
  font-size: var(--citrine-fieldcard-name-size, var(--citrine-font-size-13p5));
}

.citrine-fieldcard-meta {
  display: flex;
  align-items: center;
  gap: var(--citrine-fieldcard-meta-gap, var(--citrine-space-6));
  margin-top: var(--citrine-fieldcard-meta-top, var(--citrine-space-2));
  font-size: var(--citrine-fieldcard-meta-size, var(--citrine-font-size-11p5));
  color: var(--citrine-fieldcard-meta-ink, var(--citrine-ink-3));
}

.citrine-fieldcard-required {
  color: var(--citrine-fieldcard-danger, var(--citrine-bad));
  font-weight: var(--citrine-fieldcard-name-weight, var(--citrine-font-weight-700));
}

.citrine-fieldcard-system {
  color: var(--citrine-fieldcard-grip-ink, var(--citrine-ink-4));
  font-weight: var(--citrine-fieldcard-name-weight, var(--citrine-font-weight-600));
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--citrine-fieldcard-meta-size, var(--citrine-font-size-10));
}

.citrine-fieldcard-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--citrine-fieldcard-remove-radius, var(--citrine-r-xs));
  border: var(--citrine-fieldcard-line-width, 1px) solid transparent;
  background: transparent;
  color: var(--citrine-fieldcard-remove-ink, var(--citrine-ink-3));
  cursor: pointer;
  flex: none;
}

.citrine-fieldcard-remove:hover {
  background: var(--citrine-fieldcard-danger-wash, var(--citrine-bad-wash));
  color: var(--citrine-fieldcard-danger, var(--citrine-bad));
}

.citrine-fieldcard-remove svg {
  width: 14px;
  height: 14px;
}

/* ── FieldShell ────────────────────────────────────────────────────────────────────────── */
/* UI kit — FieldShell styles.

   Group B wave 7b: this file is now layout only, apart from the label row (see the note above it).
   Every other colour, size, weight and spacing value that used to live here is read from antd's
   token set inside `FieldShell.tsx` (`antdTheme.useToken()`) and applied inline, so a field draws
   from the same tokens antd's own components do instead of from the `--citrine-*` brand skin. Two
   whole rules went away rather than moving:

   - `.citrine-fhelp` — the help text sits in `Form.Item`'s `extra` slot, which antd already paints
     `colorTextDescription` at `token.fontSize` (`form/style/index.js:243-255`). The class stays on
     the element purely as a hook for consumers.
   - `.citrine-span2` — an exact duplicate of `Grid.css:61`, which still ships it. `FieldShell` still
     WRITES the class (a field inside a Citrine `Grid` needs it); it just no longer redeclares the
     rule. Note that inside a `FormGrid` the class is now inert: wave 7b moved `FormGrid` onto antd
     `Row`/`Col`, where the span is carried by the wrapping `Col`, not by a `grid-column`.

   Source: artifact `.field` / `.flabel` / `.req` / `.lockmark` / `.fhelp` / `.ferr` / `.counter` /
   `.withheld` / `.field.is-readonly` / `.span2`. Every class here is prefixed `citrine-` because
   a consumer's own stylesheet already owns the bare `.field` class app-wide. */

.citrine-field {
  min-width: 0;
}

/* antd's `Form.Item` is the control frame now. It carries no width of its own inside a flex or
   grid parent, and `.ant-form-item-vertical .ant-form-item-control` is already `width: 100%`
   (`form/style/index.js:413-415`) — this only stops the item shrinking below its content in a
   narrow column. */
.citrine-field-item {
  min-width: 0;
}

/* The label row's four rules — `.citrine-flabel`, `.citrine-flabel-row`,
   `.citrine-flabel-row > .citrine-flabel` and `.citrine-flabel-suffix` — are GONE from this file.
   They were the exception to "layout only" above, and the reason was `Segmented.tsx`, which
   rendered the same three classes with markup of its own: moving the label onto antd's tokens
   inside `FieldShell.tsx` would have left Segmented's copy unstyled, so wave 7b put these
   declarations back (commit `4376f16`) and reported the coordination.

   That coordination is now settled structurally rather than by hand: both components render the
   same `FieldLabelRow.tsx`, which applies antd's own label tokens (`labelColor` = `colorTextHeading`,
   `labelFontSize` = `fontSize`, `verticalLabelPadding` = `0 0 paddingXS`;
   `form/style/index.js:451-462`) inline from `antdTheme.useToken()`. Inline and not CSS on purpose:
   the deleted `.citrine-flabel-row > .citrine-flabel { margin-bottom: 0 }` rule could never have
   beaten an inline style, so the bottom spacing now lands on whichever element is outermost.

   **Every class is still written on the DOM** — nothing was removed or renamed, only these four
   rule bodies. `--citrine-space-7`, `--citrine-font-size-12p5`, `--citrine-font-weight-600` and
   `--citrine-ink-2` leave this file with them; `.citrine-lockmark`'s `--citrine-font-weight-700`
   below is now the last `--citrine-*` skin value in the field stack, kept because antd has no
   lock-marker equivalent to take a weight from (wave 7b §7). */

.citrine-lockmark {
  display: inline-flex;
  align-items: center;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  font-weight: var(--citrine-font-weight-700);
}
.citrine-lockmark svg {
  width: 11px;
  height: 11px;
}

.citrine-ferr {
  display: flex;
  align-items: center;
}
.citrine-ferr svg {
  width: 12px;
  height: 12px;
  flex: none;
}

.citrine-counter {
  text-align: right;
}

.citrine-withheld {
  display: flex;
  align-items: center;
  width: 100%;
}
.citrine-withheld svg {
  width: 14px;
  height: 14px;
}

/* `.citrine-ctrl` is the base control class every wrapped Ant Design field control applies to its own
   root node (defined once in TextInput/TextInput.css — see that file for why). This rule reaches
   across component folders on purpose: it is the same "field owns the readonly look of its
   control" relationship the artifact itself expresses as `.field.is-readonly .ctrl`. The two
   colours arrive as custom properties set by `FieldShell.tsx` from antd's tokens, because the
   element they land on is one this component does not render. */
.citrine-field.citrine-is-readonly .citrine-ctrl {
  background: var(--citrine-field-readonly-bg);
  color: var(--citrine-field-readonly-ink);
  border-style: dashed;
}

/* ── FigureRow ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — FigureRow. Equal columns of label-over-figure, wrapping to fewer columns on a narrow
   card, under an optional hairline in the palette's own line colour. */

.citrine-figrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--citrine-sp-4, 16px);
  margin: 0;
}

.citrine-figrow-ruled {
  border-top: 1px solid var(--citrine-line);
  padding-top: var(--citrine-sp-4, 16px);
}

.citrine-figrow-item {
  min-width: 0;
}

/* A field as a figure (a rate edited in place) has no message line under it, so antd's Form.Item
   bottom margin only made its box taller than its control and pushed the caption under it below the
   captions beside it. Same answer as `Topbar.css` and `PageHeader.css`: antd's own token, on its
   component roots. */
.citrine-figrow [class*='css-var-'] {
  --ant-form-item-margin-bottom: 0px;
}

.citrine-figrow-label {
  margin: 0;
  font-size: var(--citrine-font-size-12p5);
  color: var(--citrine-ink-3);
}

.citrine-figrow-value {
  margin: 0;
  font-size: 26px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--citrine-ink);
  font-variant-numeric: tabular-nums;
}

/* ── FileUpload ────────────────────────────────────────────────────────────────────────── */
/* UI kit — FileUpload. A focused drag/browse surface that composes FieldShell and Button. */
.citrine-file-upload {
  display: grid;
  gap: var(--citrine-space-10);
}

/* `variant="panel"` (the default): the dashed drop panel. `variant="button"` draws none of this —
   just the button, the status line and the file list. */
.citrine-file-upload-panel {
  padding: var(--citrine-space-14);
  border: 1px dashed var(--citrine-line-2);
  border-radius: var(--citrine-r);
  background: var(--citrine-surface-2);
}

.citrine-file-upload-panel.citrine-is-dragging {
  border-color: var(--citrine-data);
  background: var(--citrine-data-wash);
}

.citrine-file-upload-panel.citrine-is-error {
  border-color: var(--citrine-bad);
}

.citrine-file-upload-input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

.citrine-file-upload-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--citrine-space-10);
}

.citrine-file-upload-drop-copy,
.citrine-file-upload-status {
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-13p5);
}

.citrine-file-upload-status {
  font-weight: var(--citrine-font-weight-600);
}

/* `showFileNames` (default on): the chosen files, one per line. Names can be long and unbroken, so
   they wrap anywhere rather than push the field wider than its column. */
.citrine-file-upload-files {
  display: grid;
  gap: var(--citrine-space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.citrine-file-upload-file {
  color: var(--citrine-ink-2);
  font-size: var(--citrine-font-size-13p5);
  overflow-wrap: anywhere;
}

/* ── FloatingButton ────────────────────────────────────────────────────────────────────── */
/* UI kit — FloatingButton. A round action pinned bottom-right of the viewport. Tokens only. */

.citrine-floatbtn {
  position: fixed;
  inset-inline-end: var(--citrine-sp-4);
  inset-block-end: var(--citrine-sp-8);
  /* Above the sticky topbar / rail, below antd's overlays (popovers, modals start at 1000+), so a
     Popover hung from this button draws over it. */
  z-index: calc(var(--citrine-z-sticky) + 1);
  width: var(--citrine-floatbtn-size, 56px);
  height: var(--citrine-floatbtn-size, 56px);
  border: none;
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-accent);
  color: var(--citrine-accent-ink);
  box-shadow: var(--citrine-sh-lg);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  transition: background var(--citrine-duration-150) var(--citrine-easing-standard);
}

.citrine-floatbtn:hover {
  background: var(--citrine-accent-2);
}

/* `tone="brand"`: the app's own brand pair (the rail's hero), which no section hue remaps — so the
   button keeps one colour on every page. */
.citrine-floatbtn.citrine-floatbtn-brand {
  background: var(--citrine-side-hero);
  color: var(--citrine-side-hero-ink);
}
.citrine-floatbtn.citrine-floatbtn-brand:hover {
  background: color-mix(in srgb, var(--citrine-side-hero) 88%, var(--citrine-ink));
}

.citrine-floatbtn:focus-visible {
  outline: 2px solid var(--citrine-focus-ring);
  outline-offset: 3px;
}

.citrine-floatbtn-icon {
  display: inline-flex;
  font-size: 22px;
  line-height: 1;
}

.citrine-floatbtn-count {
  position: absolute;
  inset-block-start: -4px;
  inset-inline-end: -4px;
  min-width: 22px;
  height: 22px;
  padding: 0 var(--citrine-sp-1);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-bad);
  color: var(--citrine-ink-on-solid);
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-700);
  line-height: 22px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--citrine-surface);
}

.citrine-floatbtn-count-is-accent {
  background: var(--citrine-accent-deep);
}

/* Phone: AppShell shows its bottom bar here — rise above it (bar is 62px + the safe-area inset). */
@media (max-width: 900px) {
  .citrine-floatbtn {
    inset-block-end: calc(62px + env(safe-area-inset-bottom, 0px) + var(--citrine-sp-4));
  }
}

@media (prefers-reduced-motion: reduce) {
  .citrine-floatbtn {
    transition: none;
  }
}

/* ── FloatingStack ─────────────────────────────────────────────────────────────────────── */
/* UI kit — FloatingStack. A column pinned to a viewport corner. Tokens only.
   Positioning follows FloatingButton: the same insets, z-index and small-screen rise. */

.citrine-floatstack {
  /* How far the column's bottom edge stands off the window's foot (bottom placements), and its
     top edge off the window's head (top placements). */
  --citrine-floatstack-at-end: var(--citrine-sp-8);
  --citrine-floatstack-at-start: calc(var(--citrine-floatstack-top-clearance) + var(--citrine-sp-4));
  /* Extra rise for offset="aboveFloatingButton"; nothing otherwise. */
  --citrine-floatstack-lift: 0px;
  /* The tallest the window allows: never under the top band, never over the bottom one. The
     `maxHeight` prop sets --citrine-floatstack-max-height inline; it can lower this, not raise it. */
  --citrine-floatstack-cap: calc(
    100dvh - var(--citrine-floatstack-at-start) - var(--citrine-floatstack-at-end) -
      var(--citrine-floatstack-lift)
  );
  --citrine-floatstack-max-height: var(--citrine-floatstack-cap);

  position: fixed;
  /* FloatingButton's layer: above the sticky topbar / rail, below antd's overlays (1000+). */
  z-index: calc(var(--citrine-z-sticky) + 1);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--citrine-floatstack-gap);
  width: min(var(--citrine-floatstack-width), calc(100vw - 2 * var(--citrine-sp-4)));
  max-height: min(var(--citrine-floatstack-max-height), var(--citrine-floatstack-cap));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* A child keeps its own height; the column scrolls rather than squeezing a card. */
.citrine-floatstack > * {
  flex-shrink: 0;
}

.citrine-floatstack-is-bottom {
  inset-block-end: calc(var(--citrine-floatstack-at-end) + var(--citrine-floatstack-lift));
}

.citrine-floatstack-is-top {
  inset-block-start: var(--citrine-floatstack-at-start);
}

.citrine-floatstack-is-end {
  inset-inline-end: var(--citrine-sp-4);
}

.citrine-floatstack-is-start {
  inset-inline-start: var(--citrine-sp-4);
}

/* Clear a 56px FloatingButton in the same bottom corner, with a gap between the two. */
.citrine-floatstack-is-bottom.citrine-floatstack-above-button {
  --citrine-floatstack-lift: calc(var(--citrine-floatstack-button-clearance) + var(--citrine-sp-3));
}

/* Phone: AppShell shows its bottom bar here — rise above it exactly as FloatingButton does. */
@media (max-width: 900px) {
  .citrine-floatstack {
    --citrine-floatstack-at-end: calc(
      var(--citrine-floatstack-bar-clearance) + env(safe-area-inset-bottom, 0px) + var(--citrine-sp-4)
    );
  }
}

/* ── Grid ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Grid. Source: artifact `.grid` / `.g2..g6` / `.g-side*` / `.span*` block
   (citrine-reference.css) including the 1200/1080/820px responsive rules. Structure only — the
   three gap values are seeded inline from `theme.useToken()` in Grid.tsx, not declared here. */

.citrine-grid {
  display: grid;
}

.citrine-gap-sm {
  gap: var(--citrine-gap-sm);
}

.citrine-gap-md {
  gap: var(--citrine-gap-md);
}

.citrine-gap-lg {
  gap: var(--citrine-gap-lg);
}

.citrine-g2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.citrine-g3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.citrine-g4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.citrine-g6 {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

.citrine-g-side {
  grid-template-columns: minmax(0, 1fr) 330px;
  align-items: start;
}

.citrine-g-side-l {
  grid-template-columns: 310px minmax(0, 1fr);
  align-items: start;
}

/* rc.7 — the proportional twin of `.citrine-g-side`: a 2fr:1fr split with `stretch` alignment,
   for the main+side shape a consuming app's approval screen needed and `side`'s fixed 330px column couldn't
   give it. Collapses at the same breakpoint as `side`/`side-l`, below. */
.citrine-g-side-fluid {
  grid-template-columns: 2fr 1fr;
  align-items: stretch;
}

/* An uneven two-column split (`cols="split"`), for a form beside the list it feeds: `2` is 50 : 50
   and `side-left`'s fixed 310px is too narrow for a form at a desk width. Both shares are declared
   tokens (45 : 55 by default), so another ratio is an override on an ancestor or a caller class, not another rule.
   `minmax(0, …)` so a long word in either column cannot widen its track past its share. No
   `align-items`: `stretch` is Grid's default and the `align` prop already overrides it. Folds with
   the other two-pane shapes, below. */
.citrine-g-split {
  grid-template-columns: minmax(0, var(--citrine-grid-split-start)) minmax(0, var(--citrine-grid-split-end));
}

/* rc.9 — `minItemWidth`: fill the row with as many equal tracks as fit, instead of a named count.
   For a set whose size comes from DATA (one stage per row in a state table, one tile per menu
   manifest entry) no fixed count stays right once a row is inserted, and the author cannot know the
   number anyway. This is the shape a consumer's deleted stage-card class had; without it those 28
   stage cards fell back to one per row and Home became a 3,914px column.

   `min(…, 100%)` guards a minimum wider than the container: without it the track overflows instead
   of shrinking, which is the defect this branch has spent a week closing elsewhere.

   Note what is deliberately ABSENT — this class is NOT in the `--citrine-bp-sm` single-column
   collapse further down. Auto-fill already answers narrow screens continuously (at 390px a 150px
   minimum yields two columns), and forcing one column would re-create exactly the tall single-file
   stack this prop exists to prevent. */
.citrine-g-autofill {
  grid-template-columns: repeat(auto-fill, minmax(min(var(--citrine-grid-min, 180px), 100%), 1fr));
}

/* rc.5 — an arbitrary column count, for shapes the fixed g2/g3/g4/g6 set does not cover.
   Same grammar as `TileGrid`'s `--citrine-tilegrid-cols`: the component sets the custom property
   inline, so a new caller value never needs a new rule here. */
.citrine-g-custom {
  grid-template-columns: repeat(var(--citrine-grid-cols, 2), minmax(0, 1fr));
}

.citrine-span2 {
  grid-column: span 2;
}

.citrine-span3 {
  grid-column: span 3;
}

.citrine-span-full {
  grid-column: 1 / -1;
}

/* rc.7 — Gap 2: per-breakpoint `columns` counts. Each block reads only ITS OWN
   `--citrine-grid-cols-<bp>` property, falling back to the base `--citrine-grid-cols` — the
   "keep the next-larger value" forward-fill happens once, in `Grid.tsx`, not as a chain of nested
   `var()` fallbacks here. Written largest-breakpoint-first so a narrower, later rule always wins
   when several match at once, same ordering `.citrine-g6`/`.citrine-g4`/`.citrine-g-side*` already
   rely on below. `--citrine-bp-sm` (further down) still forces every Grid shape, `.citrine-g-custom`
   included, to a single column — that floor is not reopened by a per-breakpoint count. */
@media (max-width: 1280px) {
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-5xl, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 1200px) {
  .citrine-g6 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-4xl, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 1180px) {
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-3xl, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 1080px) {
  .citrine-g4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .citrine-g-side,
  .citrine-g-side-l,
  .citrine-g-side-fluid,
  .citrine-g-split {
    grid-template-columns: minmax(0, 1fr);
  }
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-2xl, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 1000px) {
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-xl, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 980px) {
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-lg, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .citrine-g-custom {
    grid-template-columns: repeat(var(--citrine-grid-cols-md, var(--citrine-grid-cols, 2)), minmax(0, 1fr));
  }
}

@media (max-width: 820px) {
  .citrine-g2,
  .citrine-g3,
  .citrine-g4,
  .citrine-g6,
  .citrine-g-custom {
    grid-template-columns: minmax(0, 1fr);
  }
  .citrine-span2,
  .citrine-span3 {
    grid-column: auto;
  }
}

/* An explicit track list, named by the caller. The presets above are track lists too; this is the
   same mechanism without a fixed shape baked into the kit. */
.citrine-g-template {
  grid-template-columns: var(--citrine-grid-template);
}

/* `narrowTemplate`: the caller's own phone track list, after the desk one so it wins there. */
@media (max-width: 820px) {
  .citrine-g-template-narrow {
    grid-template-columns: var(--citrine-grid-template-narrow);
  }
}

/* How a row treats an item shorter than its tallest sibling. There is deliberately no
 * `-stretch` rule: stretch is CSS Grid's own default, so a class for it would only ever restate
 * the browser's behaviour. See `GridAlign`. */
.citrine-g-align-start {
  align-items: start;
}

.citrine-g-align-center {
  align-items: center;
}

.citrine-g-align-end {
  align-items: end;
}

/* ── GroupHead ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — GroupHead. The words over one group of a list, with how many are in it.

   Draws no ground and no padding of its own: it sits over a bare list, not inside a box. The rule is
   optional and belongs to the head, not to the rows. */

.citrine-grouphead {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.citrine-grouphead-rule {
  padding-bottom: var(--citrine-sp-2, 8px);
  /* 2px, as the comps draw it. A hairline under a 20/700 figure reads as an accident rather than a
     division; the rule has to carry the same weight as the thing standing on it. */
  border-bottom: 2px solid var(--citrine-line, #e3e8f0);
}

.citrine-grouphead-line {
  display: flex;
  align-items: baseline;
  gap: var(--citrine-sp-3, 12px);
  min-width: 0;
}

/* `rule="trailing"`: a hairline on the title's line, from the end of the words to the far edge. */
.citrine-grouphead-rule-trailing .citrine-grouphead-line {
  align-items: center;
}
.citrine-grouphead-rule-trailing .citrine-grouphead-line::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--citrine-line, #e3e8f0);
}
/* A form section's heading reads a step above the fields' labels: 16px at 650, 8px of air over it
   and 14px before the first field — the section, not the field, is what the eye lands on. The
   title's size and weight are set after the base title rule, below. */
.citrine-grouphead.citrine-grouphead-rule-trailing {
  margin-block: var(--citrine-sp-2, 8px) 14px;
}
.citrine-grouphead-rule-trailing .citrine-grouphead-line {
  gap: 10px;
}

/* Sentence case at reading size. The shape four screens reached for by hand was a 10px uppercase
   eyebrow, which is a label for a field, not a heading over a list of things. */
.citrine-grouphead-title {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: var(--citrine-sp-2, 8px);
  min-width: 0;
  font-size: var(--citrine-font-size-15, 15px);
  font-weight: var(--citrine-font-weight-600, 600);
  line-height: 1.4;
  color: var(--citrine-ink, #14171f);
}
.citrine-grouphead-words {
  min-width: 0;
}
.citrine-grouphead-rule-trailing .citrine-grouphead-title {
  font-size: 16px;
  font-weight: 650;
}

/* The figure is DATA, so it takes the counting colour and tabular figures — the same treatment a
   count gets anywhere else in the kit.

   IT IS DRAWN AS A FIGURE, NOT AS BODY TEXT (owner ruling, 2026-09-14). This rule used to read
   "quieter in weight than the words, because the words say what the group is and the figure only
   says how big it is" — at 14px/600 it was body size and lighter than the text beside it. That is a
   defensible rule and it lost: the screens that use this head open ON the number ("four to call,
   one sent back"), and a count the eye has to hunt for cannot carry that. 20/700 is what the comps
   draw, and matches the StageStrip cell, so a count reads the same wherever the kit puts one. */
.citrine-grouphead-count {
  flex: none;
  font-size: var(--citrine-font-size-20, 20px);
  font-weight: var(--citrine-font-weight-700, 700);
  font-variant-numeric: tabular-nums;
  color: var(--citrine-data);
}
.citrine-grouphead-total {
  font-weight: var(--citrine-font-weight-500, 500);
  color: var(--citrine-ink-3, #6b768b);
}

.citrine-grouphead-actions {
  flex: none;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-sp-2, 8px);
}

.citrine-grouphead-caption {
  margin: 0;
  font-size: var(--citrine-font-size-13, 13px);
  line-height: 1.5;
  color: var(--citrine-ink-3, #6b768b);
}

/* The help mark rides beside the title, outside the heading element. */
.citrine-grouphead-hint {
  flex: none;
  display: inline-flex;
  align-items: center;
  align-self: center;
}

/* ── THE BAND ────────────────────────────────────────────────────────────────────────────────────
   A group heading that is a ROW OF A SHEET rather than a title standing over a block. It takes a
   tint and a hairline on both edges, so it reads as part of the sheet's run of lines; and title,
   help mark, caption and trailing chip go on ONE line, because a caption stacked under the title
   breaks that run. The chip keeps to the far end (`margin-inline-start: auto` on the slot). On a
   narrow sheet the line wraps and the caption takes the next line — measured on the reference
   mockup at 390px, where the band is title + mark, then the caption, then the chip at the far end.

   The tint is the ink at 5% over whatever the sheet is, so it stays a tint in both themes without a
   theme file. Opt-in: the stacked head is still right everywhere a group stands in open page space. */
.citrine-grouphead-band {
  padding: var(--citrine-grouphead-band-pad-y) var(--citrine-grouphead-band-pad-x);
  background: var(--citrine-grouphead-band-bg);
  border-top: 1px solid var(--citrine-grouphead-band-line);
  border-bottom: 1px solid var(--citrine-grouphead-band-line);
}
.citrine-grouphead-band .citrine-grouphead-line {
  flex-wrap: wrap;
  row-gap: var(--citrine-grouphead-band-gap-y);
  column-gap: var(--citrine-grouphead-band-gap-x);
}
.citrine-grouphead-band .citrine-grouphead-title {
  font-size: var(--citrine-grouphead-band-title-size);
}
.citrine-grouphead-band .citrine-grouphead-caption {
  min-width: 0;
  color: var(--citrine-grouphead-band-caption-ink);
}

/* ── Grow ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Grow. A flex child that takes the free space of its row. `min-inline-size: 0` lets it
   shrink below its content's width instead of pushing the row wider. In a two-sided `Spread` it is
   the side that gives way already; this makes that explicit for any row. */
.citrine-grow {
  flex: 1 1 auto;
  min-inline-size: 0;
}

/* `rule` — a connector between two items: asks for its basis, grows into the free space, shrinks
   to its floor. A hairline, centred on the row's cross axis. */
.citrine-grow-rule {
  flex: 1 1 var(--citrine-grow-rule-basis);
  min-inline-size: var(--citrine-grow-rule-min);
  block-size: var(--citrine-grow-rule-width);
  align-self: center;
  background: var(--citrine-grow-rule-color);
}

/* As the first of a two-sided `Spread`, the Spread's own rule for that side (a zero basis, floored
   at its longest word, capped at its one-line width — `Stack.css`) would win on specificity and
   stop a Grow at its content. Asking for Grow there is asking for the free space, so this restates
   it at a higher specificity. */
.citrine-spread > .citrine-grow:first-child:nth-last-child(2) {
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
}

/* ── HueScope ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — HueScope. A hue as scoped custom properties.
   See HueScope.tsx for why a class and why antd's button variables rather than a nested theme.

   ── EVERY NAME HERE IS A DECLARED TOKEN ────────────────────────────────────────────────────────
   `--citrine-h{n}*` (the page ramps) and `--citrine-side-h{n}-*` (the rail's hue roles) are declared
   in the tokens package, so they are defined whether or not a provider paints them. Their DEFAULTS
   alias existing roles:

     page ramp   --citrine-h{n}-deep → accent-deep, -wash → accent-wash, -btn → accent, …
     rail roles  --citrine-side-h{n}-dot → side-ink, -ink → side-ink-2, -hero → side-hero, …

   A palette that supplies ramps paints over both. One that does not keeps drawing its accent on the
   page and the rail's own inks on the rail. That is why no read below carries a fallback, and why no
   root-level copy of the accent is needed: the ramp tokens are resolved on `:root`, where nothing is
   remapped, and inherited from there as computed values, so remapping the accent inside a scope
   cannot form a cycle. */

/* No box: the scope is for inheritance only, so it cannot move a flex or grid layout. */
.citrine-huescope {
  display: contents;
}

/* ── One block per hue ──────────────────────────────────────────────────────────────────────────
   `--citrine-hue*` is what page components read; `--citrine-hue-rail-*` is what the rail reads. The
   rail never reads the page ramp: on a dark rail the page accent is navy on navy. */
/* ── Any colour: `hue="#7c3aed"` ─────────────────────────────────────────────────────────────────
   The same roles as a palette hue, DERIVED from one colour that `hueStyle()` sets inline as
   `--citrine-hue-custom` — a pipeline stage's saved colour, say. Each role is the colour mixed with
   the surface or the ink rather than a fixed shade, so it follows the theme: a wash is a light tint
   on a light page and a dark one on a dark page, the deep (used for words) leans toward the ink.
   The rail roles take the same values; the rail never shows a stage. `--citrine-hue-custom-on` (the
   words on a solid fill) is picked in `hueStyle()` by luminance — light colours get a dark shade of
   themselves, dark colours the kit's on-solid ink. */
.citrine-hued.citrine-hue-custom {
  --citrine-hue-custom-on-dark: color-mix(in srgb, var(--citrine-hue-custom) 22%, black);
  --citrine-hue: var(--citrine-hue-custom);
  --citrine-hue-wash: color-mix(in srgb, var(--citrine-hue-custom) 14%, var(--citrine-surface));
  --citrine-hue-line: color-mix(in srgb, var(--citrine-hue-custom) 40%, var(--citrine-surface));
  --citrine-hue-deep: color-mix(in srgb, var(--citrine-hue-custom) 65%, var(--citrine-ink));
  --citrine-hue-btn: var(--citrine-hue-custom);
  --citrine-hue-btn-ink: var(--citrine-hue-custom-on);
  --citrine-hue-btn-2: color-mix(in srgb, var(--citrine-hue-custom) 85%, var(--citrine-ink));
  --citrine-hue-rail-dot: var(--citrine-hue-custom);
  --citrine-hue-rail-ink: var(--citrine-hue-custom);
  --citrine-hue-rail-current: color-mix(in srgb, var(--citrine-hue-custom) 14%, var(--citrine-surface));
  --citrine-hue-rail-current-line: var(--citrine-hue-custom);
  --citrine-hue-rail-hero: var(--citrine-hue-custom);
  --citrine-hue-rail-hero-ink: var(--citrine-hue-custom-on);
  --citrine-hue-rail-hero-2: color-mix(in srgb, var(--citrine-hue-custom) 85%, var(--citrine-ink));
}

.citrine-hue-h0 {
  --citrine-hue: var(--citrine-h0);
  --citrine-hue-wash: var(--citrine-h0-wash);
  --citrine-hue-line: var(--citrine-h0-line);
  --citrine-hue-deep: var(--citrine-h0-deep);
  --citrine-hue-btn: var(--citrine-h0-btn);
  --citrine-hue-btn-ink: var(--citrine-h0-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h0-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h0-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h0-ink);
  --citrine-hue-rail-current: var(--citrine-side-h0-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h0-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h0-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h0-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h0-hero-2);
}

.citrine-hue-h1 {
  --citrine-hue: var(--citrine-h1);
  --citrine-hue-wash: var(--citrine-h1-wash);
  --citrine-hue-line: var(--citrine-h1-line);
  --citrine-hue-deep: var(--citrine-h1-deep);
  --citrine-hue-btn: var(--citrine-h1-btn);
  --citrine-hue-btn-ink: var(--citrine-h1-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h1-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h1-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h1-ink);
  --citrine-hue-rail-current: var(--citrine-side-h1-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h1-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h1-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h1-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h1-hero-2);
}

.citrine-hue-h2 {
  --citrine-hue: var(--citrine-h2);
  --citrine-hue-wash: var(--citrine-h2-wash);
  --citrine-hue-line: var(--citrine-h2-line);
  --citrine-hue-deep: var(--citrine-h2-deep);
  --citrine-hue-btn: var(--citrine-h2-btn);
  --citrine-hue-btn-ink: var(--citrine-h2-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h2-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h2-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h2-ink);
  --citrine-hue-rail-current: var(--citrine-side-h2-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h2-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h2-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h2-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h2-hero-2);
}

.citrine-hue-h3 {
  --citrine-hue: var(--citrine-h3);
  --citrine-hue-wash: var(--citrine-h3-wash);
  --citrine-hue-line: var(--citrine-h3-line);
  --citrine-hue-deep: var(--citrine-h3-deep);
  --citrine-hue-btn: var(--citrine-h3-btn);
  --citrine-hue-btn-ink: var(--citrine-h3-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h3-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h3-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h3-ink);
  --citrine-hue-rail-current: var(--citrine-side-h3-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h3-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h3-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h3-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h3-hero-2);
}

.citrine-hue-h4 {
  --citrine-hue: var(--citrine-h4);
  --citrine-hue-wash: var(--citrine-h4-wash);
  --citrine-hue-line: var(--citrine-h4-line);
  --citrine-hue-deep: var(--citrine-h4-deep);
  --citrine-hue-btn: var(--citrine-h4-btn);
  --citrine-hue-btn-ink: var(--citrine-h4-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h4-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h4-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h4-ink);
  --citrine-hue-rail-current: var(--citrine-side-h4-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h4-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h4-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h4-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h4-hero-2);
}

.citrine-hue-h5 {
  --citrine-hue: var(--citrine-h5);
  --citrine-hue-wash: var(--citrine-h5-wash);
  --citrine-hue-line: var(--citrine-h5-line);
  --citrine-hue-deep: var(--citrine-h5-deep);
  --citrine-hue-btn: var(--citrine-h5-btn);
  --citrine-hue-btn-ink: var(--citrine-h5-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h5-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h5-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h5-ink);
  --citrine-hue-rail-current: var(--citrine-side-h5-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h5-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h5-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h5-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h5-hero-2);
}

.citrine-hue-h6 {
  --citrine-hue: var(--citrine-h6);
  --citrine-hue-wash: var(--citrine-h6-wash);
  --citrine-hue-line: var(--citrine-h6-line);
  --citrine-hue-deep: var(--citrine-h6-deep);
  --citrine-hue-btn: var(--citrine-h6-btn);
  --citrine-hue-btn-ink: var(--citrine-h6-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h6-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h6-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h6-ink);
  --citrine-hue-rail-current: var(--citrine-side-h6-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h6-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h6-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h6-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h6-hero-2);
}

.citrine-hue-h7 {
  --citrine-hue: var(--citrine-h7);
  --citrine-hue-wash: var(--citrine-h7-wash);
  --citrine-hue-line: var(--citrine-h7-line);
  --citrine-hue-deep: var(--citrine-h7-deep);
  --citrine-hue-btn: var(--citrine-h7-btn);
  --citrine-hue-btn-ink: var(--citrine-h7-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h7-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h7-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h7-ink);
  --citrine-hue-rail-current: var(--citrine-side-h7-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h7-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h7-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h7-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h7-hero-2);
}

.citrine-hue-h8 {
  --citrine-hue: var(--citrine-h8);
  --citrine-hue-wash: var(--citrine-h8-wash);
  --citrine-hue-line: var(--citrine-h8-line);
  --citrine-hue-deep: var(--citrine-h8-deep);
  --citrine-hue-btn: var(--citrine-h8-btn);
  --citrine-hue-btn-ink: var(--citrine-h8-btn-ink);
  --citrine-hue-btn-2: var(--citrine-h8-btn-2);
  --citrine-hue-rail-dot: var(--citrine-side-h8-dot);
  --citrine-hue-rail-ink: var(--citrine-side-h8-ink);
  --citrine-hue-rail-current: var(--citrine-side-h8-current);
  --citrine-hue-rail-current-line: var(--citrine-side-h8-current-line);
  --citrine-hue-rail-hero: var(--citrine-side-h8-hero);
  --citrine-hue-rail-hero-ink: var(--citrine-side-h8-hero-ink);
  --citrine-hue-rail-hero-2: var(--citrine-side-h8-hero-2);
}

/* ── Inside a scope, the accent IS the hue ─────────────────────────────────────────────────────
   Links, selected rows, chips, focus and every other Citrine rule that reads an accent slot take the
   section's colour from here, with no per-component code. `--citrine-accent` becomes the DEEP member,
   not the mid: it is drawn as text (links) and as a fill under `--citrine-accent-ink`, and the ramp's
   deep clears 4.5:1 both ways in both modes (`palette-extras.test.ts` checks it on the fixture).
   The hover step moves toward the page ink, the direction that raises contrast in both modes.
   `--citrine-accent-ink` is not remapped. */
.citrine-hued {
  --citrine-accent: var(--citrine-hue-deep);
  --citrine-accent-2: color-mix(in srgb, var(--citrine-hue-deep) 80%, var(--citrine-ink));
  --citrine-accent-deep: var(--citrine-hue-deep);
  --citrine-accent-wash: var(--citrine-hue-wash);
  --citrine-accent-line: var(--citrine-hue-line);
}

/* ── The primary button takes the ramp's PAIR, never deep ────────────────────────────────────────
   antd paints a solid primary from four variables it declares on the button itself, inside
   `:where(.css-var-…)` — so (0,4,0) here beats it whatever order the sheets load in. The words'
   hover/active variables are `var(--ant-btn-text-color)` on the same element and follow this one.
   Hover and press are the ramp's derived `-btn-2`, which moves AWAY from the ink (palette.ts). */
.citrine-hued .ant-btn.ant-btn-color-primary.ant-btn-variant-solid {
  --ant-btn-color-base: var(--citrine-hue-btn);
  --ant-btn-color-hover: var(--citrine-hue-btn-2);
  --ant-btn-color-active: var(--citrine-hue-btn-2);
  --ant-btn-text-color: var(--citrine-hue-btn-ink);
}

/* ── antd's links: plain anchors and link/text Buttons ───────────────────────────────────────────
   antd declares its tokens on EVERY component root (the `css-var-…` class), so a value set on the
   scope element would be shadowed by the first antd root below it. The attribute selector reaches
   each of those roots at (0,2,0) against antd's (0,1,0). A link-variant Button reads the same
   `--ant-color-link`; an outlined default Button's words are the Button's own
   `--ant-button-default-color`, which `theme.ts` seeds with the accent.

   antd's OWN selected states — Checkbox, Radio, Switch, a focused field's edge, a selected Table row,
   a Tabs ink bar — read `--ant-color-primary*`, and a scope that stopped at links would leave them on
   the app accent beside a section-coloured page (a consumer that draws antd directly has no other
   route to it). The fill is the ramp's MID, not deep: antd draws a WHITE mark on it (a checkmark, a
   Switch knob), and in dark the deep member is the ramp's light tone — white on it is 1.6:1. The mid
   is the same in both modes and carries white at 3:1 or better, the non-text floor, for every hue.
   Words antd draws in the primary colour take `-text`, the deep. The solid primary Button is not
   touched by this: the rule above sets its own variables, at higher specificity.

   The second selector reaches a portalled popup's OWN root, which carries the hue class only when
   `CitrineProvider hueStates` is on (HueProvider then puts Select/picker popups in the scope); it
   never matches otherwise. */
.citrine-hued [class*='css-var-'],
.citrine-hued.citrine-hue-states[class*='css-var-'] {
  --ant-color-link: var(--citrine-hue-deep);
  --ant-color-link-hover: var(--citrine-accent-2);
  --ant-color-link-active: var(--citrine-accent-2);
  /* A palette's `outlineInk` keeps an outlined button's words one ink on every page. */
  --ant-button-default-color: var(--citrine-outline-ink, var(--citrine-hue-deep));
  --ant-color-primary: var(--citrine-hue);
  --ant-color-primary-hover: var(--citrine-hue);
  --ant-color-primary-active: var(--citrine-hue-deep);
  --ant-color-primary-bg: var(--citrine-hue-wash);
  --ant-color-primary-bg-hover: var(--citrine-hue-wash);
  --ant-color-primary-border: var(--citrine-hue-line);
  --ant-color-primary-border-hover: var(--citrine-hue-line);
  --ant-color-primary-text: var(--citrine-hue-deep);
  --ant-color-primary-text-hover: var(--citrine-accent-2);
  --ant-color-primary-text-active: var(--citrine-accent-2);
  --ant-control-item-bg-active: var(--citrine-hue-wash);
  --ant-control-item-bg-active-hover: var(--citrine-hue-wash);
  /* Components that resolve their own copy of the colour at theme time, as a literal, so the remap
     above cannot reach them: a checked Radio's fill, a selected Table row's ground, a Tabs ink bar and
     its selected label, the current Pagination number, and the Breadcrumb (whose ink `theme.ts` sets
     to the accent as a contrast floor). Measured 2026-09-28 on a consumer: each still drew the app
     accent inside a hued page. The bar is the MID, like every other antd mark; words take the deep. */
  --ant-radio-radio-bg-color: var(--citrine-hue);
  --ant-table-row-selected-bg: var(--citrine-hue-wash);
  --ant-table-row-selected-hover-bg: var(--citrine-hue-wash);
  --ant-tabs-ink-bar-color: var(--citrine-hue);
  --ant-tabs-item-selected-color: var(--citrine-hue-deep);
  --ant-tabs-item-hover-color: var(--citrine-accent-2);
  --ant-tabs-item-active-color: var(--citrine-accent-2);
  --ant-pagination-item-active-color: var(--citrine-hue-deep);
  --ant-pagination-item-active-color-hover: var(--citrine-accent-2);
  --ant-breadcrumb-item-color: var(--citrine-hue-deep);
  --ant-breadcrumb-link-color: var(--citrine-hue-deep);
  --ant-breadcrumb-link-hover-color: var(--citrine-accent-2);
}

/* ── Opt-in state colours (`CitrineProvider hueStates` adds `citrine-hue-states`) ────────────────
   Off, a hued scope draws exactly what it did before. (0,3,0), over antd's literals. */
.citrine-hued.citrine-hue-states [class*='css-var-'],
.citrine-hued.citrine-hue-states[class*='css-var-'] {
  /* The outlined DEFAULT Button's hover and press. antd derives them from `colorPrimaryHover` /
     `-Active` — which `theme.ts` sets on the Button as the palette's own accent pair — and emits
     them as literals, so the remap above never reached them. Measured on a consumer 2026-10-07: at
     rest the words were the hue's deep, but hovered or pressed the words and the edge jumped to the
     app accent. Words take the link's hover step (toward the ink, which raises contrast both ways);
     the edge takes the mid on hover and the deep on press, as `--ant-color-primary-hover` / `-active`
     do above. Its focus outline is `--ant-color-primary-border`, the hue's line, already. */
  --ant-button-default-hover-color: var(--citrine-accent-2);
  --ant-button-default-hover-border-color: var(--citrine-hue);
  --ant-button-default-active-color: var(--citrine-accent-2);
  --ant-button-default-active-border-color: var(--citrine-hue-deep);
  /* A field's hover and focus edge, and its focus halo — Input, InputNumber, Select, DatePicker —
     resolved as literals the same way (measured: the app accent edge inside a hue). The edge is the
     mid, antd's own `colorPrimary` role; the halo is the hue's line in the place of the palette's soft
     focus ring, at antd's own 2px outline width (`--citrine-space-2`). */
  --ant-input-hover-border-color: var(--citrine-hue);
  --ant-input-active-border-color: var(--citrine-hue);
  --ant-input-active-shadow: 0 0 0 var(--citrine-space-2) var(--citrine-hue-line);
  --ant-input-number-hover-border-color: var(--citrine-hue);
  --ant-input-number-active-border-color: var(--citrine-hue);
  --ant-input-number-active-shadow: 0 0 0 var(--citrine-space-2) var(--citrine-hue-line);
  --ant-select-hover-border-color: var(--citrine-hue);
  --ant-select-active-border-color: var(--citrine-hue);
  --ant-select-active-outline-color: var(--citrine-hue-line);
  --ant-date-picker-hover-border-color: var(--citrine-hue);
  --ant-date-picker-active-border-color: var(--citrine-hue);
  --ant-date-picker-active-shadow: 0 0 0 var(--citrine-space-2) var(--citrine-hue-line);
  /* A Select's chosen option, in its portalled list (HueProvider puts the list in the scope). */
  --ant-select-option-selected-bg: var(--citrine-hue-wash);
  /* A picker's in-range days. */
  --ant-date-picker-cell-active-with-range-bg: var(--citrine-hue-wash);
}

/* ── A picker's selected day is a FILL UNDER WORDS ───────────────────────────────────────────────
   antd draws the selected day's number in `colorTextLightSolid` on `colorPrimary`. The mid that
   `colorPrimary` takes above is a 3:1 MARK colour, not a text ground (white on a vivid mid measured
   3.2:1 on the fixture), so inside a picker's panel the primary is the ramp's button PAIR instead —
   the fill and ink every hue carries at 4.5:1 in both modes — as the solid primary Button does. The
   today ring reads the same `colorPrimary` and takes the button fill with it. (0,4,0), over the
   blocks above. Opt-in, as above. */
.citrine-hued.citrine-hue-states.ant-picker-dropdown[class*='css-var-'],
.citrine-hued.citrine-hue-states .ant-picker-dropdown[class*='css-var-'] {
  --ant-color-primary: var(--citrine-hue-btn);
  --ant-color-text-light-solid: var(--citrine-hue-btn-ink);
}

/* ── IconTile ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — IconTile. A glyph in a 34px rounded tile. */

.citrine-icontile {
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 8px;
  background: var(--citrine-hue-wash, var(--citrine-accent-wash));
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
  font-size: 16px;
}

.citrine-icontile.citrine-icontile-neutral {
  background: var(--citrine-surface-3);
  color: var(--citrine-ink-2);
}

/* ── Ink ───────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Ink. Colour only: a status tone or a palette hue on a run of text or an icon.
   The plain class draws the tone's deep, the text-safe member; with `mark` it draws the mid, for a
   glyph. A hue outside a palette with ramps draws the kit's neutral aliases, which hold the same
   floors. */

.citrine-ink-ok {
  color: var(--citrine-ok-deep);
}
.citrine-ink-ok.citrine-ink-mark {
  color: var(--citrine-ok);
}

.citrine-ink-warn {
  color: var(--citrine-warn-deep);
}
.citrine-ink-warn.citrine-ink-mark {
  color: var(--citrine-warn);
}

.citrine-ink-bad {
  color: var(--citrine-bad-deep);
}
.citrine-ink-bad.citrine-ink-mark {
  color: var(--citrine-bad);
}

.citrine-ink-info {
  color: var(--citrine-info-deep);
}
.citrine-ink-info.citrine-ink-mark {
  color: var(--citrine-info);
}

.citrine-ink-h0 {
  color: var(--citrine-h0-deep);
}
.citrine-ink-h0.citrine-ink-mark {
  color: var(--citrine-h0);
}

.citrine-ink-h1 {
  color: var(--citrine-h1-deep);
}
.citrine-ink-h1.citrine-ink-mark {
  color: var(--citrine-h1);
}

.citrine-ink-h2 {
  color: var(--citrine-h2-deep);
}
.citrine-ink-h2.citrine-ink-mark {
  color: var(--citrine-h2);
}

.citrine-ink-h3 {
  color: var(--citrine-h3-deep);
}
.citrine-ink-h3.citrine-ink-mark {
  color: var(--citrine-h3);
}

.citrine-ink-h4 {
  color: var(--citrine-h4-deep);
}
.citrine-ink-h4.citrine-ink-mark {
  color: var(--citrine-h4);
}

.citrine-ink-h5 {
  color: var(--citrine-h5-deep);
}
.citrine-ink-h5.citrine-ink-mark {
  color: var(--citrine-h5);
}

.citrine-ink-h6 {
  color: var(--citrine-h6-deep);
}
.citrine-ink-h6.citrine-ink-mark {
  color: var(--citrine-h6);
}

.citrine-ink-h7 {
  color: var(--citrine-h7-deep);
}
.citrine-ink-h7.citrine-ink-mark {
  color: var(--citrine-h7);
}

.citrine-ink-h8 {
  color: var(--citrine-h8-deep);
}
.citrine-ink-h8.citrine-ink-mark {
  color: var(--citrine-h8);
}

/* The hue in scope: a `HueScope` or hued `Card` sets `--citrine-hue*` on its element; outside
   any, the accent. */
.citrine-ink-scope {
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
}
.citrine-ink-scope.citrine-ink-mark {
  color: var(--citrine-hue, var(--citrine-accent));
}

/* A focusable mark (a labelled star with a tooltip) shows where focus is. */
.citrine-ink:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-2);
}

/* ── InputAffix ────────────────────────────────────────────────────────────────────────── */
/* UI kit — InputAffix. Group B wave 7a.
 *
 * The row itself is antd's `Space.Compact`, so `display: flex` and the wrapped control's inner
 * corner are both antd's job now — the old `.citrine-faffix { display: flex }` and the
 * `.citrine-faffix .citrine-ctrl` corner override are gone, and with them the last place this
 * component reached into another component's base class to fix a corner.
 *
 * What is left is the two affix blocks, which `Space.Compact` cannot draw because a plain `<span>`
 * is not an antd control and takes nothing from its compact context. Every value they use is seeded
 * from `theme.useToken()` in `InputAffix.tsx` — antd's own addon values. `--citrine-surface-3`,
 * `--citrine-line-2`, `--citrine-ink-3`, `--citrine-r-sm`, `--citrine-space-13`,
 * `--citrine-font-size-13` and `--citrine-font-weight-600` have all left this file. */

.citrine-faffix-pre,
.citrine-faffix-suf {
  display: grid;
  place-items: center;
  padding: 0 var(--citrine-faffix-pad);
  font-size: var(--citrine-faffix-size);
  color: var(--citrine-faffix-color);
  background: var(--citrine-faffix-bg);
  border: 1px solid var(--citrine-faffix-line);
  white-space: nowrap;
}

/* Only the OUTER corner is rounded — the inner edge butts up against the control. */
.citrine-faffix-pre {
  border-right: 0;
  border-radius: var(--citrine-faffix-radius) 0 0 var(--citrine-faffix-radius);
}

.citrine-faffix-suf {
  border-left: 0;
  border-radius: 0 var(--citrine-faffix-radius) var(--citrine-faffix-radius) 0;
}

/* ── Kanban ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Kanban. Source: artifact `.kanban`, `.lane`, `.lane-head`, `.kcard`, `.kcard-title`,
   `.kcard-line`, `.kcard-line b` (citrine-reference.css).

   Group C (2.0): structure unchanged — there is no antd pipeline board. Colours and metrics now
   come from antd's live theme via `antdTheme.useToken()` in `Kanban.tsx`, handed down as
   `--citrine-kanban-*` / `--citrine-lane-*` / `--citrine-kcard-*` custom properties. Every fallback
   is the Citrine token the rule used to read.

   The 272px lane width, the horizontal scroll, the 8px empty-lane drop target and the 24px "move
   to" button are STRUCTURAL: they are what makes a board a board. They stay literal.

   `KanbanBoard` draws this row inside a `ScrollX`, which lays it out at its full width and does the
   scrolling (with the scrollbar pinned to the window's bottom); the row's own `overflow-x` then
   never engages. It stays for a page that still uses the bare class without the frame. */

.citrine-kanban {
  display: flex;
  gap: var(--citrine-kanban-gap, var(--citrine-sp-4));
  overflow-x: auto;
  padding-bottom: var(--citrine-kanban-pad-bottom, var(--citrine-sp-2));
}

.citrine-lane {
  flex: 0 0 272px;
  width: 272px;
  background: var(--citrine-lane-bg, var(--citrine-surface-3));
  border-radius: var(--citrine-lane-radius, var(--citrine-r));
  padding: var(--citrine-lane-pad, var(--citrine-sp-3));
}

.citrine-lane-head {
  display: flex;
  align-items: center;
  gap: var(--citrine-lane-head-gap, var(--citrine-sp-2));
  padding: 2px 4px var(--citrine-lane-pad, var(--citrine-sp-3));
}

/* Sticky heads (`KanbanBoard stickyHeads`, on by default). The lane heads are drawn a second time,
   as a row of "caps" above the lanes and OUTSIDE the sideways scroll box, and that row is pinned
   with plain `position: sticky` under the Topbar (`--citrine-kanban-heads-top`, measured). Its
   containing block is the frame, so it parks at the board's foot and leaves with it. Nothing between
   the frame and the page's scroll may set `overflow` (see ScrollX.css). The row's view clips and is
   scrolled sideways in step with the board's view from `Kanban.tsx`. */
.citrine-kanban-frame {
  min-inline-size: 0;
}

.citrine-kanban-heads {
  position: sticky;
  top: var(--citrine-kanban-heads-top, 0px);
  /* Under the Topbar (which is at --citrine-z-sticky), over the cards scrolling past. */
  z-index: calc(var(--citrine-z-sticky) - 1);
  background: var(--citrine-app-canvas, var(--citrine-page));
}

.citrine-kanban-heads-view {
  overflow: hidden;
}

/* The same geometry as the row of lanes — fixed 272px lanes in the board's gap — so every cap sits
   exactly over its lane. */
.citrine-kanban-heads-row {
  display: flex;
  gap: var(--citrine-kanban-gap, var(--citrine-sp-4));
  inline-size: max-content;
  min-inline-size: 100%;
}

/* A cap is the top of its lane: the lane's ground and top corners, so cap + lane read as one. */
.citrine-lane-cap {
  flex: 0 0 272px;
  width: 272px;
  background: var(--citrine-lane-bg, var(--citrine-surface-3));
  border-start-start-radius: var(--citrine-lane-radius, var(--citrine-r));
  border-start-end-radius: var(--citrine-lane-radius, var(--citrine-r));
  padding: var(--citrine-lane-pad, var(--citrine-sp-3)) var(--citrine-lane-pad, var(--citrine-sp-3)) 0;
}
.citrine-kanban-heads-row > .citrine-lane-cap.citrine-lane-hued {
  border-block-end: 0;
}

/* Under a cap the lane loses its top corners and top padding — the cap carries them — and its own
   head stays for screen readers only. */
.citrine-kanban-capped > .citrine-lane {
  /* The containing block of the lane's hidden head (`position: absolute` below). Without it that
     head is placed against `ScrollX`'s frame, OUTSIDE the view that clips the lanes, so the far
     lanes' hidden heads stuck out past the board and widened the whole page. */
  position: relative;
  border-start-start-radius: 0;
  border-start-end-radius: 0;
  padding-top: 0;
}
.citrine-kanban-capped > .citrine-lane.citrine-lane-hued {
  border-block-start: 0;
}
.citrine-lane-head.citrine-lane-head-pinned {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* `hue`: the lane in its stage's colour — a 12% tint of the hue over the surface, a hairline of it,
   and a dot of it before the name. */
.citrine-lane.citrine-lane-hued,
.citrine-lane-cap.citrine-lane-hued {
  background: color-mix(in srgb, var(--citrine-hue, var(--citrine-accent)) 12%, var(--citrine-surface));
  border: 1px solid color-mix(in srgb, var(--citrine-hue, var(--citrine-accent)) 22%, transparent);
}
.citrine-lane-dot {
  flex: none;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  background: var(--citrine-hue, var(--citrine-accent));
}

.citrine-lane-count {
  margin-left: auto;
  font-size: var(--citrine-lane-count-size, var(--citrine-font-size-11));
  font-weight: var(--citrine-lane-count-weight, var(--citrine-font-weight-700));
  color: var(--citrine-lane-count-ink, var(--citrine-ink-3));
  font-variant-numeric: tabular-nums;
}

/* Kept deliberately small: an EMPTY lane still has to be a reachable drop target. */
.citrine-lane-drop {
  min-height: 8px;
}

.citrine-kcard {
  background: var(--citrine-kcard-bg, var(--citrine-surface));
  border-radius: var(--citrine-kcard-radius, var(--citrine-r-sm));
  padding: var(--citrine-kcard-pad, var(--citrine-sp-3));
  margin-bottom: var(--citrine-kcard-gap, var(--citrine-sp-2));
  box-shadow: var(--citrine-kcard-shadow, var(--citrine-sh));
  cursor: default;
}

.citrine-kcard[role='button'] {
  cursor: pointer;
}

.citrine-kcard:focus-visible {
  outline: var(--citrine-kcard-focus-width, 2px) solid var(--citrine-kcard-selected-line, var(--citrine-data));
  outline-offset: 2px;
}

.citrine-kcard.citrine-is-selected {
  box-shadow:
    0 0 0 2px var(--citrine-kcard-selected-ring, var(--citrine-data-wash)),
    var(--citrine-kcard-shadow, var(--citrine-sh));
  border: var(--citrine-kcard-line-width, 1px) solid var(--citrine-kcard-selected-line, var(--citrine-data));
}

.citrine-kcard.citrine-is-dragging {
  opacity: 0.6;
}

.citrine-kcard-title {
  font-weight: var(--citrine-kcard-title-weight, var(--citrine-font-weight-700));
  font-size: var(--citrine-kcard-title-size, var(--citrine-font-size-13p5));
  margin-bottom: var(--citrine-kcard-gap, var(--citrine-sp-2));
}

.citrine-kcard-line {
  font-size: var(--citrine-kcard-line-size, var(--citrine-font-size-11p5));
  color: var(--citrine-kcard-line-ink, var(--citrine-ink-3));
  display: flex;
  gap: var(--citrine-kcard-gap, var(--citrine-space-6));
}

.citrine-kcard-line b {
  color: var(--citrine-kcard-value-ink, var(--citrine-ink-2));
  font-weight: var(--citrine-kcard-title-weight, var(--citrine-font-weight-600));
}

.citrine-kcard-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--citrine-kcard-gap, var(--citrine-sp-2));
}

.citrine-kcard-move {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--citrine-kcard-move-radius, var(--citrine-r-xs));
  border: var(--citrine-kcard-line-width, 1px) solid transparent;
  background: transparent;
  color: var(--citrine-kcard-move-ink, var(--citrine-ink-3));
  cursor: pointer;
}

.citrine-kcard-move:hover {
  background: var(--citrine-kcard-move-hover-bg, var(--citrine-surface-3));
  color: var(--citrine-kcard-move-hover-ink, var(--citrine-ink));
}

.citrine-kcard-move svg {
  width: 15px;
  height: 15px;
}

.citrine-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── KeyValue ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — the three KeyValue shapes antd's own props cannot express.
 *
 * The component is otherwise stock `Descriptions` and carries no styling of its own; these rules
 * exist because a consuming app writes no CSS, so a shape it cannot ask for through a prop is a
 * shape it cannot have at all.
 */

/* ALIGN. The value cell takes one of two shapes, and this rule moves the value in both. Side by
 * side, `.ant-descriptions-item-content` is an `inline-flex` span that antd grows to fill the row
 * (`flex: 1`), so it has no free space for an auto margin to take and `justify-content` is what moves
 * its content. Bordered or stacked, the cell is a `table-cell`, where `text-align` does it — and it
 * also lines up a wrapped value's lines on the end edge. */
.citrine-kv-align-end {
  justify-content: flex-end;
  text-align: end;
}

/* A VALUE AT THE END KEEPS ITS OWN WIDTH. Side by side, antd lets the label hold its full width
 * and squeezes the value (`flex: 1`, basis 0) into what is left, so on a narrow card a phone number
 * breaks between its digits while the label beside it stays whole. A figures column wants the
 * opposite: the value keeps its natural width (up to 65% of the row, past which it still wraps)
 * and the label yields, clipped. The auto start margin carries the value cell to the row's far
 * edge. The doubled root class outranks antd's three-class selectors. */
.citrine-kv.citrine-kv-end .ant-descriptions-item-container > .ant-descriptions-item-label {
  min-inline-size: 0;
  overflow: hidden;
}
.citrine-kv.citrine-kv-end .ant-descriptions-item-container > .ant-descriptions-item-content {
  flex: 0 0 auto;
  max-inline-size: 65%;
  margin-inline-start: auto;
}

/* RULE. One hairline per pair, and none under the last — distinct from antd's `bordered`, which
 * draws a full grid with vertical edges too. `:not(:last-child)` is on the ROW, so a wrapped
 * multi-column row still gets exactly one line under it rather than one per cell. */
.citrine-kv-ruled .ant-descriptions-row:not(:last-child) > .ant-descriptions-item {
  padding-block-end: var(--citrine-sp-2, 8px);
  border-block-end: 1px solid var(--citrine-kv-rule-line, var(--citrine-line));
}

/* DENSITY. antd's `size` scale bottoms out at `small`, which the component already passes, so a
 * tighter row has to be drawn rather than forwarded. Only the block spacing moves: the inline
 * padding is what keeps a bordered table readable, and shrinking both makes a ruled list feel
 * cramped rather than compact. */
.citrine-kv-tight .ant-descriptions-row > .ant-descriptions-item {
  padding-block-end: var(--citrine-sp-1, 4px);
}

.citrine-kv-tight.citrine-kv-ruled .ant-descriptions-row:not(:last-child) > .ant-descriptions-item {
  padding-block-end: var(--citrine-sp-1, 4px);
}

/* ── Launcher ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Launcher. Destinations as tiles you hit without aiming.

   Doors: a few large tiles — glyph, label and breakdown, figure at the right. Stages: small tiles,
   figure over label. Under 480px of the Launcher's own width the doors stack and the stage tiles go
   three across, square.

   Two surfaces, chosen on the root: `.citrine-launcher-plain` (the default) and
   `.citrine-launcher-paper` (opt-in). The layout rules are shared; each surface owns its colour,
   edge, depth and padding, so neither reaches into the other.

   ── PLAIN: CITRINE'S OWN TILE ─────────────────────────────────────────────────────────────────────
   The surface inside a hairline. Live tiles are solid; quiet ones (nothing in them yet) are dashed and
   grey so the eye goes straight to the live figures. No shadow and no lift: under the pointer the edge
   darkens a little and the face takes a breath of the accent.

   ── PAPER: THE OPT-IN ─────────────────────────────────────────────────────────────────────────────
   A tile is a sheet of its hue's wash with a 4px spine of the hue down its leading edge, lifted off
   the page by a real drop shadow — offset and blurred, never a zero-offset halo, which reads as a
   glow rather than as depth. It lifts a little further under the pointer. Doors and stage tiles
   cast the same four-layer shadow, so a dashboard reads as one set of sheets; a door ranks by its
   size and its bigger lift, not by a deeper shadow.

   Every paper colour is read LIVE on the tile, not through a launcher token, and that is
   load-bearing: a token is resolved on `:root`, where no hue is in scope, so
   `--citrine-launcher-face: {accent-wash}` would paint every tile the global accent. Read directly,
   the accent roles ARE the hue inside `.citrine-hued` (HueScope.css), and are the accent everywhere
   else — the hue fallback for free. The shadow tokens tint with `currentColor` for the same reason:
   it is resolved where the shadow is drawn, so a hued tile casts a shadow of its own deep ink. Dark
   restates them in plain black — a light ink under a dark tile would glow.

   An EMPTY paper tile (`quiet`) keeps its shadow. It ranks lower by a paler face, a thin ring and a
   lighter figure, and it does not lift. It is still a real link. */

.citrine-launcher {
  container: citrine-launcher / inline-size;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-3);
}

.citrine-launcher-title {
  margin: 0;
  font-size: var(--citrine-font-size-15);
  font-weight: var(--citrine-font-weight-700);
  line-height: 1.3;
  color: var(--citrine-accent);
  text-wrap: balance;
}

.citrine-launcher-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--citrine-space-10);
}

.citrine-launcher-cell {
  display: flex;
  min-width: 0;
}

.citrine-launcher-tile {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  text-decoration: none;
}
.citrine-launcher-tile:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: 2px;
}

.citrine-launcher-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.citrine-launcher-tag {
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-600);
  line-height: 1.3;
}
.citrine-launcher-label {
  font-size: var(--citrine-font-size-13);
  font-weight: var(--citrine-font-weight-600);
  line-height: 1.3;
}
.citrine-launcher-sub {
  margin-top: var(--citrine-space-2);
  font-size: var(--citrine-font-size-13);
  line-height: 1.4;
}
.citrine-launcher-figure {
  font-size: var(--citrine-font-size-28);
  font-weight: var(--citrine-font-weight-700);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ── stages: the figure over the label ─────────────────────────────────────────────────────────── */
.citrine-launcher-stages .citrine-launcher-grid {
  grid-template-columns: repeat(auto-fill, minmax(136px, 1fr));
}
.citrine-launcher-stages .citrine-launcher-tile {
  flex-direction: column;
  justify-content: space-between;
  gap: var(--citrine-space-10);
  aspect-ratio: 1.3;
}
.citrine-launcher-stages .citrine-launcher-figure {
  order: -1;
}

/* ── doors: glyph, words, figure ───────────────────────────────────────────────────────────────── */
.citrine-launcher-doors .citrine-launcher-grid {
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--citrine-space-14);
}
.citrine-launcher-doors .citrine-launcher-tile {
  align-items: center;
  gap: var(--citrine-space-14);
}
.citrine-launcher-icon {
  flex: none;
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
}
.citrine-launcher-icon svg {
  width: 26px;
  height: 26px;
}
.citrine-launcher-doors .citrine-launcher-text {
  flex: 1;
}
.citrine-launcher-doors .citrine-launcher-label {
  font-size: var(--citrine-font-size-15);
}
.citrine-launcher-doors .citrine-launcher-figure {
  flex: none;
  font-size: var(--citrine-font-size-32);
}

@container citrine-launcher (max-width: 479px) {
  .citrine-launcher-stages .citrine-launcher-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--citrine-sp-2);
  }
  .citrine-launcher-stages .citrine-launcher-tile {
    aspect-ratio: 1;
  }
  .citrine-launcher-doors .citrine-launcher-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .citrine-launcher-icon {
    width: 44px;
    height: 44px;
  }
  .citrine-launcher-doors .citrine-launcher-figure {
    font-size: var(--citrine-font-size-28);
  }
}

/* ══ PLAIN (default) ══════════════════════════════════════════════════════════════════════════════ */

.citrine-launcher-plain .citrine-launcher-tile {
  background: var(--citrine-surface);
  border: 1px solid var(--citrine-line);
  border-radius: var(--citrine-radius-14);
  color: var(--citrine-ink);
  transition:
    background-color var(--citrine-duration-140) var(--citrine-launcher-easing),
    border-color var(--citrine-duration-140) var(--citrine-launcher-easing);
}
/* The edge of an EMPTY tile, and of a live tile under the pointer: a light mix, so the empty stages
   recede and the live figures carry the page. `--citrine-line-2` alone rendered as the heaviest
   outline on the screen in a dark-accented theme, which inverted the whole point. */
.citrine-launcher-plain .citrine-launcher-tile:hover {
  border-color: color-mix(
    in srgb,
    var(--citrine-line-2) var(--citrine-launcher-plain-edge-mix),
    var(--citrine-line)
  );
  background: color-mix(
    in srgb,
    var(--citrine-accent) var(--citrine-launcher-plain-hover-mix),
    var(--citrine-surface)
  );
}
.citrine-launcher-plain .citrine-launcher-tile:active {
  background: var(--citrine-accent-wash);
}
.citrine-launcher-plain .citrine-launcher-tag {
  color: var(--citrine-data);
}
.citrine-launcher-plain .citrine-launcher-label {
  color: var(--citrine-ink);
}
.citrine-launcher-plain .citrine-launcher-sub {
  color: var(--citrine-ink-3);
}
.citrine-launcher-plain .citrine-launcher-figure {
  color: var(--citrine-data);
}

/* Nothing here yet: the tile recedes but stays a real link. */
.citrine-launcher-plain .citrine-launcher-quiet {
  background: transparent;
  border-style: dashed;
  border-color: color-mix(
    in srgb,
    var(--citrine-line-2) var(--citrine-launcher-plain-edge-mix),
    var(--citrine-line)
  );
}
/* An empty step where money moves keeps its meaning in the edge: the data green, dashed. */
.citrine-launcher-plain .citrine-launcher-money.citrine-launcher-quiet {
  border-color: var(--citrine-data-line);
}
.citrine-launcher-plain .citrine-launcher-quiet .citrine-launcher-figure {
  font-weight: var(--citrine-font-weight-500);
  color: var(--citrine-ink-3);
}
.citrine-launcher-plain .citrine-launcher-quiet .citrine-launcher-label {
  font-weight: var(--citrine-font-weight-500);
  color: var(--citrine-ink-2);
}

.citrine-launcher-plain.citrine-launcher-stages .citrine-launcher-tile {
  padding: var(--citrine-space-14);
}
.citrine-launcher-plain.citrine-launcher-doors .citrine-launcher-tile {
  padding: var(--citrine-space-18) var(--citrine-space-20);
}
.citrine-launcher-plain .citrine-launcher-icon {
  border-radius: var(--citrine-radius-14);
  background: var(--citrine-accent-wash);
  color: var(--citrine-accent);
}

@container citrine-launcher (max-width: 479px) {
  .citrine-launcher-plain.citrine-launcher-stages .citrine-launcher-tile {
    padding: var(--citrine-space-10);
    border-radius: var(--citrine-radius-12);
  }
  .citrine-launcher-plain.citrine-launcher-stages .citrine-launcher-figure {
    font-size: var(--citrine-font-size-22);
  }
  .citrine-launcher-plain.citrine-launcher-stages .citrine-launcher-label {
    font-size: var(--citrine-font-size-12);
  }
  .citrine-launcher-plain.citrine-launcher-doors .citrine-launcher-grid {
    gap: var(--citrine-space-10);
  }
  .citrine-launcher-plain.citrine-launcher-doors .citrine-launcher-tile {
    padding: var(--citrine-space-14) var(--citrine-space-16);
  }
  .citrine-launcher-plain .citrine-launcher-icon {
    border-radius: var(--citrine-radius-12);
  }
}

/* A tile that wears a hue. The tile's accent-drawn parts (icon square, hover and press fills, focus)
   already follow the hue: inside `.citrine-hued` the accent slots ARE the hue (HueScope.css). The
   figure is data-green by default, and a section's figure takes its section's deep instead — except
   on a money step, where the green is the meaning. */
.citrine-launcher-plain
  .citrine-launcher-tile.citrine-hued:not(.citrine-launcher-money):not(.citrine-launcher-quiet)
  .citrine-launcher-figure,
.citrine-launcher-plain
  .citrine-launcher-tile.citrine-hued:not(.citrine-launcher-money)
  .citrine-launcher-tag {
  color: var(--citrine-hue-deep);
}

/* ══ PAPER (opt-in) ═══════════════════════════════════════════════════════════════════════════════ */

.citrine-launcher-paper .citrine-launcher-tile {
  background: var(--citrine-accent-wash);
  border: 0;
  border-radius: var(--citrine-launcher-radius);
  /* The ink every word and figure inherits, and the tint the light shadow is mixed from. */
  color: var(--citrine-accent-deep);
  box-shadow: var(--citrine-launcher-shadow);
  transition:
    transform var(--citrine-launcher-duration-cell) var(--citrine-launcher-easing),
    box-shadow var(--citrine-launcher-duration-cell) var(--citrine-launcher-easing);
}
/* The spine. Not scoped to `.citrine-hued`, so the hue read carries the accent as its fallback. */
.citrine-launcher-paper .citrine-launcher-tile::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--citrine-launcher-spine-width);
  border-radius: var(--citrine-launcher-radius) 0 0 var(--citrine-launcher-radius);
  background: var(--citrine-hue, var(--citrine-accent));
  pointer-events: none;
}
.citrine-launcher-paper .citrine-launcher-tile:hover {
  transform: var(--citrine-launcher-lift-cell);
  box-shadow: var(--citrine-launcher-shadow-hover);
}
.citrine-launcher-paper .citrine-launcher-tag,
.citrine-launcher-paper .citrine-launcher-label,
.citrine-launcher-paper .citrine-launcher-sub,
.citrine-launcher-paper .citrine-launcher-figure {
  color: inherit;
}

/* A step where money moves stays on its section's paper — a run of stages reads as one colour — and
   keeps its green in the figure and the tag: the green is the meaning. `--citrine-data-2`, the
   palette's deeper data step, because the figure and the small tag sit on a hue's wash, not the
   surface the data green is tuned for. */
.citrine-launcher-paper .citrine-launcher-money .citrine-launcher-figure,
.citrine-launcher-paper .citrine-launcher-money .citrine-launcher-tag {
  color: var(--citrine-data-2);
}

/* Nothing here yet: a paler face and a thin ring, the SAME shadow, a lighter figure, no lift. */
.citrine-launcher-paper .citrine-launcher-quiet {
  background: color-mix(
    in srgb,
    var(--citrine-accent-wash) var(--citrine-launcher-quiet-mix),
    var(--citrine-surface)
  );
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-accent-line),
    var(--citrine-launcher-shadow);
}
/* An empty step where money moves keeps its meaning in the ring: the data line. */
.citrine-launcher-paper .citrine-launcher-money.citrine-launcher-quiet {
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-data-line),
    var(--citrine-launcher-shadow);
}
/* Under the pointer it stays put: no lift, and its ring and resting shadow, not the hover one. */
.citrine-launcher-paper .citrine-launcher-quiet:hover {
  transform: none;
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-accent-line),
    var(--citrine-launcher-shadow);
}
.citrine-launcher-paper .citrine-launcher-money.citrine-launcher-quiet:hover {
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-data-line),
    var(--citrine-launcher-shadow);
}
.citrine-launcher-paper .citrine-launcher-quiet .citrine-launcher-figure,
.citrine-launcher-paper .citrine-launcher-quiet .citrine-launcher-label {
  font-weight: var(--citrine-font-weight-400);
}

/* The leading edge makes room for the spine. */
.citrine-launcher-paper.citrine-launcher-stages .citrine-launcher-tile {
  padding: var(--citrine-space-11) var(--citrine-space-12) var(--citrine-space-12)
    calc(var(--citrine-space-11) + var(--citrine-launcher-spine-width));
}

/* Doors: the bigger lift. */
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-tile {
  padding: var(--citrine-space-18) var(--citrine-space-20) var(--citrine-space-18)
    calc(var(--citrine-space-20) + var(--citrine-launcher-spine-width));
  box-shadow: var(--citrine-launcher-shadow);
  transition:
    transform var(--citrine-launcher-duration) var(--citrine-launcher-easing),
    box-shadow var(--citrine-launcher-duration) var(--citrine-launcher-easing);
}
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-tile:hover {
  transform: var(--citrine-launcher-lift);
  box-shadow: var(--citrine-launcher-shadow-hover);
}
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-quiet {
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-accent-line),
    var(--citrine-launcher-shadow);
}
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-money.citrine-launcher-quiet {
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-data-line),
    var(--citrine-launcher-shadow);
}
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-quiet:hover {
  transform: none;
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-accent-line),
    var(--citrine-launcher-shadow);
}
.citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-money.citrine-launcher-quiet:hover {
  box-shadow:
    inset 0 0 0 var(--citrine-space-1) var(--citrine-data-line),
    var(--citrine-launcher-shadow);
}
/* The glyph's square sits on the surface: on the tile's own wash it would vanish. */
.citrine-launcher-paper .citrine-launcher-icon {
  border-radius: var(--citrine-launcher-radius);
  background: var(--citrine-surface);
  color: inherit;
}

@container citrine-launcher (max-width: 479px) {
  .citrine-launcher-paper.citrine-launcher-stages .citrine-launcher-figure {
    font-size: var(--citrine-font-size-20);
  }
  .citrine-launcher-paper.citrine-launcher-stages .citrine-launcher-label {
    font-size: var(--citrine-font-size-11p5);
  }
  .citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-grid {
    gap: var(--citrine-space-12);
  }
  .citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-tile {
    padding: var(--citrine-space-14) var(--citrine-space-16) var(--citrine-space-14)
      calc(var(--citrine-space-16) + var(--citrine-launcher-spine-width));
  }
}

/* ══ COMPACT LAYOUT (opt-in) ══════════════════════════════════════════════════════════════════════
   Layout only: each surface keeps its own colour, edge, depth and padding across the tile. Every size
   is a `--citrine-launcher-compact-*` token. */

/* The run heading: a quiet label in the body ink, not an accent headline. */
.citrine-launcher-compact {
  gap: var(--citrine-launcher-compact-title-gap);
}
.citrine-launcher-compact .citrine-launcher-title {
  font-weight: var(--citrine-launcher-compact-title-weight);
  line-height: var(--citrine-launcher-compact-text-leading);
  color: var(--citrine-ink);
}

/* Doors, at every width: the figure first and largest, then the label and its line. A glyph, if one
   is given, sits in the top corner rather than pushing the words aside. */
.citrine-launcher-compact.citrine-launcher-doors .citrine-launcher-tile {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--citrine-launcher-compact-door-gap);
  padding-block: var(--citrine-launcher-compact-door-pad-top) var(--citrine-launcher-compact-door-pad-bottom);
}
.citrine-launcher-compact.citrine-launcher-doors .citrine-launcher-figure {
  order: -1;
  font-size: var(--citrine-launcher-compact-door-figure-size);
  line-height: var(--citrine-launcher-compact-door-figure-leading);
}
.citrine-launcher-compact.citrine-launcher-doors .citrine-launcher-text {
  flex: none;
}
.citrine-launcher-compact.citrine-launcher-doors :is(.citrine-launcher-label, .citrine-launcher-sub) {
  line-height: var(--citrine-launcher-compact-text-leading);
}
.citrine-launcher-compact.citrine-launcher-doors .citrine-launcher-icon {
  position: absolute;
  inset-block-start: var(--citrine-space-14);
  inset-inline-end: var(--citrine-space-14);
}

/* Stage tiles, at every width: ONE height per width (owner ruling, 2026-09-24), the same in every run
   and every row, from `--citrine-launcher-compact-cell-height` / `-narrow`. Nothing is cut to get
   there. The tile is a small grid: the figure and a tag share the first line, so a tag costs no
   height, and the label takes the line under it, whole. The block is centred, with only
   `-cell-pad-block` of room required above and below, so a two-line label fits inside the height
   and a one-line tile keeps its air. The height is a minimum rather than a clamp: a label of three
   lines or more grows its tile instead of clipping, and `1fr` rows keep the rest of that run level
   with it. */
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-grid {
  grid-auto-rows: 1fr;
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-tile {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    'figure tag'
    'label label';
  align-content: center;
  column-gap: var(--citrine-launcher-compact-tag-gap);
  row-gap: var(--citrine-launcher-compact-cell-gap);
  aspect-ratio: auto;
  min-height: var(--citrine-launcher-compact-cell-height-narrow);
  padding-block: var(--citrine-launcher-compact-cell-pad-block);
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-text {
  display: contents;
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-figure {
  grid-area: figure;
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-tag {
  grid-area: tag;
  align-self: center;
  justify-self: start;
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-label {
  grid-area: label;
  line-height: var(--citrine-launcher-compact-cell-label-leading);
}
.citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-sub {
  grid-column: 1 / -1;
}
/* `labelFirst`: the stage's name over its figure. The label is first in the DOM already, so only the
   two grid rows swap; the figure keeps its tag beside it. */
.citrine-launcher-compact.citrine-launcher-stages.citrine-launcher-label-first .citrine-launcher-tile {
  grid-template-areas:
    'label label'
    'figure tag';
}
.citrine-launcher-compact.citrine-launcher-stages.citrine-launcher-label-first .citrine-launcher-label {
  font-weight: var(--citrine-launcher-compact-title-weight);
}

@container citrine-launcher (max-width: 479px) {
  .citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-figure {
    font-size: var(--citrine-launcher-compact-cell-figure-size-narrow);
  }
}

@container citrine-launcher (min-width: 480px) {
  .citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-grid {
    grid-template-columns: repeat(auto-fill, minmax(var(--citrine-launcher-compact-cell-min), 1fr));
    gap: var(--citrine-launcher-compact-gap);
  }
  .citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-tile {
    min-height: var(--citrine-launcher-compact-cell-height);
  }
  .citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-figure {
    font-size: var(--citrine-launcher-compact-cell-figure-size);
    line-height: var(--citrine-launcher-compact-cell-figure-leading);
  }
  .citrine-launcher-compact.citrine-launcher-stages .citrine-launcher-label {
    font-size: var(--citrine-launcher-compact-cell-label-size);
  }
}

@media (prefers-reduced-motion: reduce) {
  .citrine-launcher-plain .citrine-launcher-tile,
  .citrine-launcher-paper .citrine-launcher-tile,
  .citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-tile {
    transition: none;
  }
  .citrine-launcher-paper .citrine-launcher-tile:hover,
  .citrine-launcher-paper.citrine-launcher-doors .citrine-launcher-tile:hover {
    transform: none;
  }
}

/* ── Ledger ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Ledger. The bahi-khata folio.

   Ruled like an account book: a hairline under every line, a double rule before the figure column,
   figures right-aligned in tabular figures so a column reads down like a column of rupees. No card
   box and no shadow — a folio is lines ruled on the page. Lines are at least 48px tall, because the
   reader is often a partner with one thumb, outdoors, between site visits.

   The folio is ruled the same at every width by default. `narrow="sheet"` opts into a phone form: under
   480px of the folio's own width a `lines` folio becomes a padded SHEET of two-line entries and a
   `totals` row becomes a flat two-column grid — see the phone block at the end.

   Space BETWEEN folios is the layout's job (`Stack`, `Grid`), never the folio's: a sibling margin here
   stacked on top of every `Stack` gap and doubled the space between two folios. */

.citrine-ledger {
  --citrine-ledger-rule: var(--citrine-line-2, #cfd5e0);
  --citrine-ledger-band: var(--citrine-accent-wash, #e9eef7);
  --citrine-ledger-row: 48px;
  /* The opt-in phone form below is gated on the folio's OWN width, not the viewport's: a folio in a
     narrow desk column reads the same as one on a phone. */
  container: citrine-ledger / inline-size;
  margin: 0;
  min-width: 0;
}

/* The heading sits on the book's navy rule. `sub` rides above the rule with it, so the rule closes
   the heading block instead of cutting a heading off from its own explanation. */
.citrine-ledger-head {
  margin: 0 0 var(--citrine-sp-1, 4px);
  padding-bottom: var(--citrine-sp-2, 8px);
  border-bottom: 2px solid var(--citrine-accent);
}
.citrine-ledger-title {
  margin: 0;
  font-size: var(--citrine-font-size-16, 16px);
  font-weight: var(--citrine-font-weight-700, 700);
  line-height: var(--citrine-line-height-1p25, 1.25);
  color: var(--citrine-accent);
  text-wrap: balance;
}
.citrine-ledger-sub {
  margin: var(--citrine-sp-1, 4px) 0 0;
  max-width: 72ch;
  font-size: var(--citrine-font-size-13p5, 13.5px);
  line-height: 1.4;
  color: var(--citrine-ink-3, #6b7280);
}

/* A folio a level down (`as="h3"` / `"h4"`) is a sub-account of the one above it: a lighter rule and a
   smaller heading, so the two never read as equals. */
.citrine-ledger-h3 .citrine-ledger-head,
.citrine-ledger-h4 .citrine-ledger-head {
  border-bottom-width: 1px;
}
.citrine-ledger-h3 .citrine-ledger-title,
.citrine-ledger-h4 .citrine-ledger-title {
  font-size: var(--citrine-font-size-14, 14px);
}

/* What an empty folio says — loading, failed, or nothing yet — as one ruled line of prose in pencil,
   under its own heading. */
.citrine-ledger-empty {
  padding: var(--citrine-sp-3, 12px) 0;
  border-bottom: 1px solid var(--citrine-ledger-rule);
  font-size: var(--citrine-font-size-14, 14px);
  line-height: 1.5;
  color: var(--citrine-ink-3, #6b7280);
}

/* The sheet the lines are ruled on. Named apart from the `citrine-ledger-lines` VARIANT class on the
   root: while the two shared a name, the root <section> itself became this column grid and a folio's
   heading sat in a column beside its own lines on any screen wide enough for two. */
.citrine-ledger-sheet {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--citrine-ledger-min, 260px), 100%), 1fr));
  column-gap: var(--citrine-sp-6, 24px);
}

.citrine-ledger-line {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: stretch;
  min-height: var(--citrine-ledger-row);
  border-bottom: 1px solid var(--citrine-ledger-rule);
  transition: background-color 140ms cubic-bezier(0.16, 1, 0.3, 1);
}

.citrine-ledger-label {
  margin: 0;
  display: flex;
  align-items: center;
  min-width: 0;
  padding: var(--citrine-sp-2, 8px) var(--citrine-sp-3, 12px) var(--citrine-sp-2, 8px) 0;
  font-size: var(--citrine-font-size-15, 15px);
  line-height: var(--citrine-line-height-1p25, 1.25);
  color: var(--citrine-ink, #1f2937);
}

/* The double rule before the figure column — the one mark every account book shares. The figure is
   written in the data green: a settled entry, where navy is the ink of the book itself. */
/* THE FIGURE COLUMN. Two things here were wrong and both were found by measuring a shipped screen
   rather than by reading this file.

   1. NO RIGHT PADDING. The digits ran flush to the sheet's edge — measured on a consumer's list at
      390px: the figure ended at x=372 against a border at x=373, while the label had 16px of room on
      the left. A ledger with a margin on one side only reads as clipped.

   2. THE RULE MOVED DOWN THE PAGE. The column was `auto`, so every line sized its own figure
      independently and the double rule sat at x=283 on some rows and x=273 on others — a 10px
      wobble, one character's worth, between a 10-digit figure and a 9-digit one. A ruled account
      book whose rule wanders is the one thing this part is for.

   THE PADDING IS FIXED HERE. THE WOBBLE IS ONLY PARTLY FIXED, and saying so is the point.
   `min-width` in `ch` with `tabular-nums` gives the column a FLOOR — below it every line agrees, so a
   sheet of short figures is now ruled straight. Above it nothing changes: each line is still its own
   grid, so a 10-digit figure and a 9-digit one on the same sheet still size their own columns and the
   rule still moves between them. Re-measured on a consuming app after this change: 98–108px, the
   same ~10px spread as before, because these figures are longer than the floor.

   Aligning every line regardless needs the lines to SHARE a grid — `display: contents` on the line
   (which would drop its rule and its positioned link overlay) or `subgrid` (which the folio sheet's
   own `auto-fill` columns are in the way of). Both are a real change to this component's structure
   rather than a value, so neither is done here. The floor is kept because it is a genuine improvement
   for the common case and costs nothing in the uncommon one.

   With `narrow="sheet"` the question goes away on a phone: the phone block at the end of this file
   draws no figure column there (the figure sits at the end of the label's line), so there is no rule
   to wander. The default keeps the column, floor and all, at every width. */
.citrine-ledger-value {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-width: var(--citrine-ledger-figure, 10ch);
  padding: 0 var(--citrine-sp-2, 8px) 0 var(--citrine-sp-3, 12px);
  border-left: 3px double var(--citrine-ledger-rule);
  font-size: var(--citrine-font-size-17, 17px);
  font-weight: var(--citrine-font-weight-700, 700);
  font-variant-numeric: tabular-nums;
  color: var(--citrine-data);
  animation: citrine-ledger-settle 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* A counted zero, or a figure at rest: pencil grey, so the few live figures carry the page. */
.citrine-ledger-muted .citrine-ledger-value {
  font-weight: var(--citrine-font-weight-500, 500);
  color: var(--citrine-ink-3, #6b7280);
}

/* A line of detail under the label. Every cell is placed explicitly: the figure spans both rows so it
   stays on the line it counts, and an auto-placed figure would take the first free column instead. */
.citrine-ledger-foot {
  margin: 0;
  font-size: var(--citrine-font-size-13, 13px);
  line-height: 1.35;
  color: var(--citrine-ink-3, #6b7280);
}
.citrine-ledger-lines .citrine-ledger-has-foot .citrine-ledger-label {
  grid-column: 1;
  grid-row: 1;
  align-items: flex-end;
  padding-bottom: 0;
}
.citrine-ledger-lines .citrine-ledger-has-foot .citrine-ledger-value {
  grid-column: 2;
  grid-row: 1 / span 2;
}
.citrine-ledger-lines .citrine-ledger-foot {
  grid-column: 1;
  grid-row: 2;
  padding: 2px var(--citrine-sp-3, 12px) var(--citrine-sp-2, 8px) 0;
}

/* A linked line is ONE tap target, the size of the line. The label reads in the book's ink so the
   line announces itself as a way in without a single glyph. */
.citrine-ledger-linked .citrine-ledger-label {
  color: var(--citrine-accent);
  font-weight: var(--citrine-font-weight-600, 600);
}
.citrine-ledger-link {
  color: inherit;
  text-decoration: none;
}
.citrine-ledger-link::after {
  content: '';
  position: absolute;
  inset: 0;
}
.citrine-ledger-link:focus-visible {
  outline: none;
}

/* The signature: under the thumb or the pointer, the line lifts into a ruled band. */
.citrine-ledger-linked:hover,
.citrine-ledger-linked:focus-within {
  background: var(--citrine-ledger-band);
}
.citrine-ledger-linked:focus-within {
  box-shadow: inset 0 0 0 2px var(--citrine-accent);
}
.citrine-ledger-linked:active {
  background: var(--citrine-accent-line, #c4d2e9);
}

/* Figures settle into their column once, from an already-visible start. */
@keyframes citrine-ledger-settle {
  from {
    opacity: 0.55;
    transform: translateY(3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .citrine-ledger-value {
    animation: none;
  }
  .citrine-ledger-line {
    transition: none;
  }
}

/* ── variant="totals": the head of the book ────────────────────────────────────────────────────
   One row of large figures, closed by a double rule the way a ledger closes a total. Phone first:
   short totals stay in ONE ruled row down to a 390px phone — the head of a book is read across, not
   down — and a desk only widens the cells. Each total carries a left and a top hairline; the sheet
   clips whichever of those sits on its own edge, so only the rules BETWEEN totals show.

   A WRAPPING FLEX ROW, NOT A GRID, because a total is read as one number and must never be cut. A
   grid's `auto-fit` columns take a fixed minimum (they cannot size to their content), so on a 390px
   phone four nine-digit figures were laid out three across at 119px a cell against 161px of figure:
   each ran into the next total and the sheet's edge clipped the last. Here every cell starts from
   the same 96px basis and grows by an equal share, so cells are equal when they fit, exactly as the
   grid drew them; but no cell narrows below its own figure (`min-content`), so a row that cannot
   hold them all whole puts fewer on a line instead. The one visible difference from the grid: a
   total that wraps onto a line of its own is as wide as that line, not one column. */
.citrine-ledger-totals .citrine-ledger-sheet {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  overflow: hidden;
  border-top: 1px solid var(--citrine-ledger-rule);
  border-bottom: 3px double var(--citrine-accent);
}
.citrine-ledger-totals .citrine-ledger-line {
  display: flex;
  flex: 1 1 min(96px, 100%);
  flex-direction: column;
  align-items: flex-start;
  gap: var(--citrine-sp-1, 4px);
  min-width: min-content;
  min-height: 0;
  margin: -1px 0 0 -1px;
  padding: var(--citrine-sp-3, 12px) var(--citrine-sp-3, 12px) var(--citrine-sp-4, 16px)
    var(--citrine-sp-4, 16px);
  border-bottom: 0;
  border-left: 1px solid var(--citrine-ledger-rule);
  border-top: 1px solid var(--citrine-ledger-rule);
}
.citrine-ledger-totals .citrine-ledger-label,
.citrine-ledger-totals .citrine-ledger-linked .citrine-ledger-label {
  order: 1;
  gap: var(--citrine-sp-2, 8px);
  flex-wrap: wrap;
  padding: 0;
  font-size: var(--citrine-font-size-13p5, 13.5px);
  font-weight: var(--citrine-font-weight-600, 600);
  color: var(--citrine-ink-3, #6b7280);
}
.citrine-ledger-totals .citrine-ledger-value {
  order: 2;
  justify-content: flex-start;
  padding: 0;
  border-left: 0;
  font-size: clamp(var(--citrine-font-size-24, 24px), 4vw + 8px, var(--citrine-font-size-32, 32px));
  font-weight: var(--citrine-font-weight-750, 750);
  line-height: var(--citrine-line-height-1p15, 1.15);
}
.citrine-ledger-totals .citrine-ledger-foot {
  order: 3;
}

/* A warn `Chip` beside a total's label wraps inside a narrow phone cell instead of being clipped by
   the sheet's edge. */
.citrine-ledger-extra {
  display: inline-flex;
  min-width: 0;
  max-width: 100%;
}
.citrine-ledger-extra > * {
  max-width: 100%;
  white-space: normal;
}

/* ── PHONE (opt-in, `narrow="sheet"`): the folio at 479px and under ─────────────────────────────────
   Only under `.citrine-ledger-narrow-sheet`; the default folio keeps its ruled lines and its ruled
   totals row at every width. Why a host might want it, measured at 390px on a consumer's list: at
   phone width the figure column behind its double rule stops reading as a column. Each line sized its
   own figure, so the rule sat at a different x on every line (263 / 277 / 273px), and the label had no
   room on its left — the text ran flush to the edge.

   `lines`: ONE padded sheet (hairline edge, small radius), each entry TWO ruled lines inside it —
   line 1 the label on the left and the figure on the right, line 2 the foot across the whole width.
   No figure column and so no rule to wander. The foot lays its children out along the line (chip
   left, detail between, a trailing figure right), wrapping when they do not fit.

   `totals`: a flat grid of cells at least 150px wide — two across on a phone, so four totals are a
   clean 2×2 and a fifth drops below — with no rule around any cell and none above or below the row.
   Desk keeps the ruled head row. */
@container citrine-ledger (max-width: 479px) {
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-sheet {
    display: block;
    overflow: hidden;
    background: var(--citrine-ledger-sheet-bg);
    border: 1px solid var(--citrine-ledger-sheet-edge);
    border-radius: var(--citrine-ledger-sheet-radius);
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-line {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto;
    row-gap: var(--citrine-ledger-entry-gap);
    column-gap: var(--citrine-sp-3, 12px);
    min-height: 0;
    padding: var(--citrine-ledger-entry-pad-top) var(--citrine-ledger-entry-pad-x)
      var(--citrine-ledger-entry-pad-bottom);
    border-bottom: 1px solid var(--citrine-ledger-sheet-edge);
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-line:last-child {
    border-bottom: 0;
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-label,
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-has-foot .citrine-ledger-label {
    grid-column: 1;
    grid-row: 1;
    align-items: center;
    min-height: var(--citrine-ledger-entry-reach);
    padding: 0;
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-value,
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-has-foot .citrine-ledger-value {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    padding: 0;
    border-left: 0;
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-lines .citrine-ledger-foot {
    grid-column: 1 / -1;
    grid-row: 2;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--citrine-ledger-entry-foot-gap-y) var(--citrine-ledger-entry-foot-gap-x);
    padding: 0;
    font-size: var(--citrine-ledger-entry-foot-size);
  }

  .citrine-ledger-narrow-sheet.citrine-ledger-totals .citrine-ledger-sheet {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--citrine-ledger-totals-cell-min), 100%), 1fr));
    gap: var(--citrine-ledger-totals-gap-y) var(--citrine-ledger-totals-gap-x);
    overflow: visible;
    border-top: 0;
    border-bottom: 0;
  }
  .citrine-ledger-narrow-sheet.citrine-ledger-totals .citrine-ledger-line {
    min-width: 0;
    margin: 0;
    padding: 0;
    border-left: 0;
    border-top: 0;
  }
}

/* ── LifecycleSteps ────────────────────────────────────────────────────────────────────── */
/* UI kit — LifecycleSteps: a record's stages as one track of dots on a line, the names under them.
 *
 * THE SAME TRACK AS THE STEPSHELL SPINE. Every size and every colour here is a `--citrine-stepshell-*`
 * token the spine reads too (dot, ring, halo, link, gaps; done green and the resting grey), so the
 * two journeys cannot drift apart — change a token and both move. See `LifecycleSteps.tsx` for the
 * four states and why this is no longer antd `Steps`.
 *
 * THE CURRENT STAGE WEARS THE PAGE ACCENT, read HERE on the component: inside a `HueScope` (an app
 * shell's section) the accent is that section's colour — its deep, 4.5:1 on the card in both themes —
 * and a root-level token would have resolved to the app accent before any scope was entered.
 */
.citrine-lifecycle {
  --citrine-lifecycle-d: var(--citrine-stepshell-dot);
  --citrine-lifecycle-now: var(--citrine-accent);
  --citrine-lifecycle-now-halo: var(--citrine-accent-wash);
  display: flex;
  align-items: flex-start;
  margin: 0;
  /* Room above for the current ring's halo, which is drawn outside its box. */
  padding: var(--citrine-stepshell-halo) 0 0;
  list-style: none;
}

/* Equal seats, so half a seat is exactly the gap to the neighbour — the line between two dots runs
 * from one's edge to the other's, clear of both. */
.citrine-lifecycle-step {
  position: relative;
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

/* Between two STEPS only — a `window` track's edge items are list items too, and are not steps. */
.citrine-lifecycle-step + .citrine-lifecycle-step::before {
  content: '';
  position: absolute;
  top: calc(var(--citrine-lifecycle-d) / 2 - var(--citrine-stepshell-link) / 2);
  left: calc(-50% + var(--citrine-lifecycle-d) / 2 + var(--citrine-stepshell-link-gap));
  right: calc(50% + var(--citrine-lifecycle-d) / 2 + var(--citrine-stepshell-link-gap));
  height: var(--citrine-stepshell-link);
  border-radius: var(--citrine-stepshell-link);
  background: var(--citrine-stepshell-rest);
}

/* Green only between two finished stages: the line into a complete dot. Into the current ring, and
 * past it, the line stays grey. The line into a stopped stage is red — and the one out of it grey,
 * because nothing after a stop was reached. */
.citrine-lifecycle-step + .citrine-lifecycle-step-is-complete::before {
  background: var(--citrine-stepshell-done);
}

.citrine-lifecycle-step + .citrine-lifecycle-step-is-stopped::before {
  background: var(--citrine-bad);
}

/* ── the dot ────────────────────────────────────────────────────────────────────────────────── */
.citrine-lifecycle-step-marker {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--citrine-lifecycle-d);
  height: var(--citrine-lifecycle-d);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-surface);
  /* Upcoming: a hollow grey ring. */
  box-shadow: inset 0 0 0 var(--citrine-stepshell-ring) var(--citrine-stepshell-rest);
}

/* Complete: a plain solid dot. */
.citrine-lifecycle-step-is-complete .citrine-lifecycle-step-marker {
  background: var(--citrine-stepshell-done);
  box-shadow: none;
}

/* Current: bigger, a ring in the accent with a soft wash of it around. The negative margin keeps its
 * layout box the size of the others, so the line stays straight; the halo is a shadow, outside the
 * box, so it moves nothing either. */
.citrine-lifecycle-step-is-current .citrine-lifecycle-step-marker {
  width: calc(var(--citrine-lifecycle-d) + var(--citrine-stepshell-dot-grow));
  height: calc(var(--citrine-lifecycle-d) + var(--citrine-stepshell-dot-grow));
  margin: calc(var(--citrine-stepshell-dot-grow) / -2);
  box-shadow:
    inset 0 0 0 var(--citrine-stepshell-ring-now) var(--citrine-lifecycle-now),
    0 0 0 var(--citrine-stepshell-halo) var(--citrine-lifecycle-now-halo);
}

/* Stopped: a solid red dot with a cross in the card's own ground. */
.citrine-lifecycle-step-is-stopped .citrine-lifecycle-step-marker {
  background: var(--citrine-bad);
  box-shadow: none;
  color: var(--citrine-surface);
  font-size: calc(var(--citrine-lifecycle-d) * 0.55);
}

/* ── the name ───────────────────────────────────────────────────────────────────────────────── */
/* Under its dot, centred. A name WRAPS BETWEEN WORDS, never inside one, and clamps at two lines; a
 * single word wider than its seat is shortened with an ellipsis. The full name stays in the DOM and in
 * its `title`, so the accessible name is whole. */
.citrine-lifecycle-step-label {
  display: -webkit-box;
  max-width: 100%;
  margin-top: var(--citrine-stepshell-name-gap);
  padding-inline: var(--citrine-stepshell-name-inset);
  overflow: hidden;
  font-size: var(--citrine-font-size-13);
  line-height: 1.3;
  text-align: center;
  text-overflow: ellipsis;
  word-break: normal;
  overflow-wrap: normal;
  color: var(--citrine-ink-3);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.citrine-lifecycle-step-is-complete .citrine-lifecycle-step-label {
  color: var(--citrine-ink-2);
}

.citrine-lifecycle-step-is-current .citrine-lifecycle-step-label {
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-lifecycle-now);
}

.citrine-lifecycle-step-is-stopped .citrine-lifecycle-step-label {
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-ink);
}

/* ── variant="trail": the same track, compact, for a narrow card ─────────────────────────────── */
/* Smaller dots and names, one line each; a name too long for its seat ends in an ellipsis. */
.citrine-lifecycle-compact {
  --citrine-lifecycle-d: var(--citrine-stepshell-dot-narrow);
}

.citrine-lifecycle-compact .citrine-lifecycle-step-label {
  display: block;
  font-size: var(--citrine-stepshell-name-size);
  white-space: nowrap;
}

/* ── variant="window": a few stages of a long journey, numbered, names whole ─────────────────── */
/* Each step draws its own two half-lines — into its dot (::before) and out of it (::after) — so a seat
 * can hold its dot at the centre OR against the card's edge and the line still meets it.
 *
 * EQUAL GAPS. A middle stage holds a whole seat, its dot at the centre. The item at each END — a
 * faded edge stage, or the end stage itself when there is no edge — holds HALF a seat with its dot
 * on the outer side. So every dot is exactly one seat from the next, wherever the window sits. */
.citrine-lifecycle-window {
  --citrine-lifecycle-d: var(--citrine-space-24);
  --citrine-lifecycle-edge-w: calc(var(--citrine-space-20) * 2);
  --citrine-lifecycle-line-gap: calc(var(--citrine-lifecycle-d) / 2 + var(--citrine-stepshell-link-gap));
  --citrine-lifecycle-end-gap: calc(
    var(--citrine-stepshell-halo) + var(--citrine-lifecycle-d) + var(--citrine-stepshell-link-gap)
  );
  /* An end dot's centre sits this far in from the edge; its half seat starts this wide so the gap
   * from it to the next dot still comes out one whole seat. */
  --citrine-lifecycle-end-inset: calc(var(--citrine-stepshell-halo) + var(--citrine-lifecycle-d) / 2);
  position: relative;
}

.citrine-lifecycle-window .citrine-lifecycle-step::before,
.citrine-lifecycle-window .citrine-lifecycle-step::after,
.citrine-lifecycle-edge::before,
.citrine-lifecycle-edge::after {
  content: '';
  position: absolute;
  top: calc(var(--citrine-lifecycle-d) / 2 - var(--citrine-stepshell-link) / 2);
  height: var(--citrine-stepshell-link);
  border-radius: var(--citrine-stepshell-link);
  background: var(--citrine-stepshell-rest);
}

.citrine-lifecycle-window .citrine-lifecycle-step::before,
.citrine-lifecycle-edge::before {
  left: 0;
  right: calc(50% + var(--citrine-lifecycle-line-gap));
}

.citrine-lifecycle-window .citrine-lifecycle-step::after,
.citrine-lifecycle-edge::after {
  left: calc(50% + var(--citrine-lifecycle-line-gap));
  right: 0;
}

/* Into a done stage: green. Out of a done stage into another done one: green. Into a stop: red. */
.citrine-lifecycle-window .citrine-lifecycle-step-is-complete::before,
.citrine-lifecycle-edge.citrine-lifecycle-step-is-complete::before,
.citrine-lifecycle-window
  .citrine-lifecycle-step-is-complete:has(+ .citrine-lifecycle-step-is-complete)::after,
.citrine-lifecycle-edge-before.citrine-lifecycle-edge-line-done::after {
  background: var(--citrine-stepshell-done);
}

.citrine-lifecycle-window .citrine-lifecycle-step-is-stopped::before,
.citrine-lifecycle-window .citrine-lifecycle-step:has(+ .citrine-lifecycle-step-is-stopped)::after {
  background: var(--citrine-bad);
}

/* Nothing before the first stage shown: it sits against the start edge in half a seat, its name under it. */
.citrine-lifecycle-window:not(.citrine-lifecycle-has-before) .citrine-lifecycle-step-first {
  flex: 0.5 1 var(--citrine-lifecycle-end-inset);
  align-items: flex-start;
  padding-left: var(--citrine-stepshell-halo);
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-before) .citrine-lifecycle-step-first::before {
  display: none;
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-before) .citrine-lifecycle-step-first::after {
  left: var(--citrine-lifecycle-end-gap);
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-before)
  .citrine-lifecycle-step-first
  .citrine-lifecycle-step-label {
  padding-left: 0;
  text-align: left;
}

/* Nothing after the last stage shown: it sits against the end edge in half a seat. */
.citrine-lifecycle-window:not(.citrine-lifecycle-has-after) .citrine-lifecycle-step-last {
  flex: 0.5 1 var(--citrine-lifecycle-end-inset);
  align-items: flex-end;
  padding-right: var(--citrine-stepshell-halo);
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-after) .citrine-lifecycle-step-last::after {
  display: none;
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-after) .citrine-lifecycle-step-last::before {
  right: var(--citrine-lifecycle-end-gap);
}

.citrine-lifecycle-window:not(.citrine-lifecycle-has-after)
  .citrine-lifecycle-step-last
  .citrine-lifecycle-step-label {
  padding-right: 0;
  text-align: right;
}

/* The number in the dot: the ring's ink when upcoming, the card's ground on a done dot, the accent
 * and heavier on the current ring. */
.citrine-lifecycle-window .citrine-lifecycle-step-marker {
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-600);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--citrine-ink-3);
}

.citrine-lifecycle-window .citrine-lifecycle-step-is-complete .citrine-lifecycle-step-marker {
  color: var(--citrine-surface);
}

.citrine-lifecycle-window .citrine-lifecycle-step-is-current .citrine-lifecycle-step-marker {
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-700);
  color: var(--citrine-lifecycle-now);
}

.citrine-lifecycle-window .citrine-lifecycle-step-label {
  font-size: var(--citrine-stepshell-name-size);
}

/* The current name is the one the eye should land on: bolder and a step larger than the rest. */
.citrine-lifecycle-window .citrine-lifecycle-step-is-current .citrine-lifecycle-step-label {
  font-size: var(--citrine-font-size-13);
  font-weight: var(--citrine-font-weight-700);
}

/* ── the faded edges ── */
/* Half a seat each, the dot on the outer side (see EQUAL GAPS above); only the line toward the track. */
.citrine-lifecycle-edge {
  position: relative;
  display: flex;
  flex: 0.5 1 var(--citrine-lifecycle-end-inset);
  min-width: 0;
}

.citrine-lifecycle-edge-before {
  justify-content: flex-start;
  padding-left: var(--citrine-stepshell-halo);
}

.citrine-lifecycle-edge-after {
  justify-content: flex-end;
  padding-right: var(--citrine-stepshell-halo);
}

.citrine-lifecycle-edge-before::before,
.citrine-lifecycle-edge-after::after {
  display: none;
}

.citrine-lifecycle-edge-before::after {
  left: var(--citrine-lifecycle-end-gap);
}

.citrine-lifecycle-edge-after::before {
  right: var(--citrine-lifecycle-end-gap);
}

.citrine-lifecycle-edge-button {
  display: flex;
  width: 100%;
  min-height: var(--citrine-lifecycle-edge-w);
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--citrine-r-sm);
  background: none;
  font: inherit;
  cursor: pointer;
}

.citrine-lifecycle-edge-after .citrine-lifecycle-edge-button {
  justify-content: flex-end;
}

span.citrine-lifecycle-edge-button {
  cursor: default;
}

.citrine-lifecycle-edge-button:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-2);
}

/* The card's own ground, laid lightly over the edge stage and thinning toward the track: the stage and
 * its number stay readable but plainly dimmer than the stages in the window. */
.citrine-lifecycle-edge-veil {
  position: absolute;
  inset: calc(var(--citrine-stepshell-halo) * -1) 0 0;
  z-index: 1;
  pointer-events: none;
}

.citrine-lifecycle-edge-before .citrine-lifecycle-edge-veil {
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--citrine-surface) 55%, transparent) 0%,
    color-mix(in srgb, var(--citrine-surface) 30%, transparent) 60%,
    transparent 100%
  );
}

.citrine-lifecycle-edge-after .citrine-lifecycle-edge-veil {
  background: linear-gradient(
    to left,
    color-mix(in srgb, var(--citrine-surface) 55%, transparent) 0%,
    color-mix(in srgb, var(--citrine-surface) 30%, transparent) 60%,
    transparent 100%
  );
}

/* ── List ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — List. Strips the browser default marker and indent from a real <ul>/<ol> while the
   element stays a real list.

   Note (2026-09-10): the paragraph this replaces cited `.citrine-note-content ul` in Note.css as a
   more specific selector that let a List rendered inside a Note keep Note's own list spacing. That
   selector is gone — Group B wave 4 rebuilt `Note` onto antd's `Alert`, whose `description` slot
   renders `children` straight into antd's own markup with no Citrine-owned class around it, and
   `Note.css` is down to one rule (`.citrine-note + .citrine-note` spacing) that says nothing about
   lists. There is no longer a competing selector to lose to: a `List` rendered inside a `Note` gets
   this rule's reset the same as anywhere else. */
.citrine-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Space between siblings, set by the `gap` prop as a custom property. A margin rather than a flex
   or grid gap on purpose: the rule above already removes the browser marker, which alone drops list
   semantics in Safari/VoiceOver, and changing the display type as well would put a second layer
   between this element and the "list, N items" announcement a real <ul> exists to produce. With no
   `gap` passed the variable is unset and the declaration resolves to nothing, so every list that
   predates this rule is untouched. */
.citrine-list > li + li {
  margin-block-start: var(--citrine-list-gap);
}

/* `bullets` — the browser's own marker back, with room for it. Opt-in; the rule above stays the
   default. Restoring the marker also restores the list semantics Safari drops with `list-style:
   none`. */
.citrine-list.citrine-list-bullets {
  list-style: disc;
  padding-inline-start: var(--citrine-sp-5);
}

ol.citrine-list.citrine-list-bullets {
  list-style: decimal;
}

/* `divided` — a ruled feed: air above and below each row, a hairline between rows. The gap rule
   above still applies on top when `gap` is passed; a ruled list normally passes none. */
.citrine-list.citrine-list-divided > li {
  padding-block: var(--citrine-list-divided-pad);
}
.citrine-list.citrine-list-divided > li + li {
  border-block-start: 1px solid var(--citrine-list-divided-line);
}

/* `ListItem tone` — the row on a status wash, its content inset so words keep off the edge. In a
   ruled list the wash stands in for the hairlines on either side of it. */
.citrine-list-item-toned {
  padding-inline: var(--citrine-list-item-tone-pad);
  border-radius: var(--citrine-list-item-tone-radius);
}
.citrine-list-item-bad {
  background: var(--citrine-list-item-bad-ground);
}
.citrine-list-item-warn {
  background: var(--citrine-list-item-warn-ground);
}
.citrine-list.citrine-list-divided > li.citrine-list-item-toned,
.citrine-list.citrine-list-divided > li.citrine-list-item-toned + li {
  border-block-start-color: transparent;
}

/* ── MapPin ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — MapPin styles.
 *
 * The controls are OURS, drawn over Google's canvas, because Google's own chrome belongs to Google's
 * design system and reads as a foreign object in a Citrine screen — different radii, different type,
 * different grey, and a drop shadow from another decade.
 *
 * There is still only ONE control for each thing: Google's `mapTypeControl` and `zoomControl` are off,
 * and these buttons drive the map API directly. Two controls setting the same value is the bug this
 * avoids, not the pattern it follows.
 *
 * ⚠ EVERY COLOUR IS A TOKEN. The map underneath can be a pale street plan or a dark satellite
 *   photograph, and the controls have to stay legible over both — so they sit on a real surface
 *   rather than tinting themselves against whatever is behind them.
 */

.citrine-map-pin {
  position: relative;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--citrine-line);
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-surface-2);
}

.citrine-map-pin-canvas {
  width: 100%;
  height: 100%;
}

/* ── Full screen ───────────────────────────────────────────────────────────────────────────── */
/* The map takes the whole viewport for one decision — placing a pin on an exact doorway — and gives
 * it straight back. Not a modal: there is nothing to confirm or cancel, and no second task behind a
 * scrim. The form underneath keeps its scroll position and everything typed into it.
 *
 * ⚠ `100dvh`, not `100vh`. On a phone `vh` is the viewport with the browser's address bar HIDDEN, so
 *   the bottom of a `100vh` element sits under the bar until the user scrolls — and here that is
 *   exactly where the zoom and close buttons are. `dvh` follows the bar as it comes and goes. */
.citrine-map-pin-is-expanded {
  position: fixed;
  inset: 0;
  z-index: 1000;
  height: 100dvh;
  border: 0;
  border-radius: 0;
}

/* Google paints its own focus ring inside the canvas; ours would double it. */
.citrine-map-pin-canvas:focus-visible {
  outline: none;
}

/* ── The floating control shell, shared by both groups ─────────────────────────────────────── */
.citrine-map-pin-control {
  position: absolute;
  z-index: 1;
  display: flex;
  overflow: hidden;
  border: 1px solid var(--citrine-line);
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-surface);
  /* Offset and blur, so the control sits ABOVE the map rather than glowing on it. */
  box-shadow:
    0 1px 2px rgb(0 0 0 / 12%),
    0 4px 12px rgb(0 0 0 / 10%);
  /* The one authored moment: the controls arrive after the map has drawn, so they read as part of
     the map rather than as furniture waiting for it. */
  animation: citrine-map-pin-arrive 220ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes citrine-map-pin-arrive {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .citrine-map-pin-control {
    animation: none;
  }
}

/* ── The view switch ───────────────────────────────────────────────────────────────────────── */
.citrine-map-pin-views {
  top: var(--citrine-space-8);
  left: var(--citrine-space-8);
}

.citrine-map-pin-view {
  min-height: 34px;
  padding: 0 var(--citrine-space-12);
  border: 0;
  background: transparent;
  color: var(--citrine-ink-3);
  font: inherit;
  font-size: var(--citrine-font-size-13);
  font-weight: var(--citrine-font-weight-500);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color 120ms ease-out,
    color 120ms ease-out;
}

.citrine-map-pin-view + .citrine-map-pin-view {
  border-left: 1px solid var(--citrine-line);
}

.citrine-map-pin-view:hover {
  background: var(--citrine-surface-2);
  color: var(--citrine-ink);
}

/* The selected view. Weight and ink carry it, with the accent as a quiet wash rather than a slab —
   this is a state, not a call to action, and the map behind it is the thing to look at. */
.citrine-map-pin-view[aria-pressed='true'] {
  background: color-mix(in srgb, var(--citrine-accent) 10%, var(--citrine-surface));
  color: var(--citrine-accent);
  font-weight: var(--citrine-font-weight-600);
}

/* ── Expand ────────────────────────────────────────────────────────────────────────────────── */
/* Top-right: the view switch owns the top-left and zoom owns the bottom-right, so the three groups
 * never collide, including at 390px where the switch is at its widest. */
.citrine-map-pin-expand {
  top: var(--citrine-space-8);
  right: var(--citrine-space-8);
}

/* ── Zoom ──────────────────────────────────────────────────────────────────────────────────── */
.citrine-map-pin-zoom {
  right: var(--citrine-space-8);
  bottom: var(--citrine-space-20);
  flex-direction: column;
}

.citrine-map-pin-zoom-button {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--citrine-ink-2);
  cursor: pointer;
  transition:
    background-color 120ms ease-out,
    color 120ms ease-out;
}

.citrine-map-pin-zoom-button + .citrine-map-pin-zoom-button {
  border-top: 1px solid var(--citrine-line);
}

.citrine-map-pin-zoom-button:hover {
  background: var(--citrine-surface-2);
  color: var(--citrine-ink);
}

.citrine-map-pin-zoom-button:active {
  background: var(--citrine-surface-3);
}

.citrine-map-pin-zoom-button svg {
  display: block;
}

/* Full screen on a phone puts the controls where the hardware is. Inside a card the browser's own
 * chrome keeps them clear; edge to edge, the notch sits over the top row and the home indicator under
 * the bottom one. `env()` resolves to 0 everywhere that has neither, so this costs nothing on a
 * desktop. */
.citrine-map-pin-is-expanded .citrine-map-pin-views,
.citrine-map-pin-is-expanded .citrine-map-pin-expand {
  top: calc(var(--citrine-space-8) + env(safe-area-inset-top, 0px));
}

.citrine-map-pin-is-expanded .citrine-map-pin-views {
  left: calc(var(--citrine-space-8) + env(safe-area-inset-left, 0px));
}

.citrine-map-pin-is-expanded .citrine-map-pin-expand,
.citrine-map-pin-is-expanded .citrine-map-pin-zoom {
  right: calc(var(--citrine-space-8) + env(safe-area-inset-right, 0px));
}

.citrine-map-pin-is-expanded .citrine-map-pin-zoom {
  bottom: calc(var(--citrine-space-20) + env(safe-area-inset-bottom, 0px));
}

/* ── Search ────────────────────────────────────────────────────────────────────────────────── */
/* A centered search surface keeps the map visible on a desk instead of turning the controls into a
 * full-width banner. The view switch and expand action share its lower edge; the map stays the focus. */
/* citrine-ok: MapPin search layout refresh approved by the user in chat on 2026-10-14. */
.citrine-map-pin-search {
  top: var(--citrine-space-8);
  left: 50%;
  right: auto;
  width: calc(100% - var(--citrine-space-8) * 2);
  max-width: 70ch;
  z-index: 2;
  flex-direction: column;
  transform: translateX(-50%);
  animation-name: citrine-map-pin-search-arrive;
}

@keyframes citrine-map-pin-search-arrive {
  from {
    opacity: 0;
    transform: translate(-50%, 4px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

.citrine-map-pin-has-search .citrine-map-pin-views,
.citrine-map-pin-has-search .citrine-map-pin-expand {
  top: calc(var(--citrine-space-8) * 2 + var(--citrine-target-min));
  min-height: var(--citrine-target-min);
}

.citrine-map-pin-has-search .citrine-map-pin-views {
  left: max(var(--citrine-space-8), calc(50% - 35ch));
}

.citrine-map-pin-has-search .citrine-map-pin-expand {
  right: max(var(--citrine-space-8), calc(50% - 35ch));
}

.citrine-map-pin-search-field {
  display: flex;
  align-items: center;
  gap: var(--citrine-space-8);
  padding: 0 var(--citrine-space-12);
  color: var(--citrine-ink-3);
}

.citrine-map-pin-search-icon {
  flex: none;
}

.citrine-map-pin-search-input {
  flex: 1;
  min-width: 0;
  height: var(--citrine-target-min);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--citrine-ink);
  font: inherit;
  font-size: var(--citrine-font-size-14);
}

.citrine-map-pin-search-input::placeholder {
  color: var(--citrine-ink-3);
}

/* The ring sits on the whole box, not the bare input, so it outlines what looks like the field. */
.citrine-map-pin-search-input:focus-visible {
  outline: none;
}

.citrine-map-pin-search:focus-within {
  outline: 2px solid var(--citrine-accent);
  outline-offset: -2px;
}

.citrine-map-pin-search-list {
  max-height: 240px;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  border-top: 1px solid var(--citrine-line);
  list-style: none;
}

.citrine-map-pin-search-option {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--citrine-space-8) var(--citrine-space-12);
  cursor: pointer;
}

.citrine-map-pin-search-option + .citrine-map-pin-search-option {
  border-top: 1px solid var(--citrine-line);
}

.citrine-map-pin-search-option:hover,
.citrine-map-pin-search-option[aria-selected='true'] {
  background: var(--citrine-surface-2);
}

.citrine-map-pin-search-main {
  color: var(--citrine-ink);
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-500);
}

.citrine-map-pin-search-secondary,
.citrine-map-pin-search-note {
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-13);
}

.citrine-map-pin-search-note {
  margin: 0;
  padding: var(--citrine-space-8) var(--citrine-space-12);
  border-top: 1px solid var(--citrine-line);
}

.citrine-map-pin-search-list[hidden],
.citrine-map-pin-search-note[hidden] {
  display: none;
}

.citrine-map-pin-is-expanded .citrine-map-pin-search {
  top: calc(var(--citrine-space-8) + env(safe-area-inset-top, 0px));
  left: 50%;
  right: auto;
  width: calc(
    100% - var(--citrine-space-8) * 2 - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)
  );
  max-width: 70ch;
  transform: translateX(-50%);
}

.citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-views,
.citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-expand {
  top: calc(var(--citrine-space-8) * 2 + var(--citrine-target-min) + env(safe-area-inset-top, 0px));
}

.citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-views {
  left: max(calc(var(--citrine-space-8) + env(safe-area-inset-left, 0px)), calc(50% - 35ch));
}

.citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-expand {
  right: max(calc(var(--citrine-space-8) + env(safe-area-inset-right, 0px)), calc(50% - 35ch));
}

/* ── Keyboard ──────────────────────────────────────────────────────────────────────────────── */
.citrine-map-pin-view:focus-visible,
.citrine-map-pin-zoom-button:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: -2px;
}

/* ── Phones ────────────────────────────────────────────────────────────────────────────────── */
/* A partner uses this standing at a gate, one-handed, often in sunlight. The targets grow to the
   44px the hand actually needs, and the controls move off the very edge so a thumb reaching from the
   side of the screen does not catch them while panning. */
@media (max-width: 480px) {
  .citrine-map-pin-view {
    min-height: 40px;
    padding: 0 var(--citrine-space-16);
    font-size: var(--citrine-font-size-14);
  }

  .citrine-map-pin-zoom-button {
    width: 44px;
    height: 44px;
  }

  .citrine-map-pin-search-input {
    height: var(--citrine-target-min);
  }

  .citrine-map-pin-has-search .citrine-map-pin-views,
  .citrine-map-pin-has-search .citrine-map-pin-expand {
    top: calc(var(--citrine-space-8) * 2 + var(--citrine-target-min));
  }

  .citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-views,
  .citrine-map-pin-is-expanded.citrine-map-pin-has-search .citrine-map-pin-expand {
    top: calc(var(--citrine-space-8) * 2 + var(--citrine-target-min) + env(safe-area-inset-top, 0px));
  }
}

/* ── Matrix ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Matrix. Rebuilt on antd `Table` (Group B wave 5): 160 lines down to this.
   Cell borders, header treatment, row hover, the outer frame and — the big one — the sticky first
   column all come from antd now (`bordered`, `size="small"`, `fixed: 'left'` + `scroll.x`). The
   hand-written `position: sticky` block and its z-index/shadow companions are gone.

   Two things have no antd equivalent and stay (plus the two pinned-column repairs further down):

   1. The scroll region's focus ring. Matrix wraps antd's table in its own `role="region"`
      `tabIndex={0}` element so the grid is announced and reachable; antd draws nothing for that
      element because it does not know it exists.
   2. The second line under a row or column label (`sub` — typically a plan code). antd has no
      slot for a sub-label, so its typography is Citrine's.

   `.citrine-visually-hidden` is NOT redefined here — Text.css owns it. Matrix.css used to carry a
   duplicate copy of it, which is the kind of thing a rebuild is a good moment to stop doing. */

.citrine-matrix-scroll:focus-visible {
  outline: 2px solid var(--citrine-data);
  outline-offset: 2px;
}

/* ── THE PINNED COLUMN IS ALWAYS OPAQUE ─────────────────────────────────────────────────────────
   The row label is a `<th scope="row">`, and antd paints EVERY `<th>` in the body with the header
   fill (`table/style/index.js:138-143`, `.ant-table-tbody > tr > th { background: headerBg }`). That
   rule outranks antd's own "a pinned cell gets the table's ground" (`fixed.js:42`), so an app that
   sets `headerBg` translucent — a quieter header is a common choice — gets a pinned column the
   scrolled cells slide visibly UNDER, their marks drawn through the labels.

   The fix keeps the header fill and lays it over the container's ground: a one-colour gradient is a
   background IMAGE, so it stacks on top of a background COLOUR. Stock antd's opaque header fill
   covers the ground completely, so the stock look does not move by a pixel; a translucent fill now
   tints an opaque cell instead of a hole. Row hover (`.ant-table-cell-row-hover`, 0,4,0) still
   outranks this (0,3,2) and paints the whole row as before. */
.citrine-matrix .ant-table-thead > tr > th.citrine-is-sticky,
.citrine-matrix .ant-table-tbody > tr > th.citrine-is-sticky,
/* …and under `sticky`, the WHOLE label row is pinned (antd moves it into `.ant-table-sticky-holder`),
   so the rows scroll up beneath every column label, not just the corner. Same layering, same reason;
   an unpinned header row is left as antd draws it. */
.citrine-matrix .ant-table-sticky-holder .ant-table-thead > tr > th {
  background:
    linear-gradient(var(--ant-table-header-bg, transparent), var(--ant-table-header-bg, transparent)),
    var(--ant-color-bg-container, var(--citrine-surface));
}

/* ── THE GRID'S STACKING STAYS INSIDE THE GRID ──────────────────────────────────────────────────
   antd numbers a pinned cell's z-index from the column count (`--z-offset-reverse`, set per cell by
   rc-table `Cell/index.js:98-99`), so a wide grid's pinned column climbs past an app's sticky top
   bar (`--citrine-z-sticky`) and is drawn OVER it as the page scrolls. `isolation` gives the grid its
   own stacking context: every number antd picks is now ordered inside the grid, and the grid as a
   whole sits in the page's order like any other block. */
.citrine-matrix {
  isolation: isolate;
}

/* The caption's text is visually hidden — absolutely positioned — and its static place is out along
   the full width of a wide grid. With no positioned box between it and the page, the grid's own
   sideways scroller did not clip it, and on a phone the PAGE grew to that width (a 474px page at
   390). The caption holds it, so it sits inside the scroller like the cells do. */
.citrine-matrix caption {
  position: relative;
}

/* `lines="rows"`: hairlines between rows only (antd draws those without `bordered`), and the column
   labels on the table's own ground, in the quiet ink at body weight. */
.citrine-matrix.citrine-matrix-rows .ant-table-thead > tr > th {
  background: var(--ant-color-bg-container, var(--citrine-surface));
  color: var(--citrine-ink-3);
  font-weight: 400;
}

/* `highlightHeads`: the row labels and the column labels — a pivot's two sets of groups — on the
   page hue's wash in its deep ink (the accent's outside a `HueScope`). The wash is laid over the
   container's ground, as the pinned-column rule above does, so a translucent wash still leaves the
   pinned row labels opaque. */
.citrine-matrix.citrine-matrix-heads .ant-table-thead > tr > th,
.citrine-matrix.citrine-matrix-heads .ant-table-tbody > tr > th {
  background:
    linear-gradient(
      var(--citrine-hue-wash, var(--citrine-accent-wash)),
      var(--citrine-hue-wash, var(--citrine-accent-wash))
    ),
    var(--ant-color-bg-container, var(--citrine-surface));
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
}

/* The nested-row toggle: a bare "+" / "−" glyph, and an empty slot of its width on a row with
   nothing under it, so every row's words start on one line. */
.citrine-matrix-toggle,
.citrine-matrix-toggle-slot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 16px;
  block-size: 16px;
  margin-inline-end: var(--citrine-sp-2, 8px);
  vertical-align: middle;
}
.citrine-matrix-toggle {
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--citrine-ink-2);
  font-size: 14px;
  cursor: pointer;
}
.citrine-matrix-toggle:hover {
  color: var(--citrine-accent);
}
.citrine-matrix-rowtext {
  display: inline-block;
  vertical-align: middle;
}
.citrine-matrix-toggle:focus-visible {
  outline: 2px solid var(--citrine-focus-ring);
  outline-offset: 2px;
}

.citrine-matrix-rowsub {
  font-size: var(--citrine-font-size-10);
  color: var(--citrine-ink-4);
  font-weight: var(--citrine-font-weight-600);
}

/* ── Measure ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — Measure. A centred column at a form's readable width (`--citrine-measure`).

   `inline-size: 100%` lets it fill a narrower parent; `max-inline-size` stops it at the measure;
   `margin-inline: auto` centres it in a wider one. Nothing else: it draws no surface and leaves the
   layout of its children to the caller. */
.citrine-measure {
  inline-size: 100%;
  max-inline-size: var(--citrine-measure);
  margin-inline: auto;
}

/* `size="field"` — one field held at a field's width, at the start of its column rather than
   centred: it sits in a form's flow, under its siblings' left edge. */
.citrine-measure.citrine-measure-field {
  max-inline-size: var(--citrine-measure-field);
  margin-inline: 0;
}

/* ── MediaFrame ────────────────────────────────────────────────────────────────────────── */
/* UI kit — MediaFrame. Its media child as a block the full width of the column, with no border —
   so an inline `<iframe>` or `<audio>` neither leaves a gap under itself (the inline baseline) nor
   draws the browser's default iframe border. Only the frame's own direct media children. */
.citrine-media-frame {
  inline-size: 100%;
  min-inline-size: 0;
}

.citrine-media-frame > :is(audio, video, iframe, img, canvas, embed, object) {
  display: block;
  inline-size: 100%;
  border: 0;
}

/* ── Menu ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Menu styles. Source: artifact `.menu` / `.menu-item`.

   Tokens only. The popup SURFACE antd draws (`.ant-dropdown-menu`) is antd's own and is not
   restyled here (ruling D15; see `Menu.tsx`).

   Most of what is left styles the content this kit renders inside each entry, on our own
   `citrine-menu-*` classes. The one exception is `.citrine-menu-cut`, which sits on antd's entry
   node itself and has to outrank antd's own entry rule to land — see the rule below. */

.citrine-menu-item {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-space-2);
  min-width: 0;
}

.citrine-menu-item-label {
  color: inherit;
}

/* The reason a disabled entry cannot be used. Quieter than the label, but present — a dimmed entry
   with no explanation is a dead end. Wraps rather than truncating: a reason cut to an ellipsis is
   the same dead end with extra steps. */
.citrine-menu-item-reason {
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-11);
  line-height: var(--citrine-line-height-1p25);
  white-space: normal;
}

/* A destructive entry is separated from the benign ones above it, so the pointer never lands on
   "Delete" while aiming at the entry above. Margin carries the gap; the rule carries the meaning.

   The class sits on antd's `<li class="ant-dropdown-menu-item">`, and antd's dropdown sheet sets
   `margin: 0` and its own padding on `.ant-dropdown .ant-dropdown-menu .ant-dropdown-menu-item`
   (three classes; its `:where()` hash adds none). A bare `.citrine-menu-cut` loses that for margin
   and padding, so the selector is anchored at this component's own popup root (`.citrine-menu`,
   via `rootClassName`) and carries four classes, which wins whatever order the sheets load in. */
.citrine-menu .ant-dropdown-menu .ant-dropdown-menu-item.citrine-menu-cut {
  margin-top: var(--citrine-space-4);
  border-top: 1px solid var(--citrine-line);
  padding-top: var(--citrine-space-4);
}

/* ── Modal ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Modal. Source: artifact `.modal`, `.ov-head`, `.ov-title`, `.ov-body`, `.ov-foot`
   (citrine-reference.css). Tokens only. Scoped under our own `.citrine-modal` class (which lands on
   antd's Modal root node via the `className` prop) so it never collides with Drawer's copy of
   the same `.citrine-ov-*` class names. */

.citrine-modal .citrine-ov-head {
  padding: var(--citrine-sp-5) var(--citrine-sp-6) var(--citrine-sp-3);
}

.citrine-modal .citrine-ov-title {
  font-size: var(--citrine-font-size-17);
  font-weight: var(--citrine-font-weight-700);
  letter-spacing: -0.01em;
}

.citrine-modal .citrine-ov-body {
  padding: var(--citrine-sp-2) var(--citrine-sp-6) var(--citrine-sp-5);
  overflow-y: auto;
}

.citrine-modal .citrine-ov-foot {
  padding: var(--citrine-sp-4) var(--citrine-sp-6);
  border-top: 1px solid var(--citrine-line);
  background: var(--citrine-surface-2);
  display: flex;
  gap: var(--citrine-sp-2);
  justify-content: flex-end;
}

.citrine-modal .citrine-confirm-desc {
  margin: 0;
  font-size: var(--citrine-font-size-13p5);
  color: var(--citrine-ink-2);
}

/* ── Note ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Note. 95 lines before Citrine 2.0 (Group B, wave 4); this is what survives the rebuild
   onto antd `Alert`.

   Everything else is gone because antd now draws it: the flex row, the padding, the radius, the
   icon sizing, the title treatment and all five tone washes are `Alert`'s own
   (`antd/es/alert/style/index.js`). The `neutral` tone — which antd has no `type` for — is painted
   from `theme.useToken()` inline in `Note.tsx`, NOT from a Citrine brand colour and not by
   overriding an `.ant-*` class.

   What is left is one rule, and it is layout rather than skin: two Notes stacked directly against
   each other. antd's `Alert` carries no outer margin at all — stock antd expects the parent to be a
   `Flex`/`Space` with a `gap`. One consumer has 482 Note sites written against a component that
   spaced itself, so dropping this would push adjacent callouts flush together across the product
   for no design gain. The `.citrine-note` class it hangs on is still applied by `Note.tsx`.

   Deliberately NOT carried over: `.citrine-note-content ul` / `li`. `List.css`'s header comment
   still cites that selector as the reason a `<List>` inside a `<Note>` keeps Note's own list
   spacing instead of List's reset — that is no longer true, and it is `List`'s comment to correct
   (another wave owns that file). Measured blast radius in that consumer: one file, its wallet
   screen (control: 119 files nest SOME element inside a Note, so the scan finds real hits). */

.citrine-note + .citrine-note {
  margin-top: var(--citrine-sp-3);
}

/* The ACCENT tone: antd has no accent alert, so it is drawn as `info` and its ground and edge are
   repainted from the accent family here (its words and icon ride `Alert`'s own `styles` slots in
   Note.tsx). Inside a `HueScope` the accent slots are the section's hue, so the note follows the
   page's section colour. Declared BEFORE the line and quiet forms, which take the ground and the
   edge off again at the same weight. */
.citrine-note-accent {
  background: var(--citrine-accent-wash);
  border-color: var(--citrine-accent-line);
}

/* The LINE form: one icon-led advisory line instead of an object on the page. antd's `Alert` always
   draws a ground, an edge and block padding; there is no prop that removes them (`banner` is a
   full-bleed page banner with its own layout, not an unboxed line), so they come off here.

   The inline padding goes too, so the note's text lines up with the copy above it rather than
   sitting indented inside an invisible box. Block padding is kept at zero rather than reduced: a
   line's spacing belongs to whatever stacks it. */
.citrine-note-line {
  background: transparent;
  border: 0;
  padding-block: 0;
  padding-inline: 0;
}

/* The QUIET form: an unboxed note that still leads with a title. Same removals as the line (no
   ground, no edge, no inline padding, so the text lines up with the copy above it) plus one thing
   the line does not have: a hairline above, which is what separates the note from the content it
   follows once the box is gone. The air under the hairline is the only block padding it keeps.
   The icon and title metrics are inline `styles` slots in `Note.tsx`, for the reason given there. */
.citrine-note-quiet {
  background: transparent;
  border: 0;
  border-block-start: var(--citrine-note-quiet-rule-width) solid var(--citrine-note-quiet-rule);
  border-radius: 0;
  padding-block: var(--citrine-note-quiet-gap) 0;
  padding-inline: 0;
}

/* ── NumberInput ───────────────────────────────────────────────────────────────────────── */
/* UI kit — NumberInput styles. Source: artifact `.ctrl.is-num`.
   `.citrine-ctrl` base (including `.citrine-ctrl.citrine-is-error`) lives in `TextInput/TextInput.css`.

   Ant Design's InputNumber renders a wrapper element as its root (not the bare `<input>`), so our
   own `.citrine-ctrl` className lands on that wrapper rather than the native input. The outer box
   (border/radius/background/height) still comes through; the fine chrome inside the wrapper is
   Ant Design's own and is not reached through `.ant-*` selectors here. That inner-chrome delta is
   covered by `adapters/antd/theme.ts` (ConfigProvider tokens), not by this file.

   TABULAR FIGURES are set on both nodes. On the wrapper for anything drawn beside the input (a
   prefix or suffix); on the `<input>` itself — named `citrine-num-input` through antd's semantic
   `classNames.input` — because that is where the digits are, and inheritance does not reach them:
   Chromium's UA sheet gives form controls a `font` shorthand that resets `font-variant-numeric`
   on the `<input>`. */
.citrine-is-num,
.citrine-num-input {
  font-variant-numeric: tabular-nums;
}

/* FULL WIDTH, like every other field. antd's InputNumber has a fixed default width, so a number or
   money box stopped at ~90–180px while the text fields beside it filled their column. Inside an
   `InputAffix` group (now `block`) it takes the room the affix leaves. */
.citrine-is-num {
  width: 100%;
}

/* ── OptionCardGroup ───────────────────────────────────────────────────────────────────── */
/* UI kit — OptionCardGroup styles. Group B wave 8.
 *
 * Structure only. Every colour, radius, spacing step and type size is seeded inline from
 * `theme.useToken()` in `OptionCardGroup.tsx`, so this control draws from antd's own scale.
 * Gone from this file: `--citrine-sp-1`, `--citrine-sp-2`, `--citrine-sp-3`, `--citrine-line-2`,
 * `--citrine-r-sm`, `--citrine-surface`, `--citrine-ink`, `--citrine-ink-3`, `--citrine-accent`,
 * `--citrine-accent-wash`, `--citrine-font-weight-700`, `--citrine-font-size-12`.
 *
 * The 640px collapse is NOT moved onto an antd breakpoint. antd's nearest are `screenSM` 576 and
 * `screenMD` 768; either would move the single-column point for every caller that relies on it, a
 * visible behaviour change rather than a skin change. Since 5.0 it applies only under
 * `.citrine-option-cards-collapse` (the `collapse` prop, default on), so a caller can turn it off.
 *
 * There is deliberately no hover, focus or transition rule here. antd would supply
 * `controlItemBgHover`, an `outline` focus ring and `motionDurationMid` for all three, but each
 * would be a NEW behaviour rather than the removal of a brand skin — and the browser's own focus
 * ring is untouched because nothing here sets `outline`. */

.citrine-option-cards {
  display: grid;
  gap: var(--citrine-option-cards-gap);
}
.citrine-option-cards-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.citrine-option-cards-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.citrine-option-cards-cols-auto {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
/* A 48px floor so a card is a thumb's target on a phone — universal, whatever marks the choice. The
   card is a grid of one column (the words) unless `indicator="radio"` adds the mark before them. */
.citrine-option-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  min-height: 48px;
  padding: var(--citrine-option-card-padding);
  text-align: left;
  border: var(--citrine-option-card-border-width) solid var(--citrine-option-card-border);
  border-radius: var(--citrine-option-card-radius);
  background: var(--citrine-option-card-bg);
  color: var(--citrine-option-card-ink);
  cursor: pointer;
}
.citrine-option-card-is-on {
  border-color: var(--citrine-option-card-on-border);
  background: var(--citrine-option-card-on-bg);
  box-shadow: 0 0 0 var(--citrine-option-card-border-width) var(--citrine-option-card-on-border);
}
.citrine-option-card:not(:disabled):hover {
  border-color: var(--citrine-option-card-hover-border);
}
.citrine-option-card-is-on:not(:disabled):hover {
  border-color: var(--citrine-option-card-on-border);
}
/* The kit draws its own ring on a control it draws itself: 2px of the action colour, 2px out. */
.citrine-option-card:focus-visible {
  outline: 2px solid var(--citrine-option-card-on-border);
  outline-offset: 2px;
}
/* `indicator="radio"`: a visible radio mark beside the words. The card becomes two columns — the
   mark, then the words. */
.citrine-option-cards-indicator-radio .citrine-option-card {
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 10px;
}
.citrine-option-card-dot {
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  border: 1px solid var(--citrine-option-card-border);
  border-radius: 50%;
  background: var(--citrine-option-card-bg);
}
.citrine-option-card-is-on .citrine-option-card-dot {
  border: 5px solid var(--citrine-option-card-on-border);
}
.citrine-option-card:disabled .citrine-option-card-dot {
  background: var(--citrine-option-card-disabled-bg);
}
.citrine-option-card-text {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-option-card-gap);
  min-width: 0;
}
.citrine-option-card-label {
  font-weight: var(--citrine-option-card-label-weight);
}
.citrine-option-card-description {
  color: var(--citrine-option-card-description-ink);
  font-size: var(--citrine-option-card-description-size);
}
/* antd's own disabled treatment — `colorTextDisabled` on `colorBgContainerDisabled` — rather than
   a blanket `opacity: 0.55`, which is not a look antd draws anywhere. */
.citrine-option-card:disabled {
  background: var(--citrine-option-card-disabled-bg);
  color: var(--citrine-option-card-disabled-ink);
  cursor: not-allowed;
}
/* SELECTED **and** DISABLED. Without this rule the card keeps a full-strength primary border and
   ring, which is the visual language of an interactive, focused control — so an inert card wearing
   one invites a click that does nothing. It is live today in a
   consumer's review screen that passes a `value` with the whole group
   inert.

   The mechanism, probed rather than assumed (`getComputedStyle` over this exact stylesheet returned
   `#1677ff` and `0 0 0 1px #1677ff` for a card carrying both states): the rule above declares only
   `background`, `color` and `cursor`, so it never CONTESTS `.citrine-option-card-is-on`'s
   `border-color` and `box-shadow` and those survive untouched. Specificity is real but decides only
   `background` and `color`.

   The treatment is antd's own, from `Radio.Button` — the nearest thing antd ships to a selectable
   card. `radio/style/index.js:383-388` gives a checked-and-disabled button `borderColor:
   colorBorder`, `boxShadow: 'none'` and a fill of `buttonCheckedBgDisabled`
   (= `controlItemBgActiveDisabled`, `:430`). Muted, not erased: that fill is denser than a plain
   disabled control's, so the card still reads as the chosen one. Text needs no declaration here —
   antd's `buttonCheckedColorDisabled` is `colorTextDisabled` (`:431`), which the rule above already
   applied.

   The selector is compound ON PURPOSE. `.citrine-option-card-is-on:disabled` would tie
   `.citrine-option-card:disabled` at (0,2,0) and win only on source order, so a later reshuffle of
   this file would silently put the ring back. (0,3,0) does not care where the rule sits.
   `OptionCardGroup.test.tsx` loads this file and asserts the result, because Vitest runs with
   `css: false` and no ordinary component test can see a cascade fix. */
.citrine-option-card.citrine-option-card-is-on:disabled {
  border-color: var(--citrine-option-card-border);
  background: var(--citrine-option-card-on-disabled-bg);
  box-shadow: none;
}
/* `collapse` (default on): a fixed count folds to one column on a narrow viewport. */
@media (max-width: 640px) {
  .citrine-option-cards-collapse.citrine-option-cards-cols-2,
  .citrine-option-cards-collapse.citrine-option-cards-cols-3 {
    grid-template-columns: 1fr;
  }
}

/* ── Page ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Page + Screen. Group B wave 7a.
 *
 * `Page` keeps no skin of its own: it has no background, no border and no shadow, and its two
 * gutter values are seeded from `theme.useToken()` in `Page.tsx` (antd's `paddingLG` and
 * `padding`) rather than from `--citrine-sp-6` / `--citrine-sp-4`.
 *
 * What is left is geometry: the content cap (`--citrine-page-max`, 1480px) and the narrower gutter
 * below `--citrine-bp-md`. `Screen` is a show/hide switch and never had skin.
 *
 * Whatever pads the page's sides also says so, as `--citrine-app-gutter` (see `AppShell.css`), so a
 * full-width bar inside can bleed by exactly that much.
 *
 * THREE HOOKS let a frame around the page take over its sides without this sheet naming the frame:
 * `--citrine-page-pad-inline` (the side padding), `--citrine-page-gutter` (what the page reports as
 * `--citrine-app-gutter`) and `--citrine-page-max` (the cap). Unset, the page pads and caps itself.
 * `AppShell layout="column"` sets all three on a desk, because its column is already the padding and
 * the cap — two would stack and a heading would miss the column's edge. All three are deliberately
 * WIDTH-INDEPENDENT: a frame wants the same override at every size, so the narrow branch below reads
 * them too, unchanged.
 *
 * `--citrine-page-pad-inline-desk` is a FOURTH, separate hook, desktop-only on purpose: `Page`'s own
 * `padInline` prop writes here, not to `--citrine-page-pad-inline` — that prop is for "this page's
 * sides differ from its own top/bottom", a per-page choice, not a frame's. Reusing the frame's hook
 * would have carried a page's desktop override down into the narrow gutter too, which no caller
 * asked for (narrow already has its own `--citrine-page-pad-narrow` default). */

.citrine-page {
  --citrine-app-gutter: var(--citrine-page-gutter, var(--citrine-page-pad));
  padding: var(--citrine-page-pad);
  padding-inline: var(
    --citrine-page-pad-inline-desk,
    var(--citrine-page-pad-inline, var(--citrine-page-pad))
  );
  max-width: var(--citrine-page-max);
  width: 100%;
}

@media (max-width: 900px) {
  .citrine-page {
    --citrine-app-gutter: var(--citrine-page-gutter, var(--citrine-page-pad-narrow));
    padding: var(--citrine-page-pad-narrow);
    padding-inline: var(--citrine-page-pad-inline, var(--citrine-page-pad-narrow));
  }
}

.citrine-screen {
  display: none;
}

.citrine-screen.citrine-is-active {
  display: block;
}

/* ── PageHeader ────────────────────────────────────────────────────────────────────────── */
/* UI kit — PageHeader. Source: artifact `.pagehead` block (citrine-reference.css).

   Group C (2.0): structure unchanged — antd removed `PageHeader` in v5 and the obvious recomposition
   regresses the `as` prop (see the block comment in `PageHeader.tsx`). Colours and metrics now come
   from antd's live theme via `antdTheme.useToken()`, handed down as `--citrine-pagehead-*` custom
   properties. Every fallback is the Citrine token the rule used to read.

   `flex: 1 1 320px` and `max-width: 72ch` are STRUCTURAL: the first is what makes the actions wrap
   under the title on a narrow viewport instead of squeezing it, the second is a measure limit for
   readability. Both stay literal. */

.citrine-pagehead {
  display: flex;
  align-items: flex-start;
  gap: var(--citrine-pagehead-gap, var(--citrine-sp-4));
  flex-wrap: wrap;
  margin-bottom: var(--citrine-pagehead-gap-below, var(--citrine-sp-6));
}

/* In a `Stack`, the column's gap already sits under the header. Give that part back, so the header
   is its own gap-below from the next block — not gap-below plus the column's gap. */
.citrine-stack > .citrine-pagehead {
  margin-bottom: max(
    0px,
    calc(var(--citrine-pagehead-gap-below, var(--citrine-sp-6)) - var(--citrine-stack-gap, 0px))
  );
}

.citrine-pagehead-main {
  flex: 1 1 320px;
  min-width: 0;
}

.citrine-pagetitle {
  margin: 0;
  font-size: var(--citrine-pagehead-title-size, var(--citrine-font-size-26));
  font-weight: var(--citrine-pagehead-title-weight, var(--citrine-font-weight-750));
  line-height: var(--citrine-pagehead-title-line, var(--citrine-line-height-1p15));
  color: var(--citrine-pagehead-title-ink, var(--citrine-ink));
  /* The -0.025em display tightening is gone. It was a Citrine typographic signature; stock antd's
     `Typography.Title` sets no letter-spacing at any level, and this header is meant to read as
     antd now. */
  text-wrap: balance;
}

.citrine-pagesub {
  margin: var(--citrine-pagehead-sub-gap, var(--citrine-sp-2)) 0 0;
  font-size: var(--citrine-pagehead-sub-size, var(--citrine-font-size-13p5));
  color: var(--citrine-pagehead-sub-ink, var(--citrine-ink-3));
  max-width: 72ch;
}

/* `onBack`: the back button and the heading on one line, centred on each other. */
.citrine-pagetitle-row {
  display: flex;
  align-items: center;
  gap: var(--citrine-pagehead-actions-gap, var(--citrine-sp-2));
}
.citrine-pagetitle-row > .citrine-pagetitle {
  margin: 0;
}
/* The back button: a 32px square in the section's hue — its line as the border, its deep as the glyph. */
.citrine-pagetitle-row > .citrine-pageback {
  flex: none;
  inline-size: 32px;
  block-size: 32px;
  border-radius: 8px;
  border-color: var(--citrine-hue-line, var(--citrine-line));
  background: var(--citrine-surface);
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
}

/* `subActions`: the subtitle and its controls on one line, wrapping under it on a narrow column. The
   row carries the gap the subtitle used to carry, so the text sits on the controls' centre line. */
.citrine-pagesub-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--citrine-pagehead-actions-gap, var(--citrine-sp-2));
  margin-top: var(--citrine-pagehead-sub-gap, var(--citrine-sp-2));
}
.citrine-pagesub-row > .citrine-pagesub {
  margin: 0;
}

/* A field in the header (a date-range or organisation picker beside the sub, or among the actions)
   has no message line under it, so antd's Form.Item bottom margin only made its box taller than its
   control — a blank band under the pickers once they wrap on a phone. Same answer as `Topbar.css`:
   antd's own token, on its component roots. */
.citrine-pagehead [class*='css-var-'] {
  --ant-form-item-margin-bottom: 0px;
}

.citrine-pageactions {
  display: flex;
  align-items: center;
  gap: var(--citrine-pagehead-actions-gap, var(--citrine-sp-2));
  flex-wrap: wrap;
}

/* band — the book's spine. The accent ink as a field, title and sub reversed out, the page's first
   and only saturated region, so the eye starts where the book starts. */
.citrine-pagehead-band {
  padding: var(--citrine-sp-5, 20px) var(--citrine-sp-6, 24px);
  border-radius: var(--citrine-r-sm, 6px);
  background: var(--citrine-accent);
}
.citrine-pagehead-band .citrine-pagetitle {
  color: var(--citrine-accent-ink, #ffffff);
}
.citrine-pagehead-band .citrine-pagesub {
  color: var(--citrine-accent-line, #c4d2e9);
  max-width: none;
}

/* A ghost button on the band — a help icon beside the title, an action — is antd's text button, which
   paints its own dark ink: navy on the navy spine. Reverse it out with the title. */
.citrine-pagehead.citrine-pagehead-band {
  color: var(--citrine-accent-ink, #ffffff);
}
.citrine-pagehead.citrine-pagehead-band .ant-btn-variant-text:not(:disabled):not(.ant-btn-disabled) {
  color: inherit;
}
.citrine-pagehead.citrine-pagehead-band .ant-btn-variant-text:not(:disabled):not(.ant-btn-disabled):hover {
  color: inherit;
  background: color-mix(in srgb, var(--citrine-accent-ink, #ffffff) 16%, transparent);
}

/* ── wash: the section's band (opt-in) ───────────────────────────────────────────────────────────
   With `wash`, a header inside a `HueScope`, or wearing `hue` itself, is the section's BAND: the ramp's
   wash as the ground, its line as a hairline, and the title and sub in its deep. Without `wash` a hued
   header stays plain — the hue still reaches its buttons and links through the scope. Outside a hue
   `wash` draws nothing: the ramp it paints with only exists inside one. */
:is(.citrine-hued .citrine-pagehead, .citrine-pagehead.citrine-hued).citrine-pagehead-wash:not(
    .citrine-pagehead-band
  ) {
  padding: var(--citrine-sp-5, 20px) var(--citrine-sp-6, 24px);
  border: 1px solid var(--citrine-hue-line);
  border-radius: var(--citrine-r-sm, 10px);
  background: var(--citrine-hue-wash);
}
:is(.citrine-hued .citrine-pagehead, .citrine-pagehead.citrine-hued).citrine-pagehead-wash:not(
    .citrine-pagehead-band
  )
  :is(.citrine-pagetitle, .citrine-pagesub) {
  color: var(--citrine-hue-deep);
}

/* ── bleed: a block sized to a centred column plus a bleed each side, on a desk (opt-in) ─────────
   Meant for `AppShell layout="column"`. The header becomes a rounded block that reaches past the
   column by `--citrine-pagehead-band-bleed` on each side and pads back in by the same, so the heading
   inside lands on the column's edge like the content under it; and air above it so it does not sit
   flush against the top bar. The air is taken from the page's own top padding when the header opens
   the page, so the heading lands at the same height whether or not a `Page` wraps it. Measured on a
   consumer's mockup at 1440: 14px of air, 22px over the heading, 20px under it, and 28px of bleed.
   Every value is a `--citrine-pagehead-band-*` token. With `wash`, the hairline is inset rather than a
   border, so the bleed is exact.

   ON A PHONE the same header is flat and edge to edge: it reaches past the page's own side padding
   (`--citrine-app-gutter`, which `Page` sets), and up to the top bar when it opens the page, with no
   corners; with `wash` its only edge is the hue's line along the bottom. */
@media (max-width: 900px) {
  .citrine-pagehead-bleed {
    margin-inline: calc(-1 * var(--citrine-app-gutter, 0px));
    padding: var(--citrine-pagehead-band-pad-top-narrow) var(--citrine-pagehead-band-pad-x-narrow)
      var(--citrine-pagehead-band-pad-bottom-narrow);
    border-radius: 0;
  }

  .citrine-pagehead-bleed:first-child {
    margin-block-start: calc(-1 * var(--citrine-app-gutter, 0px));
  }

  :is(
      .citrine-hued .citrine-pagehead,
      .citrine-pagehead.citrine-hued
    ).citrine-pagehead-bleed.citrine-pagehead-wash:not(.citrine-pagehead-band) {
    padding: var(--citrine-pagehead-band-pad-top-narrow) var(--citrine-pagehead-band-pad-x-narrow)
      var(--citrine-pagehead-band-pad-bottom-narrow);
    border: 0;
    border-block-end: var(--citrine-pagehead-band-hairline) solid var(--citrine-hue-line);
    border-radius: 0;
  }
}

@media not all and (max-width: 900px) {
  .citrine-pagehead-bleed {
    margin-inline: calc(-1 * var(--citrine-pagehead-band-bleed));
    padding: var(--citrine-pagehead-band-pad-top) var(--citrine-pagehead-band-bleed)
      var(--citrine-pagehead-band-pad-bottom);
    border-radius: var(--citrine-pagehead-band-radius);
  }

  .citrine-pagehead-bleed:first-child {
    margin-block-start: calc(var(--citrine-pagehead-band-air) - var(--citrine-page-pad, 0px));
  }

  :is(
      .citrine-hued .citrine-pagehead,
      .citrine-pagehead.citrine-hued
    ).citrine-pagehead-bleed.citrine-pagehead-wash:not(.citrine-pagehead-band) {
    padding: var(--citrine-pagehead-band-pad-top) var(--citrine-pagehead-band-bleed)
      var(--citrine-pagehead-band-pad-bottom);
    border: 0;
    border-radius: var(--citrine-pagehead-band-radius);
    box-shadow: inset 0 0 0 var(--citrine-pagehead-band-hairline) var(--citrine-hue-line);
  }
}

/* The solid spine takes the ramp's BUTTON pair, the one fill-and-ink pair every hue guarantees at 4.5:1.
   The sub goes to the same ink: the ramp's line on its button fill does not carry text. */
:is(.citrine-hued .citrine-pagehead, .citrine-pagehead.citrine-hued).citrine-pagehead-band {
  background: var(--citrine-hue-btn);
  color: var(--citrine-hue-btn-ink);
}
:is(.citrine-hued .citrine-pagehead, .citrine-pagehead.citrine-hued).citrine-pagehead-band
  :is(.citrine-pagetitle, .citrine-pagesub) {
  color: var(--citrine-hue-btn-ink);
}

/* ── actionsNarrow="stack": the actions as full-width bars on a phone (opt-in) ────────────────────
   Same breakpoint as `bleed`: one header, one idea of where a phone begins. The actions row takes a
   whole line of its own, so it always lands under the heading rather than beside it when it happens
   to fit, and turns into a column whose children stretch to its width.

   The height floor is the kit's target floor, not a new number: antd draws a button 32px tall, and a
   full-width bar a thumb must hit should not be shorter than every other target the kit holds open.
   `min-block-size` beats antd's fixed `height` without naming its class. `> *` is this component's
   own slot, not another component's class. */
@media (max-width: 900px) {
  .citrine-pagehead-stack .citrine-pageactions {
    flex: 1 0 100%;
    flex-direction: column;
    align-items: stretch;
  }

  .citrine-pagehead-stack .citrine-pageactions > * {
    min-block-size: var(--citrine-target-min);
  }
}

/* ── Pagination ────────────────────────────────────────────────────────────────────────── */
/* UI kit — Pagination. Rebuilt on antd `Pagination` (Group B wave 5): 63 lines down to this.
   Every page pill, its hover and active states, the prev/next controls and the size changer are
   antd's now. What survives is the `loadMore` variant, which has no antd equivalent at all — it is
   a Citrine button plus a count, not a pager — and the tabular figures the count is set in. */

.citrine-pagination-total {
  font-variant-numeric: tabular-nums;
}

/* The pager sits at the END of its row, as a list's footer draws it. `safe`: a pager wider than its
   column falls back to the start, so the overflow runs off the end (scrollable), never off the start. */
nav.citrine-pagination {
  display: flex;
  align-items: center;
  justify-content: safe flex-end;
}

/* `lead`: the count (or whatever the caller puts there) at the start, the pager at the end. */
.citrine-pagination-has-lead {
  flex-wrap: wrap;
  gap: var(--citrine-sp-3);
}
.citrine-pagination-lead {
  margin-inline-end: auto;
}

.citrine-pagination-load-more {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--citrine-sp-3);
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-12p5);
}

/* ── PaletteRow ────────────────────────────────────────────────────────────────────────── */
/* UI kit — PaletteRow / TypeIcon. Source: artifact `.paletterow`, `.paletterow:hover`,
   `.typeicon`, `.typeicon svg` (citrine-reference.css).

   Group C (2.0): structure unchanged — there is no antd draggable palette row. Colours and metrics
   now come from antd's live theme via `antdTheme.useToken()` in `PaletteRow.tsx`, handed down as
   `--citrine-paletterow-*` / `--citrine-typeicon-*` custom properties. Every fallback is the
   Citrine token the rule used to read.

   The tile sizes (30px, 26px) and the glyph sizes (15px, 14px) are STRUCTURAL: they are what makes
   a palette row a fixed-height, scannable list rather than a stack of buttons. They stay literal. */

.citrine-paletterow {
  display: flex;
  align-items: center;
  gap: var(--citrine-paletterow-gap, var(--citrine-sp-3));
  padding: var(--citrine-paletterow-pad-y, 9px) var(--citrine-paletterow-pad-x, var(--citrine-sp-3));
  border-radius: var(--citrine-paletterow-radius, var(--citrine-r-sm));
  background: var(--citrine-paletterow-bg, var(--citrine-surface-2));
  font-size: var(--citrine-paletterow-font-size, var(--citrine-font-size-12p5));
  /* Transparent, but reserved — the row must not jump a pixel when :hover paints the border. */
  border: var(--citrine-paletterow-line-width, 1px) solid transparent;
}

.citrine-paletterow[role='button'] {
  cursor: pointer;
}

.citrine-paletterow:hover {
  border-color: var(--citrine-paletterow-hover-line, var(--citrine-data-line));
  background: var(--citrine-paletterow-hover-bg, var(--citrine-data-wash));
}

.citrine-paletterow:focus-visible {
  outline: var(--citrine-paletterow-focus-width, 2px) solid
    var(--citrine-paletterow-focus-ink, var(--citrine-data));
  outline-offset: 2px;
}

.citrine-paletterow.citrine-is-dragging {
  opacity: 0.6;
}

/* ── TypeIcon ───────────────────────────────────────────────────────────────────────────────────
   `tone="data"` keeps reading `--citrine-data*` directly. That is deliberate, not an oversight:
   antd has exactly one accent, Citrine's palette has two, and the design of record (§3.4) keeps the
   data accent as a Citrine-owned token for exactly this reason. Resolving both tones to
   `colorPrimary` would make the `tone` prop compile green and do nothing. */
.citrine-typeicon {
  width: 30px;
  height: 30px;
  border-radius: var(--citrine-typeicon-radius, var(--citrine-radius-9));
  flex: none;
  display: grid;
  place-items: center;
  background: var(--citrine-data-wash);
  color: var(--citrine-data);
}

.citrine-typeicon.citrine-typeicon-accent {
  background: var(--citrine-typeicon-accent-bg, var(--citrine-accent-wash));
  color: var(--citrine-typeicon-accent-ink, var(--citrine-accent-deep));
}

.citrine-typeicon svg {
  width: 15px;
  height: 15px;
}

.citrine-paletterow-text {
  min-width: 0;
  flex: 1;
}

.citrine-paletterow-label {
  font-weight: var(--citrine-paletterow-label-weight, var(--citrine-font-weight-650));
  color: var(--citrine-paletterow-label-ink, var(--citrine-ink));
}

.citrine-paletterow-hint {
  font-size: var(--citrine-paletterow-hint-size, var(--citrine-font-size-11));
  color: var(--citrine-paletterow-hint-ink, var(--citrine-ink-3));
  margin-top: var(--citrine-paletterow-hint-gap, var(--citrine-space-1));
}

.citrine-paletterow-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--citrine-paletterow-add-radius, var(--citrine-r-xs));
  border: var(--citrine-paletterow-line-width, 1px) solid
    var(--citrine-paletterow-add-line, var(--citrine-line-2));
  background: var(--citrine-paletterow-add-bg, var(--citrine-surface));
  color: var(--citrine-paletterow-add-ink, var(--citrine-ink-3));
  cursor: pointer;
  flex: none;
}

.citrine-paletterow-add:hover {
  background: var(--citrine-paletterow-hover-bg, var(--citrine-data-wash));
  border-color: var(--citrine-paletterow-hover-line, var(--citrine-data-line));
  color: var(--citrine-paletterow-add-hover-ink, var(--citrine-data));
}

.citrine-paletterow-add svg {
  width: 14px;
  height: 14px;
}

/* ── PhotoSlot ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — PhotoSlot styles.
 *
 * Structure only. Every colour, radius, spacing step and type size is seeded inline from
 * `theme.useToken()` in `PhotoSlot.tsx` (the OptionCardGroup pattern), so the squares draw from
 * antd's own scale and follow the palette's accent rather than antd's derivation of it.
 *
 * The shape is the comp's: squares that fill the width they are given, no narrower than 96px, a
 * dashed edge while empty, and the words under the picture — never on it. */

.citrine-photoslots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--citrine-photoslots-min, 96px), 1fr));
  gap: var(--citrine-photoslots-gap, 8px);
}

/* `scroll`: one row that slides, for squares that are there to be looked through rather than filled
 * in. The columns are laid along the inline axis at a FIXED width — `min` stops being a floor and
 * becomes the size, because a row with no wrap has no leftover width to share out. `auto-flow:
 * column` keeps the source order, so a caller that has already sorted its photographs by box gets
 * them in that order along the line.
 *
 * `overscroll-behavior-inline: contain` keeps a sideways swipe inside the row: without it, reaching
 * the end of the photographs hands the gesture to the page and, in a browser with swipe navigation,
 * to the back button. Snapping is `proximity`, not `mandatory` — a partner dragging past the last
 * square should be able to stop where they like rather than be pulled onto a tile. */
.citrine-photoslots-is-scroll {
  grid-auto-flow: column;
  grid-auto-columns: var(--citrine-photoslots-min, 96px);
  grid-template-columns: none;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x proximity;
  /* The row is its own scroll container, so the squares must not be squashed to fit the card. */
  padding-block-end: var(--citrine-photoslots-gap, 8px);
}

.citrine-photoslots-is-scroll > * {
  scroll-snap-align: start;
}

.citrine-photoslot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 96px;
  padding: var(--citrine-photoslot-pad, 8px);
  border: 1px dashed var(--citrine-photoslot-line);
  border-radius: var(--citrine-photoslot-radius, 8px);
  background: var(--citrine-photoslot-bg);
  color: var(--citrine-photoslot-ink);
  font: inherit;
  font-size: var(--citrine-photoslot-size, 12px);
  line-height: 1.35;
  text-align: center;
  cursor: pointer;
}

.citrine-photoslot:not(:disabled):hover {
  border-color: var(--citrine-photoslot-hover);
}

/* The kit draws its own ring on a control it draws itself: 2px of the action colour, 2px out. */
.citrine-photoslot:focus-visible {
  outline: 2px solid var(--citrine-photoslot-on);
  outline-offset: 2px;
}

/* The slot the picker is labelled for. A solid edge and a ring, so "chosen" is not only a tint. */
.citrine-photoslot-is-on {
  border-style: solid;
  border-color: var(--citrine-photoslot-on);
  background: var(--citrine-photoslot-on-bg);
  box-shadow: 0 0 0 1px var(--citrine-photoslot-on);
}

.citrine-photoslot:disabled {
  background: var(--citrine-photoslot-disabled-bg);
  color: var(--citrine-photoslot-disabled-ink);
  cursor: not-allowed;
}

/* ── the face: the glyph while empty, the photograph once there is one ─────────────────────────── */

.citrine-photoslot-face {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
}

.citrine-photoslot-is-filled {
  justify-content: flex-start;
  border-style: solid;
  border-color: var(--citrine-photoslot-line-solid);
}

.citrine-photoslot-is-filled .citrine-photoslot-face {
  position: relative;
  flex: 1 1 auto;
  align-self: stretch;
  min-height: 48px;
  overflow: hidden;
  border-radius: calc(var(--citrine-photoslot-radius, 8px) - 2px);
}

.citrine-photoslot-cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* `fit="contain"`: the whole photograph in the same face, scaled down to fit. `object-position` is
 * centred by default. The face carries the fill, so the frame shows before the picture loads. */
.citrine-photoslot-is-contain .citrine-photoslot-face {
  background: var(--citrine-photoslot-mat);
}

.citrine-photoslot-is-contain .citrine-photoslot-cover {
  object-fit: contain;
}

/* `size="lg"`: the face is a true square as wide as the slot, so the slot grows with its grid
 * column and its size comes from the grid's `min`. `flex: none` replaces the filled face's
 * `flex: 1 1 auto`, so the square keeps its shape when a neighbour in the row is taller. The words
 * stay under it; the top alignment keeps a row of squares level when one label wraps. */
.citrine-photoslot-is-lg {
  justify-content: flex-start;
}

.citrine-photoslot-is-lg .citrine-photoslot-face {
  flex: none;
  align-self: stretch;
  aspect-ratio: 1 / 1;
}

/* The one mark that sits on the photograph, in the accent's own ink — never a word. */
.citrine-photoslot-badge {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--citrine-photoslot-on);
  color: var(--citrine-photoslot-on-ink);
}

.citrine-photoslot-label {
  overflow-wrap: anywhere;
}

.citrine-photoslot-count,
.citrine-photoslot-busy {
  font-variant-numeric: tabular-nums;
}

/* ── PiiTag ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — PiiTag. Rebuilt on antd `Tag` (Group B wave 3): background, border and text colour now
   come from antd's own `color="error"` status styling (`tag/style/statusCmp.js`), not a Citrine
   brand token. What's left here is PiiTag's own typography — smaller and bolder than antd's
   default Tag text — which has no antd prop equivalent. */
.citrine-pii {
  font-size: var(--citrine-font-size-9p5);
  letter-spacing: 0.08em;
  font-weight: var(--citrine-font-weight-800);
}

/* ── Popover ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — Popover styles.

   The panel itself renders in a portal and is styled through antd's `styles` prop in Popover.tsx —
   the same route Menu, Tooltip and Modal take. What is left here is the content this kit renders
   inside it, which sits on our own `citrine-popover-*` classes and is reachable normally. */

.citrine-popover-title {
  display: block;
  font-size: var(--citrine-font-size-13);
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-ink);
}

.citrine-popover-body {
  color: var(--citrine-ink-2);
  font-size: var(--citrine-font-size-13);
  line-height: var(--citrine-line-height-1p25);
}

/* ── Radio ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Radio family styles.

   RULING D14 (Group A audit, finding F-A5, 2026-09-10): the dot skin is gone.
   `.citrine-radio-dotring` used to be 18×18 with a `1.5px var(--citrine-line-2)` ring over a
   `var(--citrine-surface)` fill, and `-is-on` drew its own 9px `::after` dot in `var(--citrine-data)`
   — the GREEN — while the checkbox beside it in the same form drew navy. That inconsistency is what
   F-A5 was really about. antd draws both controls at 16px from ONE `colorPrimary`, so stripping the
   pair fixes the split as well as the divergence from stock.

   WHERE THOSE RULES ACTUALLY LANDED — the reason they had to go rather than be re-pointed. antd
   routes `classNames.icon` to the inner control's own `className` (`antd/es/radio/radio.js:105`), so
   `.citrine-radio-dotring` sat on the OUTER `.ant-radio` span, while the ring a user sees is
   `.ant-radio-inner` and its dot is that element's own `::after`. Citrine's ring and dot were
   therefore an EXTRA pair drawn around antd's, held over them by the `display: grid; place-items:
   center` above. `Radio.test.tsx` pins that landing point by render.

   Stripped in the same commit as `Checkbox.css` — see that file's header for why the two never move
   apart.

   What is left below is Citrine's OWN markup and its own feature — the `columns` grid on
   `RadioGroup`, which antd has no equivalent of. */

.citrine-radio-group {
  display: grid;
  gap: var(--citrine-sp-2) var(--citrine-sp-4);
  grid-template-columns: minmax(0, 1fr);
}

.citrine-radio-group-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.citrine-radio-group-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* ── RailToggle ────────────────────────────────────────────────────────────────────────── */
/* UI kit — RailToggle. A folded panel as a narrow strip with its name written down it. */

.citrine-railtoggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
  gap: 8px;
  inline-size: 36px;
  min-block-size: 100%;
  padding: 6px 0 0;
  border: 0;
  border-inline-end: 1px solid var(--citrine-line);
  background: transparent;
  color: var(--citrine-accent);
  font: inherit;
  cursor: pointer;
}
.citrine-railtoggle:hover {
  background: var(--citrine-surface-3);
}
.citrine-railtoggle:focus-visible {
  outline: 2px solid var(--citrine-focus-ring);
  outline-offset: -2px;
}

.citrine-railtoggle-icon {
  display: inline-flex;
  font-size: 16px;
}

.citrine-railtoggle-label {
  writing-mode: vertical-rl;
  font-size: 12px;
  color: var(--citrine-ink-3);
}

.citrine-railtoggle-chev {
  font-size: 12px;
}

.citrine-railtoggle-count {
  min-inline-size: 18px;
  padding: 0 4px;
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-accent);
  color: var(--citrine-accent-ink);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}

/* ── RecordLine ────────────────────────────────────────────────────────────────────────── */
/* UI kit — RecordLine. Tile + bold name + quiet sub line; `indent` steps it in by tree level. */

.citrine-recordline {
  display: flex;
  align-items: center;
  gap: 10px;
  min-inline-size: 0;
}

.citrine-recordline-tile {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 8px;
  background: var(--citrine-hue-wash, var(--citrine-accent-wash));
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
  font-size: 12px;
  font-weight: 700;
}

.citrine-recordline-text {
  display: flex;
  flex-direction: column;
  min-inline-size: 0;
}

.citrine-recordline-title {
  font-weight: 650;
  color: var(--citrine-ink);
}

.citrine-recordline-sub {
  font-size: 11.5px;
  color: var(--citrine-ink-3);
}
.citrine-recordline-sub-mono {
  font-family: var(--citrine-font-family-mono);
}

/* Tree levels, 20px a step, capped at 8. */
.citrine-recordline-indent-1 {
  padding-inline-start: 20px;
}
.citrine-recordline-indent-2 {
  padding-inline-start: 40px;
}
.citrine-recordline-indent-3 {
  padding-inline-start: 60px;
}
.citrine-recordline-indent-4 {
  padding-inline-start: 80px;
}
.citrine-recordline-indent-5 {
  padding-inline-start: 100px;
}
.citrine-recordline-indent-6 {
  padding-inline-start: 120px;
}
.citrine-recordline-indent-7 {
  padding-inline-start: 140px;
}
.citrine-recordline-indent-8 {
  padding-inline-start: 160px;
}

/* ── Result ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Result. antd's own `Result` draws the panel (`result/style/index.js`); this file only
   neutralises the title element, which `as` can make an `h1`–`h6`. A heading arrives with the
   browser's own margins and font size; inheriting keeps antd's title look whichever tag it is.
   Same treatment as `.citrine-empty-title`. */
.citrine-result-title {
  margin: 0;
  font: inherit;
  color: inherit;
}

/* ── ScrollBox ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — ScrollBox. Grows with its content up to a height from the token scale, then scrolls
   inside itself. Draws no surface of its own: it sits inside the card or dialog that holds it. */
.citrine-scrollbox {
  overflow-y: auto;
  overscroll-behavior: contain;
}

.citrine-scrollbox-sm {
  max-block-size: var(--citrine-scrollbox-sm);
}

.citrine-scrollbox-md {
  max-block-size: var(--citrine-scrollbox-md);
}

.citrine-scrollbox-lg {
  max-block-size: var(--citrine-scrollbox-lg);
}

.citrine-scrollbox:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-2);
}

/* ── ScrollX ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — ScrollX. A sideways-scrolling frame whose scrollbar is pinned to the window's bottom.

   The pinned bar is `position: sticky; bottom`, so its containing block is the frame: it is stuck
   only while the frame is on screen, and parks right under the content once the frame's own bottom
   comes into view. Like every other sticky thing in the kit (see AppShell.css), it anchors to the
   page's scroll — nothing between the frame and `.citrine-app-main` may set `overflow` to anything
   but `visible`. */

.citrine-scrollx {
  position: relative;
  min-inline-size: 0;
}

.citrine-scrollx-view {
  overflow-x: auto;
  overflow-y: hidden;
}

/* The frame's own scrollbar goes while the pinned one is up — one scrollbar, never two. */
.citrine-scrollx-pinned > .citrine-scrollx-view {
  scrollbar-width: none;
}
.citrine-scrollx-pinned > .citrine-scrollx-view::-webkit-scrollbar {
  display: none;
}

/* The content at its own natural width, never narrower than the frame — so a fixed-width row of
   lanes sets the scroll width, and a short one still fills the frame. */
.citrine-scrollx-track {
  inline-size: max-content;
  min-inline-size: 100%;
}

.citrine-scrollx-bar {
  position: sticky;
  bottom: var(--citrine-scrollx-bottom, 0px);
  z-index: var(--citrine-z-sticky);
  overflow-x: auto;
  overflow-y: hidden;
  /* Content scrolling under the pinned bar is covered by the page's own ground — the AppShell
     canvas inside a shell, the page colour outside one. */
  background: var(--citrine-app-canvas, var(--citrine-page));
}

/* 1px tall: the bar's height is the browser's own scrollbar plus this. */
.citrine-scrollx-spacer {
  block-size: 1px;
}

/* ── SearchBox ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — SearchBox styles.

   Group A audit (RULING D11, 2026-09-10): this file used to add
   `border-radius: var(--citrine-r-pill)`. Verified by render that the class lands on antd's own
   root — `<span class="ant-input-affix-wrapper … citrine-searchbox">` — so that declaration was a
   brand pill radius painted straight over antd's `borderRadius`, the same species as the 999px
   radius D10 removed from `Segmented`. It is gone; the control now takes antd's own radius.

   What stays is layout, not skin: the width floor and its ceiling. */
.citrine-searchbox {
  /* The floor yields to the container rather than overflowing it. A 240px minimum is comfortable on
     a phone with page gutters, but this control is often dropped into a toolbar beside other things
     — and there a hard minimum pushes its siblings off the edge instead of shrinking.

     `min()` in the floor itself, because a `min-width` beats a `max-width` when they disagree: the
     old `min-width: 240px` + `max-width: 100%` pair still drew 240px in a narrower box. Measured in a
     240px StickyPane rail, whose held scrollbar gutter leaves 225px: the field ran 15px past the edge
     and the rail's sideways clip cut its right border off. */
  min-width: min(240px, 100%);
  max-width: 100%;
}

/* `width="readable"`: a search heading a full-width page stops at a comfortable reading width. */
.citrine-searchbox.citrine-searchbox--readable {
  max-width: min(100%, 36rem);
}

/* ── SearchCombobox ────────────────────────────────────────────────────────────────────── */
/* UI kit — SearchCombobox. A search field with a grouped results panel; a full-screen sheet on a
   phone with `mobile="sheet"`. The field and the panel are antd's (`AutoComplete`); this file draws
   only what the part adds: the panel's rows and heads, the loading bar, and the sheet's layout. */

.citrine-searchcb {
  display: flex;
  flex: 1;
  align-items: center;
  min-inline-size: 0;
}

/* The field takes the width it is given and yields to a narrow container rather than pushing its
   neighbours off the edge. */
.citrine-searchcb-field {
  flex: 1;
  min-inline-size: 0;
  max-inline-size: 100%;
}

/* The shortcut hint in the empty field. */
.citrine-searchcb-kbd {
  padding: 0 var(--citrine-space-4);
  border: var(--citrine-space-1) solid var(--citrine-line);
  border-radius: var(--citrine-r-xs);
  color: var(--citrine-ink-3);
  font-family: inherit;
  font-size: var(--citrine-font-size-11);
  white-space: nowrap;
}

/* ── The panel ─────────────────────────────────────────────────────────────────────────────── */

.citrine-searchcb-panel {
  position: relative;
  max-block-size: var(--citrine-searchcombobox-panel-max);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* status="loading": a thin bar sweeps across the top; the old results stay under it. */
.citrine-searchcb-progress {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--citrine-z-raised);
  block-size: var(--citrine-searchcombobox-progress-height);
  overflow: hidden;
  background: linear-gradient(
      90deg,
      transparent 0 30%,
      var(--citrine-searchcombobox-progress-color) 30% 70%,
      transparent 70% 100%
    )
    0 0 / 200% 100%;
  animation: citrine-searchcb-sweep var(--citrine-searchcombobox-progress-duration) linear infinite;
}
@keyframes citrine-searchcb-sweep {
  from {
    background-position: 100% 0;
  }
  to {
    background-position: -100% 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .citrine-searchcb-progress {
    animation: none;
    background-position: 50% 0;
  }
}

.citrine-searchcb-message {
  padding: var(--citrine-space-12) var(--citrine-space-12) var(--citrine-space-8);
  color: var(--citrine-ink-2);
  font-size: var(--citrine-font-size-13);
}

.citrine-searchcb-notice,
.citrine-searchcb-tips {
  padding: var(--citrine-space-8) var(--citrine-space-12);
  border-block-start: var(--citrine-space-1) solid var(--citrine-line);
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-12);
}

/* A group's head: its label and count. Muted, small, never a target. */
.citrine-searchcb-head {
  color: var(--citrine-ink-3);
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-600);
}

/* Every row holds the 44px target. Scoped through the popup and the panel to outrank antd's own
   32px row (`.ant-select-dropdown .ant-select-item`), which antd injects after this stylesheet. */
.citrine-searchcb-popup .citrine-searchcb-panel .citrine-searchcb-row,
.citrine-searchcb-popup .citrine-searchcb-panel .citrine-searchcb-link {
  min-block-size: var(--citrine-target-min);
  align-items: center;
}

/* A result row's title is set in the regular weight so the matched words (`Strong`, 700) stand out
   by weight alone: a bold run inside a bold title does not read as a match. */
.citrine-searchcb-title {
  font-weight: var(--citrine-font-weight-400);
}
.citrine-searchcb-line {
  display: block;
  white-space: normal;
}
.citrine-searchcb-best {
  display: inline-block;
  margin-inline-start: var(--citrine-space-6);
  padding: 0 var(--citrine-space-4);
  border: var(--citrine-space-1) solid currentColor;
  border-radius: var(--citrine-r-xs);
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-700);
  vertical-align: middle;
}

/* "See all", "Clear recent searches", the status action: a row that reads as a link. */
.citrine-searchcb-linktext {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-space-4);
  color: var(--citrine-accent-deep);
  font-weight: var(--citrine-font-weight-600);
}
.citrine-searchcb-more {
  display: inline-flex;
}

/* ── The phone sheet (`mobile="sheet"`) ────────────────────────────────────────────────────── */

.citrine-searchcb-sheet-head {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  padding: var(--citrine-sp-2) var(--citrine-sp-3);
  border-block-end: var(--citrine-space-1) solid var(--citrine-line);
}
.citrine-searchcb-sheet-field {
  display: flex;
  flex: 1;
  min-inline-size: 0;
}
.citrine-searchcb-sheet-field .citrine-searchcb-field {
  min-inline-size: 0;
}
/* The search button and the sheet's back button hold the 44px target. `IconButton` is antd's 32px
   circle, sized by antd's own two-class rules, so these are scoped one step deeper to outrank them. */
.citrine-searchcb .citrine-searchcb-trigger,
.citrine-searchcb-sheet-head .citrine-searchcb-back {
  flex: none;
  inline-size: var(--citrine-target-min);
  min-inline-size: var(--citrine-target-min);
  block-size: var(--citrine-target-min);
}

/* In the sheet the panel is not a dropdown: it is the rest of the screen, under the field. antd
   positions its popup with inline `left`/`top`; a static box ignores them. */
.citrine-searchcb-popup.citrine-searchcb-popup-sheet {
  position: static;
  inline-size: auto;
  min-inline-size: 0;
  padding: 0;
  box-shadow: none;
  border-radius: 0;
}
.citrine-searchcb-popup-sheet .citrine-searchcb-panel {
  max-block-size: none;
  overflow: visible;
}

/* ── Section ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — Section. Group B wave 7a.
 *
 * Almost everything that used to be here is now drawn by antd's `Collapse`: the box, its border and
 * radius, the header band, the header padding, the disclosure arrow and its rotation, and the body
 * padding. `--citrine-line`, `--citrine-surface`, `--citrine-surface-3`, `--citrine-r-sm`,
 * `--citrine-ink-3`, `--citrine-ink-4`, `--citrine-sp-*`, `--citrine-font-size-*`,
 * `--citrine-duration-150` and `--citrine-easing-standard` have all left this file.
 *
 * Two things antd does not draw are kept, both seeded from `theme.useToken()` in `Section.tsx`:
 * the gap between stacked sections, and the muted `meta` hint.
 *
 * `.citrine-section-head`, `.citrine-section-title` and `.citrine-section-body` are still applied —
 * `.citrine-section-head` is load-bearing, because `Section.tsx` finds its OWN header through
 * `querySelectorAll('.citrine-section-head')` to restore `aria-controls` and the Space key (antd
 * exposes no prop that puts an attribute on its header element) — but they carry no styling of
 * their own now. Until `SectionGroup` was deleted this selector also drove that component's
 * roving arrow-key focus; it is no less load-bearing without it.
 * `.citrine-section-chev` and `.citrine-is-open` are GONE: antd renders and rotates the arrow. */

.citrine-section + .citrine-section {
  margin-top: var(--citrine-section-gap);
}

.citrine-section-meta {
  color: var(--citrine-section-meta-color);
  font-size: var(--citrine-section-meta-size);
}

/* ── spine ─────────────────────────────────────────────────────────────────────────────────────
   A full-height leading border down the whole section, header and body alike, open or closed. It
   is drawn OVER antd's own box (whose outline and surface stay as they are) by a pseudo-element on
   this component's root, so it never reaches into antd's classes: as wide as the box's corner
   radius, rounded on its leading corners exactly as the box is, and painted only in its first
   `--citrine-section-spine-width` — so the strip follows the box's curve rather than poking out of
   it. The radius is antd's `borderRadiusLG`, which `Section.tsx` hands over as
   `--citrine-section-radius`. Each section owns its own spine, so stacked sections draw one each. */
.citrine-section-spine {
  position: relative;
}

.citrine-section-spine::before {
  --citrine-section-spine-ink: var(--citrine-section-spine);
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 1;
  width: max(var(--citrine-section-radius, var(--citrine-r-xs)), var(--citrine-section-spine-width));
  border-start-start-radius: var(--citrine-section-radius, var(--citrine-r-xs));
  border-end-start-radius: var(--citrine-section-radius, var(--citrine-r-xs));
  background: linear-gradient(
    to right,
    var(--citrine-section-spine-ink) var(--citrine-section-spine-width),
    transparent var(--citrine-section-spine-width)
  );
  pointer-events: none;
}

/* ── flush ─────────────────────────────────────────────────────────────────────────────────────
   Several unboxed sections inside one card: no gap between them, a hairline rule instead. antd's
   `ghost` already removed the box and the header band; `Section.tsx` zeroes the side padding. */
.citrine-section-flush + .citrine-section-flush {
  margin-top: 0;
  border-top: 1px solid var(--citrine-section-rule);
}

/* The step-number badge. Its wash and ink are the scope's accent, which a hued page remaps to its
   own hue (HueScope.css) — so the badge wears the section colour with no per-hue rule here. */
.citrine-section-num {
  display: inline-grid;
  place-items: center;
  inline-size: 22px;
  block-size: 22px;
  margin-inline-end: 10px;
  border-radius: 6px;
  background: var(--citrine-accent-wash);
  color: var(--citrine-accent-deep);
  font-size: 12px;
  font-weight: 700;
  vertical-align: middle;
}

/* With a hue — its own, or the nearest HueScope's — the spine is the hue's MID. */
.citrine-section-spine.citrine-hued::before {
  --citrine-section-spine-ink: var(--citrine-hue);
}

/* ── Segmented ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — Segmented styles. The control's look is antd's own: RULING D10 removed the
   component-scoped ConfigProvider theme that used to repaint the track, the items and the radius
   in Citrine's brand palette, so `Segmented` and `ButtonGroup` — the same antd primitive — now
   draw the same thing. No `.ant-*` selector is written here or anywhere in this component.

   The one rule below is the kit-wide typeface rule, not a skin: antd's `fontFamily` token is
   already `var(--citrine-font-family-sans, …)` (`adapters/antd/theme.ts`), and `--citrine-sans` is
   that same token under its short alias (`packages/tokens/src/generated/citrine.css:214`). It is
   restated on the root node the `className` prop reaches so the control cannot inherit a different
   face from an ancestor. `TextInput.css:18` carries the identical rule for the same reason. */
.citrine-seg {
  font-family: var(--citrine-sans);
}

/* WRAP. antd offers `block` and `vertical` and neither serves: `block` divides ONE line into
   ever-narrower cells, and `vertical` is a different control. Measured on the live build at 390px,
   a database-driven option list pushed the page 196px sideways — `.ant-segmented` scrollWidth 570
   inside a 390 viewport — and the screen cannot shorten a list it does not own.

   The rule goes on the GROUP, not the root. antd renders `.ant-segmented > .ant-segmented-group`
   and the group is the flex row; `flex-wrap` on the root would be inert — a rule that reads
   correctly and does nothing. Verified against the live DOM before it was written.

   `height: auto` releases the track, which antd sizes to a single row from `controlHeight`; without
   it a second line is drawn outside the groove. */
.citrine-seg-wrap {
  height: auto;
}

.citrine-seg-wrap .ant-segmented-group {
  flex-wrap: wrap;
}

/* ── Select ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Select styles. Source: artifact `.ctrl.ctrl-sel`.
   `.citrine-ctrl` base (including `.citrine-ctrl.citrine-is-error`) lives in `TextInput/TextInput.css`.

   The reference draws its custom caret with a background-image gradient on a native `<select>`.
   Ant Design's Select renders its own caret through a private internal node
   (`.ant-select-arrow`), which the `.ant-*` rule forbids us from targeting — so the caret colour
   here comes from `adapters/antd/theme.ts` (ConfigProvider tokens), not from this file. `.citrine-ctrl-sel` is
   kept as the styling hook for whatever IS reachable on our own root className. */
.citrine-ctrl-sel {
  cursor: pointer;
}

/* `width="fixed"`: one width whatever is picked, so a picker in a bar does not jump as its label
   changes. Never wider than the space it has; antd already ends a long picked label in "…".
   In a full flex row (a phone's top bar) it gives way below 16rem, but never grows: `flex: none`
   held it at 16rem there and ran the bar past the screen's edge. */
.citrine-select-fixed {
  flex: 0 1 16rem;
  width: 16rem;
  min-width: 0;
  max-width: 100%;
}

.citrine-select-fixed .citrine-ctrl-sel {
  width: 100%;
}

.citrine-select-searching {
  display: block;
  margin-top: var(--citrine-sp-1);
  color: var(--citrine-ink-4);
  font-size: var(--citrine-font-size-11p5);
}

/* ── Showcase ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Showcase. The signed-out page: scenes that take turns on the left, the form card on the right.

   The structure and every measure are a consumer's approved mockups, carried over as they were drawn.
   Every colour, face and duration is a `--citrine-showcase-*` token (neutral by default; the consumer
   sets its own). The few `--citrine-showcase-*` names read WITH a fallback are the component's own
   hooks, set inline by `Showcase.tsx` per band, per chip, per board, never by the tokens package.

   The page answers to its OWN width (`container`), so the mockups' `cqw` and container queries hold
   wherever it is mounted: under 860px the picture goes and the headline alone carries the scene.

   Reduced motion: every animation and transition below is switched off at the end of this file, and
   each scene is drawn in its finished state. */

/* The root is the size container (a container query cannot size the container itself, so the grid is
   the page inside it). */
.citrine-showcase {
  container-type: inline-size;
  min-width: 0;
  font-family: var(--citrine-showcase-font-body);
  /* The mockups' body measure, so the page reads the same whatever the host document sets. */
  font-size: 15px;
  line-height: 1.5;
  color: var(--citrine-showcase-ink);
}

.citrine-showcase-page {
  /* The page's margin, which is also where the logo is pinned. */
  --citrine-showcase-pad: clamp(24px, 4cqw, 56px);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 400px);
  gap: 48px;
  align-items: center;
  min-height: var(--citrine-showcase-min-height);
  min-width: 0;
  padding: var(--citrine-showcase-pad);
  /* Room at the top for the pinned logo, so the centred columns never run under it. */
  padding-top: calc(var(--citrine-showcase-pad) + var(--citrine-showcase-logo-height) + 22px);
  background: var(--citrine-showcase-canvas);
  overflow: hidden;
}

/* The two soft lights behind the board scene: the run's end colour top-right, its start bottom-left. */
.citrine-showcase--board .citrine-showcase-page {
  background:
    radial-gradient(
      700px 380px at 100% 0,
      color-mix(in srgb, var(--citrine-showcase-end) 14%, transparent),
      transparent 60%
    ),
    radial-gradient(
      700px 420px at 0 100%,
      color-mix(in srgb, var(--citrine-showcase-start) 12%, transparent),
      transparent 60%
    ),
    var(--citrine-showcase-canvas);
}

.citrine-showcase--orbit .citrine-showcase-page {
  grid-template-columns: minmax(0, 1fr) minmax(0, 400px);
  gap: 40px;
  background:
    radial-gradient(
      520px 520px at 28% 50%,
      color-mix(in srgb, var(--citrine-showcase-start) 12%, transparent),
      transparent 65%
    ),
    var(--citrine-showcase-canvas);
}

.citrine-showcase--trend .citrine-showcase-page {
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 400px);
  background:
    radial-gradient(
      700px 400px at 0 0,
      color-mix(in srgb, var(--citrine-showcase-start) 8%, transparent),
      transparent 60%
    ),
    radial-gradient(
      600px 380px at 100% 100%,
      color-mix(in srgb, var(--citrine-showcase-end) 10%, transparent),
      transparent 60%
    ),
    var(--citrine-showcase-canvas-trend);
}

.citrine-showcase--funnel .citrine-showcase-page {
  --citrine-showcase-pad: clamp(20px, 5cqw, 72px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 400px);
}

.citrine-showcase h1,
.citrine-showcase h3,
.citrine-showcase p {
  margin: 0;
}

/* ── the left column: logo, eyebrow, headline, picture, caption ───────────────────────────────── */

.citrine-showcase-left {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-width: 0;
  animation: citrine-showcase-enter var(--citrine-showcase-fade) var(--citrine-showcase-easing) both;
}

/* The orbit and the funnel are centred in their column: their pictures are narrower than the
   board and the figures, and a wide page left them huddled at its edge. */
.citrine-showcase--orbit .citrine-showcase-left,
.citrine-showcase--funnel .citrine-showcase-left {
  align-items: center;
  text-align: center;
}

.citrine-showcase--orbit .citrine-showcase-left {
  gap: 14px;
}

/* With a headline and facts above and below it, the orbit is drawn a size smaller than the
   mockup's 460px, so the whole column fits a laptop window. */
.citrine-showcase--orbit .citrine-showcase-sys {
  width: min(100%, 400px);
}

.citrine-showcase--orbit .citrine-showcase-eyebrow,
.citrine-showcase--funnel .citrine-showcase-eyebrow {
  align-self: center;
}

@keyframes citrine-showcase-enter {
  from {
    opacity: 0;
    transform: translateY(var(--citrine-showcase-rise));
  }
}

/* Pinned to the page's top-left corner, in the margin reserved above the columns. */
.citrine-showcase-logo {
  position: absolute;
  top: var(--citrine-showcase-pad);
  left: var(--citrine-showcase-pad);
  z-index: 1;
  display: inline-flex;
  align-items: center;
}

.citrine-showcase-logo-img {
  display: block;
  height: var(--citrine-showcase-logo-height);
  width: auto;
  max-width: 100%;
}

.citrine-showcase-eyebrow {
  align-self: flex-start;
  display: inline-flex;
  gap: 8px;
  align-items: center;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  color: var(--citrine-showcase-start);
  background: var(--citrine-showcase-wash);
  border-radius: 999px;
  padding: 6px 12px;
}

.citrine-showcase-eyebrow::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--citrine-showcase-end);
  animation: citrine-showcase-ping var(--citrine-showcase-eyebrow-ping) ease-out infinite;
}

@keyframes citrine-showcase-ping {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--citrine-showcase-end) 70%, transparent);
  }
  100% {
    box-shadow: 0 0 0 8px color-mix(in srgb, var(--citrine-showcase-end) 0%, transparent);
  }
}

.citrine-showcase-title {
  font-family: var(--citrine-showcase-font-display);
  font-weight: 800;
  font-size: var(--citrine-showcase-title-size);
  line-height: 1.04;
  letter-spacing: -0.03em;
  max-width: 13ch;
  text-wrap: balance;
}

.citrine-showcase--funnel .citrine-showcase-title {
  font-size: var(--citrine-showcase-title-size-funnel);
  line-height: 1.05;
  letter-spacing: -0.025em;
  max-width: 15ch;
}

.citrine-showcase-keyword {
  background: linear-gradient(90deg, var(--citrine-showcase-start), var(--citrine-showcase-end));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.citrine-showcase-sample {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.65;
}

.citrine-showcase-cap {
  color: var(--citrine-showcase-ink-2);
  font-size: 13px;
}

/* ── the card on the right: the form, which never moves ───────────────────────────────────────── */

.citrine-showcase-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
  padding: var(--citrine-showcase-card-pad);
  border: 1.5px solid transparent;
  border-radius: var(--citrine-showcase-card-radius);
  /* Two layers: the surface inside, the run as the edge. */
  background:
    linear-gradient(var(--citrine-showcase-surface), var(--citrine-showcase-surface)) padding-box,
    linear-gradient(135deg, var(--citrine-showcase-start), var(--citrine-showcase-end)) border-box;
  box-shadow: var(--citrine-showcase-card-shadow);
}

.citrine-showcase-bar {
  height: 4px;
  border-radius: 4px;
  background: linear-gradient(
    90deg,
    var(--citrine-showcase-start),
    var(--citrine-showcase-end),
    var(--citrine-showcase-start)
  );
  background-size: 200% 100%;
  animation: citrine-showcase-slide var(--citrine-showcase-bar-slide) linear infinite;
}

@keyframes citrine-showcase-slide {
  to {
    background-position: -200% 0;
  }
}

/* ── Board ────────────────────────────────────────────────────────────────────────────────────── */

.citrine-showcase-board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--citrine-showcase-board-cols, 4), minmax(0, 1fr));
  padding: 12px 0;
  border: 1.5px solid transparent;
  border-radius: var(--citrine-showcase-board-radius);
  background:
    linear-gradient(var(--citrine-showcase-surface), var(--citrine-showcase-surface)) padding-box,
    linear-gradient(135deg, var(--citrine-showcase-start), var(--citrine-showcase-end)) border-box;
}

.citrine-showcase-col {
  padding: 0 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-left: 1px dashed var(--citrine-showcase-line);
  min-height: var(--citrine-showcase-board-min-height);
  min-width: 0;
}

.citrine-showcase-col:first-child {
  border-left: 0;
}

.citrine-showcase-col h3 {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--citrine-showcase-ink-2);
  font-weight: 800;
  padding: 0 2px 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The last column is where the card ends up: it reads in the run's end colour. */
.citrine-showcase-col:last-child h3 {
  color: var(--citrine-showcase-won-ink);
}

.citrine-showcase-ghost {
  height: 44px;
  border-radius: 8px;
  background: var(--citrine-showcase-surface-2);
}

/* The card: as wide as one column, moved by whole columns. `--citrine-showcase-mover-step` is the
   column it is in (set inline); a change slides it there. */
.citrine-showcase-mover {
  position: absolute;
  left: 0;
  top: 44px;
  width: calc(100% / var(--citrine-showcase-board-cols, 4));
  padding: 0 8px;
  transform: translateX(calc(var(--citrine-showcase-mover-step, 0) * 100%));
  transition: transform var(--citrine-showcase-board-move) var(--citrine-showcase-board-easing);
}

/* Back to the first column in one jump, not a slide across every column. */
.citrine-showcase-mover--jump {
  transition: none;
}

.citrine-showcase-mcard {
  background: linear-gradient(135deg, var(--citrine-showcase-start), var(--citrine-showcase-start-2));
  color: var(--citrine-showcase-on-solid);
  border-radius: 10px;
  padding: 10px;
  font-size: 12px;
  box-shadow: 0 14px 28px -12px color-mix(in srgb, var(--citrine-showcase-start) 70%, transparent);
  transition:
    background var(--citrine-showcase-board-move) var(--citrine-showcase-board-easing),
    box-shadow var(--citrine-showcase-board-move) var(--citrine-showcase-board-easing);
}

.citrine-showcase-mcard b {
  display: block;
  font-size: 12.5px;
  overflow-wrap: anywhere;
}

.citrine-showcase-mcard span {
  opacity: 0.85;
}

/* In the last column the card takes the run's far colours. */
.citrine-showcase-mcard--won {
  background: linear-gradient(135deg, var(--citrine-showcase-mid), var(--citrine-showcase-end-deep));
  box-shadow: 0 14px 28px -12px color-mix(in srgb, var(--citrine-showcase-end-deep) 70%, transparent);
}

/* ── Orbit ────────────────────────────────────────────────────────────────────────────────────── */

.citrine-showcase-sys {
  position: relative;
  width: min(100%, 460px);
  aspect-ratio: 1;
  justify-self: center;
  align-self: center;
  display: grid;
  place-items: center;
}

/* A soft turning glow behind the mark, in the two colours of the run. */
.citrine-showcase-sys::before {
  content: '';
  position: absolute;
  inset: 34%;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    var(--citrine-showcase-start),
    var(--citrine-showcase-end),
    var(--citrine-showcase-start)
  );
  filter: blur(30px);
  opacity: 0.35;
  /* Still: a turning blur repainted every frame flickered under the mark. */
}

.citrine-showcase-ring {
  position: absolute;
  border-radius: 50%;
}

.citrine-showcase-ring--outer {
  inset: 6%;
  border: 2px solid transparent;
  background:
    linear-gradient(var(--citrine-showcase-canvas), var(--citrine-showcase-canvas)) padding-box,
    conic-gradient(
        var(--citrine-showcase-start),
        var(--citrine-showcase-end),
        color-mix(in srgb, var(--citrine-showcase-start) 15%, transparent),
        var(--citrine-showcase-start)
      )
      border-box;
  animation: citrine-showcase-spin var(--citrine-showcase-orbit-spin) linear infinite;
}

.citrine-showcase-ring--inner {
  inset: 26%;
  border: 1.5px dashed var(--citrine-showcase-ring);
  animation: citrine-showcase-spin var(--citrine-showcase-orbit-spin-2) linear infinite reverse;
}

@keyframes citrine-showcase-spin {
  to {
    transform: rotate(360deg);
  }
}

/* A chip is a zero-width spoke turned to its angle; its face sits at the spoke's end and turns back
   the other way at the ring's own speed, so the words stay upright while the ring goes round. */
.citrine-showcase-chip {
  position: absolute;
  left: 50%;
  top: 0;
  height: 100%;
  width: 0;
  transform: rotate(var(--citrine-showcase-chip-angle, 0deg));
}

.citrine-showcase-chip-face {
  position: absolute;
  top: -15px;
  left: 0;
  transform: translateX(-50%) rotate(calc(-1 * var(--citrine-showcase-chip-angle, 0deg)));
  white-space: nowrap;
  font-size: 12px;
  font-weight: 800;
  background: var(--citrine-showcase-surface);
  border: 1px solid var(--citrine-showcase-line);
  border-radius: 999px;
  padding: 6px 12px;
  box-shadow: var(--citrine-showcase-chip-shadow);
  animation: citrine-showcase-counter var(--citrine-showcase-orbit-spin) linear infinite;
}

.citrine-showcase-ring--inner .citrine-showcase-chip-face {
  animation: citrine-showcase-counter-back var(--citrine-showcase-orbit-spin-2) linear infinite;
}

@keyframes citrine-showcase-counter {
  from {
    transform: translateX(-50%) rotate(calc(-1 * var(--citrine-showcase-chip-angle, 0deg)));
  }
  to {
    transform: translateX(-50%) rotate(calc(-1 * var(--citrine-showcase-chip-angle, 0deg) - 360deg));
  }
}

@keyframes citrine-showcase-counter-back {
  from {
    transform: translateX(-50%) rotate(calc(-1 * var(--citrine-showcase-chip-angle, 0deg)));
  }
  to {
    transform: translateX(-50%) rotate(calc(-1 * var(--citrine-showcase-chip-angle, 0deg) + 360deg));
  }
}

/* The dot: the consumer's colour when given (`--citrine-showcase-dot`, inline), else the chip's own
   hue (`HueScope` classes), else the start of the run. */
.citrine-showcase-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
  background: var(--citrine-showcase-dot, var(--citrine-hue, var(--citrine-showcase-start)));
}

.citrine-showcase-core {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}

/* The mark on a white tile, at one fixed size, hovering: it drifts up and settles, never scales. */
.citrine-showcase-core-mark {
  display: block;
  box-sizing: border-box;
  width: 88px;
  height: 88px;
  padding: 12px;
  object-fit: contain;
  border-radius: 24px;
  background: var(--citrine-showcase-surface);
  box-shadow: 0 20px 40px -14px color-mix(in srgb, var(--citrine-showcase-start) 70%, transparent);
  animation: citrine-showcase-hover var(--citrine-showcase-orbit-beat) ease-in-out infinite;
}

@keyframes citrine-showcase-hover {
  50% {
    transform: translateY(-6px);
  }
}

.citrine-showcase-core b {
  font-family: var(--citrine-showcase-font-display);
  font-size: 20px;
  font-weight: 800;
  background: linear-gradient(90deg, var(--citrine-showcase-start), var(--citrine-showcase-end-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.citrine-showcase-core span {
  font-size: 12.5px;
  color: var(--citrine-showcase-ink-2);
}

/* ── Trend ────────────────────────────────────────────────────────────────────────────────────── */

.citrine-showcase-kpis {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.citrine-showcase-kpi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px;
  border: 1.5px solid transparent;
  border-radius: var(--citrine-showcase-tile-radius);
  background:
    linear-gradient(var(--citrine-showcase-surface), var(--citrine-showcase-surface)) padding-box,
    linear-gradient(
        135deg,
        color-mix(in srgb, var(--citrine-showcase-start) 35%, transparent),
        color-mix(in srgb, var(--citrine-showcase-end) 35%, transparent)
      )
      border-box;
}

.citrine-showcase-kpi span {
  font-size: 12px;
  color: var(--citrine-showcase-ink-2);
  font-weight: 700;
}

.citrine-showcase-kpi b {
  font-family: var(--citrine-showcase-font-display);
  font-size: var(--citrine-showcase-figure-size);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* The last figure is the one that matters most: it reads in the run. */
.citrine-showcase-kpi:last-child b {
  background: linear-gradient(90deg, var(--citrine-showcase-start), var(--citrine-showcase-end-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.citrine-showcase-kpi em {
  font-style: normal;
  font-size: 12px;
  font-weight: 800;
  color: var(--citrine-showcase-won-ink);
}

.citrine-showcase-chart {
  background: var(--citrine-showcase-canvas);
  border-radius: var(--citrine-showcase-tile-radius);
  padding: 14px 16px;
}

.citrine-showcase-chart svg {
  display: block;
  width: 100%;
  height: 120px;
}

.citrine-showcase-stop-start {
  stop-color: var(--citrine-showcase-start);
}

.citrine-showcase-stop-end {
  stop-color: var(--citrine-showcase-end);
}

/* `pathLength="1"` on the path makes one dash the whole line, whatever its real length. */
.citrine-showcase-line {
  fill: none;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: citrine-showcase-draw var(--citrine-showcase-trend-draw) 0.3s cubic-bezier(0.4, 0, 0.2, 1)
    forwards;
}

.citrine-showcase-area {
  opacity: 0;
  animation: citrine-showcase-fadein 1s 1.6s forwards;
}

.citrine-showcase-line-end {
  fill: var(--citrine-showcase-end);
  stroke: var(--citrine-showcase-surface);
  stroke-width: 3;
  transform-origin: center;
  transform-box: fill-box;
  transform: scale(0);
  animation: citrine-showcase-pop 0.4s 2.3s forwards;
}

@keyframes citrine-showcase-draw {
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes citrine-showcase-fadein {
  to {
    opacity: 0.9;
  }
}

@keyframes citrine-showcase-pop {
  to {
    transform: scale(1);
  }
}

/* ── Funnel ───────────────────────────────────────────────────────────────────────────────────── */

.citrine-showcase-funnel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: 520px;
}

/* A band's width, lean and colour are set inline per band. Its colour is the consumer's `fill` when
   given, else a point on the run from start (the top band) to end-deep (the bottom one). The light:
   each band starts at the dim opacity and is lit in turn, top to bottom, one `--citrine-showcase-
   funnel-step` apart; the cycle is one step per band, so the light comes round again as soon as the
   last band has had its turn. */
.citrine-showcase-band {
  height: var(--citrine-showcase-funnel-band);
  width: var(--citrine-showcase-band-w, 100%);
  clip-path: polygon(
    0 0,
    100% 0,
    calc(100% - var(--citrine-showcase-band-in, 0%)) 100%,
    var(--citrine-showcase-band-in, 0%) 100%
  );
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--citrine-showcase-on-solid);
  font-size: 14px;
  font-weight: 700;
  background: var(
    --citrine-showcase-band-fill,
    color-mix(
      in oklab,
      var(--citrine-showcase-start),
      var(--citrine-showcase-end-deep) calc(var(--citrine-showcase-band-mix, 0) * 1%)
    )
  );
  opacity: var(--citrine-showcase-funnel-dim);
  animation: citrine-showcase-lit
    calc(var(--citrine-showcase-funnel-step) * var(--citrine-showcase-funnel-count, 4))
    var(--citrine-showcase-easing) infinite;
  animation-delay: calc(var(--citrine-showcase-funnel-step) * var(--citrine-showcase-band-i, 0));
}

.citrine-showcase-band span {
  font-variant-numeric: tabular-nums;
  font-weight: 800;
}

@keyframes citrine-showcase-lit {
  0%,
  28%,
  100% {
    opacity: var(--citrine-showcase-funnel-dim);
    transform: scale(1);
  }
  8%,
  18% {
    opacity: 1;
    transform: scale(1.03);
  }
}

/* ── Tiles: a headline, a quiet line, a row of still tiles ────────────────────────────────────── */
/* One block, left-aligned inside itself, so the headline, the line and the row share an edge however
   the column places it. A tile is the launcher's paper sheet (its wash, its spine, its shadow, read
   from the launcher's own tokens so the two stay one family), but still and not a link. Inside a
   tile's hue scope the accent roles ARE the hue (HueScope.css), so the wash, ink and spine below take
   the tile's hue, and the accent where none is given. */

.citrine-showcase-hero {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-space-14);
  width: 100%;
  max-width: var(--citrine-showcase-tiles-max);
  min-width: 0;
}

.citrine-showcase-hero .citrine-showcase-title {
  font-size: var(--citrine-showcase-title-size-funnel);
  font-weight: var(--citrine-font-weight-700);
  line-height: var(--citrine-showcase-tiles-title-leading);
  letter-spacing: var(--citrine-showcase-tiles-title-tracking);
  max-width: none;
}

/* The headline in a hue's deep ink (`hue`). */
.citrine-showcase-title--hued {
  color: var(--citrine-accent-deep);
}

/* Here a keyword is the accent alone, not the run: one colour. */
.citrine-showcase-hero .citrine-showcase-keyword {
  background: none;
  color: var(--citrine-showcase-start);
}

.citrine-showcase-lede {
  color: var(--citrine-showcase-ink-2);
  max-width: 44ch;
}

.citrine-showcase-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--citrine-sp-3);
  margin: var(--citrine-space-14) 0 0;
  padding: 0;
  list-style: none;
}

.citrine-showcase-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--citrine-space-14) var(--citrine-space-14) var(--citrine-space-12)
    calc(var(--citrine-space-12) + var(--citrine-launcher-spine-width));
  border-radius: var(--citrine-launcher-radius);
  background: var(--citrine-accent-wash);
  color: var(--citrine-accent-deep);
  box-shadow: var(--citrine-launcher-shadow);
}

/* The spine down the leading edge. */
.citrine-showcase-tile::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--citrine-launcher-spine-width);
  border-radius: var(--citrine-launcher-radius) 0 0 var(--citrine-launcher-radius);
  background: var(--citrine-hue, var(--citrine-accent));
}

.citrine-showcase-tile-icon {
  display: inline-flex;
  font-size: var(--citrine-font-size-18);
  line-height: var(--citrine-line-height-1);
}

.citrine-showcase-tile b {
  margin-top: var(--citrine-space-6);
  font-weight: var(--citrine-font-weight-700);
  font-size: var(--citrine-font-size-14);
  overflow-wrap: anywhere;
}

.citrine-showcase-tile-line {
  font-size: var(--citrine-font-size-12p5);
  overflow-wrap: anywhere;
}

/* ── facts: a row of small tiles under a picture, where a wide page had nothing to say ───────── */

.citrine-showcase-facts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}

.citrine-showcase-fact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 120px;
  padding: 10px 16px;
  border: 1px solid var(--citrine-showcase-line);
  border-radius: var(--citrine-showcase-tile-radius);
  background: var(--citrine-showcase-surface);
}

.citrine-showcase-fact b {
  font-family: var(--citrine-showcase-font-display);
  font-size: 20px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  background: linear-gradient(90deg, var(--citrine-showcase-start), var(--citrine-showcase-end-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.citrine-showcase-fact span {
  font-size: 12px;
  font-weight: 700;
  color: var(--citrine-showcase-ink-2);
}

/* ── steady: the same page for every scene ────────────────────────────────────────────────────── */
/* Opt-in (`steady`). Each scene above sets its own margin, columns, gap and alignment, and the page
   has only a least height, so a turn moves the logo, the card and the heading. Here every scene gets
   one value of each, and the page is exactly one window tall. The soft light behind each scene is
   left alone: a colour change is not a move. */

@container (width > 860px) {
  /* The page keeps only its margin at the top. The room for the pinned logo is kept by the scene
     column alone (below), so the card has the whole height between the two margins: on a laptop
     window the form fits without scrolling. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-page {
    --citrine-showcase-pad: var(--citrine-showcase-steady-pad);
    /* The side margin: the page margin, or on a window wider than the two columns may span, half of
       what is left over, so the columns sit together in the middle and no hollow opens between the
       scene and the card. The logo is pinned at the same edge (below). */
    --citrine-showcase-steady-inline: max(
      var(--citrine-showcase-pad),
      (100cqw - var(--citrine-showcase-steady-max-width)) / 2
    );
    box-sizing: border-box;
    grid-template-columns: var(--citrine-showcase-steady-columns);
    gap: var(--citrine-showcase-steady-gap);
    height: var(--citrine-showcase-steady-height);
    min-height: 0;
    padding-top: var(--citrine-showcase-pad);
    padding-inline: var(--citrine-showcase-steady-inline);
  }

  /* The logo keeps to the scene column's edge, whatever the window's width. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-logo {
    left: var(--citrine-showcase-steady-inline);
  }

  /* The scene column fills the page below the logo, centres its lines there and clips what does not
     fit, so a tall scene never pushes the card. `safe` keeps the headline: when a scene is taller
     than the column, the overflow is cut at the bottom (the picture), never at the top. The lines
     stretch across the column, as they do without `steady`: aligned to the start, the board, the
     figures and the trend line shrank to their content and left a hollow before the card. The
     headline keeps its measure and the eyebrow and the facts their start, so the left edge holds. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-left {
    box-sizing: border-box;
    align-self: stretch;
    align-items: stretch;
    justify-content: safe center;
    text-align: left;
    gap: var(--citrine-showcase-steady-left-gap);
    min-height: 0;
    padding-top: calc(var(--citrine-showcase-logo-height) + var(--citrine-showcase-steady-logo-gap));
    overflow: hidden;
  }

  .citrine-showcase.citrine-showcase--steady .citrine-showcase-eyebrow {
    align-self: flex-start;
  }

  .citrine-showcase.citrine-showcase--steady .citrine-showcase-facts {
    justify-content: flex-start;
  }

  .citrine-showcase.citrine-showcase--steady .citrine-showcase-title {
    font-size: var(--citrine-showcase-title-size);
    line-height: 1.04;
    letter-spacing: -0.03em;
    max-width: 13ch;
  }

  .citrine-showcase.citrine-showcase--steady .citrine-showcase-sys {
    width: var(--citrine-showcase-steady-orbit-size);
  }

  /* The funnel spans the scene column like the board and the figures, so it reaches the card's gap. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-funnel {
    max-width: none;
  }

  /* The funnel and the trend line shrink on a short window, like the orbit, so the column fits. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-band {
    height: min(var(--citrine-showcase-funnel-band), var(--citrine-showcase-steady-band));
  }

  .citrine-showcase.citrine-showcase--steady .citrine-showcase-chart svg {
    height: var(--citrine-showcase-steady-chart-height);
  }

  /* The card keeps its place. On a window too short for the form it scrolls inside itself. */
  .citrine-showcase.citrine-showcase--steady .citrine-showcase-card {
    max-height: 100%;
    min-height: 0;
    overflow-y: auto;
  }
}

/* ── pane: the scenes on the surface, the form on a full-height pane of the canvas ─────────────── */
/* Opt-in (`frame="pane"`). The page is two equal halves with no margin and no gap; each half centres
   what it holds. The card loses its edge, its bar and its corners and becomes the pane: the canvas
   from the top of the page to the bottom. Written after the steady rules, at their weight, so the
   pane keeps its own columns and margins when both are set. */

.citrine-showcase.citrine-showcase--pane .citrine-showcase-page {
  /* The halves run edge to edge, so with `steady` too the logo stays at the page margin. */
  --citrine-showcase-steady-inline: var(--citrine-showcase-pad);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0;
  align-items: stretch;
  padding: 0;
  background: var(--citrine-showcase-surface);
}

.citrine-showcase.citrine-showcase--pane .citrine-showcase-left {
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  padding: var(--citrine-showcase-pane-pad);
}

.citrine-showcase.citrine-showcase--pane .citrine-showcase-card {
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  padding: var(--citrine-showcase-pane-form-pad);
  border: 0;
  border-radius: 0;
  background: var(--citrine-showcase-canvas);
  box-shadow: none;
}

.citrine-showcase.citrine-showcase--pane .citrine-showcase-card > * {
  width: 100%;
  max-width: var(--citrine-showcase-pane-form-width);
}

.citrine-showcase.citrine-showcase--pane .citrine-showcase-bar {
  display: none;
}

/* ── narrow: the logo and the form, nothing else ──────────────────────────────────────────────── */

@container (max-width: 860px) {
  .citrine-showcase-page,
  .citrine-showcase--orbit .citrine-showcase-page,
  .citrine-showcase--trend .citrine-showcase-page,
  .citrine-showcase--funnel .citrine-showcase-page {
    --citrine-showcase-pad: 18px;
    grid-template-columns: 1fr;
    align-content: start;
    gap: 24px;
  }

  /* The scenes are not drawn at all on a phone: the logo stays pinned and the form follows. */
  .citrine-showcase-left {
    display: none;
  }

  .citrine-showcase-card {
    padding: var(--citrine-showcase-card-pad-narrow);
  }

  /* A narrow pane is the whole page: the canvas from top to bottom, the form centred on it. */
  .citrine-showcase.citrine-showcase--pane .citrine-showcase-page {
    grid-template-columns: minmax(0, 1fr);
    align-content: stretch;
    background: var(--citrine-showcase-canvas);
  }

  .citrine-showcase.citrine-showcase--pane .citrine-showcase-card {
    padding: var(--citrine-sp-6) var(--citrine-sp-4);
  }
}

/* ── reduced motion: every scene in its finished state, and no turns ──────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .citrine-showcase-left,
  .citrine-showcase-eyebrow::before,
  .citrine-showcase-bar,
  .citrine-showcase-ring--outer,
  .citrine-showcase-ring--inner,
  .citrine-showcase-chip-face,
  .citrine-showcase-ring--inner .citrine-showcase-chip-face,
  .citrine-showcase-core-mark,
  .citrine-showcase-band {
    animation: none;
  }

  .citrine-showcase-mover,
  .citrine-showcase-mcard {
    transition: none;
  }

  .citrine-showcase-band {
    opacity: 1;
  }

  .citrine-showcase-line {
    animation: none;
    stroke-dashoffset: 0;
  }

  .citrine-showcase-area {
    animation: none;
    opacity: 0.9;
  }

  .citrine-showcase-line-end {
    animation: none;
    transform: none;
  }
}

/* ── SidePanes ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — SidePanes. A fixed side pane and a fluid main pane, divided by a hairline. */

.citrine-sidepanes {
  display: grid;
  grid-template-columns: var(--citrine-sidepanes-side, 300px) minmax(0, 1fr);
}

.citrine-sidepanes-side {
  padding: 16px 20px;
  border-inline-end: 1px solid var(--citrine-line);
  min-inline-size: 0;
}

.citrine-sidepanes-main {
  padding: 16px 20px;
  min-inline-size: 0;
}

.citrine-sidepanes-main-center {
  display: grid;
  place-items: center;
}

.citrine-sidepanes-main.citrine-sidepanes-main-flush {
  padding: 0;
}

/* `sidePosition="end"`: main first, the side pane after it with its hairline on its leading edge. */
.citrine-sidepanes.citrine-sidepanes-end {
  grid-template-columns: minmax(0, 1fr) var(--citrine-sidepanes-side, 300px);
}
.citrine-sidepanes-end > .citrine-sidepanes-side {
  border-inline-end: 0;
  border-inline-start: 1px solid var(--citrine-line);
}

/* `blockSize`: a fixed-height frame. The side pane scrolls inside it; the main pane keeps the full
   height (its content can fill it with `height: 100%`). `min-block-size: 0` lets the grid row
   shrink to the frame instead of growing to the taller pane. */
.citrine-sidepanes.citrine-sidepanes-fixed {
  grid-template-rows: minmax(0, 1fr);
}
.citrine-sidepanes-fixed > .citrine-sidepanes-side {
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.citrine-sidepanes-fixed > .citrine-sidepanes-main {
  min-block-size: 0;
}

/* A narrow column: the panes stack, the hairline between them. */
@media (max-width: 900px) {
  .citrine-sidepanes,
  .citrine-sidepanes.citrine-sidepanes-end {
    grid-template-columns: minmax(0, 1fr);
  }
  .citrine-sidepanes-side {
    border-inline-end: 0;
    border-block-end: 1px solid var(--citrine-line);
  }
  .citrine-sidepanes-end > .citrine-sidepanes-side {
    border-inline-start: 0;
    border-block-end: 0;
    border-block-start: 1px solid var(--citrine-line);
  }
}

/* ── Sidebar ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — Sidebar. Source: artifact `.sidebar` block (citrine-reference.css). Tokens only — the
   rail's own inks below are mixes of the four rail tokens, not new colours. */

/* The OUTER element owns the footprint — the space the rail takes in the page's flex row — and the
   INNER one owns the look. They are usually the same width; they come apart in hover mode, where the
   footprint stays at 64px while the inner layer opens over the content. */
.citrine-sidebar {
  width: var(--citrine-sidebar-width);
  flex: 0 0 var(--citrine-sidebar-width);
  position: sticky;
  top: 0;
  height: 100vh;
  /* ── THE RAIL'S OWN INKS (2.0.0-rc.17) ────────────────────────────────────────────────────────
     The rail is a navy surface in BOTH themes, so nothing drawn on it may borrow the PAGE's colours —
     not `--citrine-ink`, not `--citrine-surface`, not antd's `colorPrimary`. That borrowing was the
     single biggest defect measured on a consuming app's live rail (2026-09-11): the current row
     took the page's action navy as its fill, 1.19:1 against the navy rail in light, and its words
     fell to 3.23:1 in dark; the hero took `--citrine-surface`, which in dark is a dark slab at
     1.1:1 on the rail. Everything below is a mix of the four rail tokens, so a palette that
     re-seeds the rail re-seeds these with it. Measured with a navy rail: rows 7.1:1, current fill
     1.53:1 with its words at 9.9:1, focus ring 8.4:1. */
  /* ── THE MIXES ARE TOKENS NOW (2026-09-23) ───────────────────────────────────────────────────
     Each of these reads a declared rail role — `--citrine-side-row`, `-hover`, `-current`, `-ring`
     in the tokens package — whose DEFAULT is the mix described above. A palette that supplies its
     own (`sideRow`, `sideHover`, `sideCurrent`, `focusRing`) paints over it; one that does not
     draws exactly the mix. A light rail needs its own; the mixes were tuned on a navy one. */
  --citrine-sidebar-row-ink: var(--citrine-side-row);
  --citrine-sidebar-hover: var(--citrine-side-hover);
  --citrine-sidebar-current: var(--citrine-side-current);
  --citrine-sidebar-ring: var(--citrine-side-ring);
  /* The rail's quiet words — group headings, the brand's sub line, the collapse glyph, the footer's
     role — read `--citrine-side-head` (default: `side-ink`). The rules inside the rail and the
     footer's border stay `side-2`, as they always drew; `side-edge` is the rail's OUTER edge. */
  --citrine-sidebar-head-ink: var(--citrine-side-head);
  --citrine-sidebar-rule: var(--citrine-side-2);
  --citrine-sidebar-thumb: color-mix(in srgb, var(--citrine-side-ink) 30%, var(--citrine-side));
  --citrine-sidebar-ease: cubic-bezier(0.16, 1, 0.3, 1);
  /* The width of a top-level row's icon seat: the bare glyph, or the tile under
     `iconFrame="tile"`. A parent group's child band lines its guide and its rows' words up on it. */
  --citrine-sidebar-lead: var(--citrine-sidebar-glyph-size, 16px);
  /* Width is the only thing that animates. Collapsing is a change of SIZE, and animating anything
     else (opacity on every label, a slide on every icon) turns one movement into a dozen competing
     ones. Honoured by `prefers-reduced-motion` below. */
  transition:
    width 160ms ease,
    flex-basis 160ms ease;
}

.citrine-sidebar-inner {
  position: relative;
  width: 100%;
  height: 100%;
  background: var(--citrine-side);
  color: var(--citrine-side-ink);
  /* ── THE RAIL DRAWS ITS OWN EDGE. Do not delete this as redundant in light mode. ───────────────
     The rail used to have no edge at all: `--citrine-side` simply butted onto `--citrine-canvas`
     and the step between them WAS the boundary. On a near-black dark page that step is ~1.04:1 and
     the whole left-hand structure of the application disappears. `--citrine-line-2` is the line that
     clears WCAG 1.4.11's 3:1 against the surfaces either side of it in dark (3.30 on the canvas,
     3.44 on the rail); in light the rail/page step already carries the boundary and the line is a
     refinement.

     A `border` and not a `box-shadow`: `.citrine-sidebar-peek:not(.citrine-is-collapsed)
     .citrine-sidebar-inner` already sets `box-shadow` for the open overlay, and a shadow here would
     be REPLACED by it — the seam would vanish in exactly the mode that overlays the page. The width
     is safe because `base.css` makes every `citrine-*` element `border-box`, so the 1px comes out of
     the content box and the expanded (`--citrine-sidebar-width`) and 64px footprints are unchanged. */
  border-right: 1px solid var(--citrine-side-edge);
  display: flex;
  flex-direction: column;
  /* The fallback scroller. A rail composed with `SidebarBody` never reaches it — the body band is
     the only thing that scrolls and the top and the footer stay put — but a rail without one still
     scrolls as a whole rather than cutting off. */
  overflow-y: auto;
  overflow-x: hidden;
  transition: width 160ms ease;
}

/* ── Collapsed ────────────────────────────────────────────────────────────────────────────────
   64px is the icon plus its padding and nothing else. The rail keeps its structure at this width —
   brand mark, group rules, row icons, footer avatar — so it still reads as the same navigation
   rather than as a different, poorer one. */
.citrine-sidebar.citrine-is-collapsed {
  width: 64px;
  flex: 0 0 64px;
}

/* ── Hover mode ───────────────────────────────────────────────────────────────────────────────
   The footprint is PINNED at 64px whatever the inner layer is doing. This is the whole reason the
   inner element exists: if the rail grew in flow, every page would jump sideways each time the
   pointer crossed it, and content shifting under the cursor is worse than the gutter it saves. */
.citrine-sidebar-peek,
.citrine-sidebar-peek.citrine-is-collapsed {
  width: 64px;
  flex: 0 0 64px;
  /* THE Z-INDEX BELONGS HERE, ON THE RAIL — not only on the inner layer that does the overlapping.
     `position: sticky` ALWAYS creates a stacking context, even at `z-index: auto`, so a z-index on
     its child is ordered only WITHIN it. Raising the context itself is what actually lifts the open
     rail above the page chrome. +1 over `z-sticky` beats `Topbar`, which is later in `AppShell`'s DOM
     and would otherwise win a tie; still far below antd's overlays, so this can never cover a modal. */
  z-index: calc(var(--citrine-z-sticky) + 1);
}

.citrine-sidebar-peek .citrine-sidebar-inner {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 64px;
  /* ABOVE `z-sticky`, not at it: `Topbar` is sticky at `z-sticky` and later in the DOM, so an equal
     z-index would still lose and clip the rail's top strip. */
  z-index: calc(var(--citrine-z-sticky) + 1);
}

/* Open. The shadow is what makes it read as a layer ABOVE the page rather than as a rail that has
   grown — without it the overlap looks like a rendering fault. */
.citrine-sidebar-peek:not(.citrine-is-collapsed) .citrine-sidebar-inner {
  width: var(--citrine-sidebar-width);
  box-shadow: var(--citrine-sh-lg);
}

/* ── The brand ────────────────────────────────────────────────────────────────────────────────
   The mark starts at 24px — the same left edge as the group headings and the row icons below it. The
   rail used to have five left edges (12, 20, 24, 40, 68px), which is what made it look unaligned. */
.citrine-brand {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  padding: var(--citrine-sp-5) var(--citrine-sp-5) var(--citrine-sp-4) var(--citrine-sp-6);
}

/* `logo`: the wordmark over its caption, left-aligned in the brand's own padding. */
.citrine-brand.citrine-brand-logo {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.citrine-brand-wordmark img,
.citrine-brand-wordmark svg {
  display: block;
  height: 40px;
  width: auto;
  max-width: 150px;
}
.citrine-brand-logo .citrine-brand-sub {
  padding-inline-start: 2px;
  font-weight: 600;
  color: var(--citrine-side-ink-2);
}

.citrine-brand-mark {
  width: 32px;
  height: 32px;
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-accent);
  color: var(--citrine-accent-ink);
  display: grid;
  place-items: center;
  font-weight: var(--citrine-font-weight-800);
  font-size: var(--citrine-font-size-15);
  flex: none;
  /* A logo passed as the mark takes the box's rounded corners rather than overhanging them. */
  overflow: hidden;
}

/* An image or SVG mark fills the box exactly, so a consumer's logo needs no sizing or inline style of
   its own — a consumer's used to carry `style={{ display: 'block', borderRadius }}` for exactly
   this. */
/* `markFrame="plain"`: no accent fill, so a full-colour logo shows in its own colours. An image keeps
   its proportions inside the box instead of being stretched to a square. */
.citrine-brand-mark-plain {
  background: transparent;
  color: inherit;
}

.citrine-brand-mark-plain > img {
  object-fit: contain;
}

.citrine-brand-mark > img,
.citrine-brand-mark > svg {
  display: block;
  width: 100%;
  height: 100%;
}

.citrine-brand-text {
  min-width: 0;
}

.citrine-brand-name {
  color: var(--citrine-side-ink-2);
  font-weight: var(--citrine-font-weight-700);
  font-size: var(--citrine-font-size-15);
  letter-spacing: -0.01em;
}

.citrine-brand-sub {
  font-size: var(--citrine-font-size-11);
  color: var(--citrine-sidebar-head-ink);
  letter-spacing: 0.05em;
  /* One line. A workspace or organisation name wrapped onto two or three lines pushed the rail's
     navigation down and read as a paragraph; it ends in an ellipsis instead, with the full text as
     the line's title (SidebarBrand sets it when `sub` is text). */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Pushed to the trailing edge so the name can grow into whatever room is left rather than the chip
   floating in the middle of it. */
.citrine-brand-trailing {
  margin-left: auto;
  flex: none;
}

/* Collapsed there is only the mark, so it centres in the rail instead of sitting at a text indent
   that no longer has text after it. */
.citrine-is-collapsed .citrine-brand {
  justify-content: center;
  padding-inline: 0;
}

/* ── The collapse toggle ──────────────────────────────────────────────────────────────────────
   FIRST in the rail's DOM and tab order, and quiet: it is chrome for the navigation, not one of its
   destinations, and a control that looks like a nav row would be read as one.

   EXPANDED, IT SITS IN THE BRAND ROW, at its trailing edge and level with the 32px mark. It used to
   take a 42px row of its own above the brand — a row of chrome on a rail that measured 545px taller
   than a 900px window. The brand row keeps room for it (`.citrine-sidecollapse + .citrine-brand`).

   A GLYPH, not a labelled row. `aria-label` on the button already names it, so a visible "Collapse"
   caption is a second copy of the same word — and one that has to be hidden again the moment the
   rail narrows to 64px. */
.citrine-sidecollapse {
  position: absolute;
  top: calc(var(--citrine-sp-3) + 1px);
  right: var(--citrine-sp-3);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--citrine-r-sm);
  background: none;
  color: var(--citrine-sidebar-head-ink);
  cursor: pointer;
  flex: none;
}

.citrine-sidecollapse:hover {
  background: var(--citrine-side-hover);
  color: var(--citrine-side-ink-2);
}

.citrine-sidecollapse:focus-visible {
  outline: 2px solid var(--citrine-sidebar-ring);
  outline-offset: -2px;
}

.citrine-sidecollapse-icon {
  display: flex;
  flex: none;
  width: 18px;
  height: 18px;
}

/* The glyphs default to `1em`, which here would follow the button's inherited font size rather than
   the box it sits in. Any CSS rule beats those attributes — that is the icon package's own contract. */
.citrine-sidecollapse-icon svg {
  width: 100%;
  height: 100%;
}

/* The brand row that holds the toggle: 12px from the top, and room at the trailing edge for the 30px
   button plus an 8px gap. Adjacent-sibling, so a rail with no toggle keeps the brand's own padding.

   12px, not 16, UNDER it — the gap down to the hero. Measured on a consuming app's live rail at
   1440×900 (2026-09-11): 732px of groups in a 722px band, so the last heading sat under the fade.
   The hero's own 16px below it is the contract's and stays; this gap and the band's foot
   (`.citrine-sidebody`) gave the 12px back, and the hero now reads with the brand as the rail's
   fixed top. */
.citrine-sidecollapse + .citrine-brand {
  padding: var(--citrine-sp-3) calc(var(--citrine-sp-3) + 30px + var(--citrine-sp-2)) var(--citrine-sp-3)
    var(--citrine-sp-6);
}

/* Collapsed, the toggle goes back into the flow ABOVE the mark, centred in the 64px column. */
.citrine-is-collapsed .citrine-sidecollapse {
  position: static;
  margin: var(--citrine-sp-3) auto 0;
}

.citrine-is-collapsed .citrine-sidecollapse + .citrine-brand {
  padding: var(--citrine-sp-2) 0 var(--citrine-sp-4);
}

/* ── The hero call to action ──────────────────────────────────────────────────────────────────
   Inverted: this sits ON the dark rail, where the loudest available treatment is the rail's own
   lightest ink as a fill. It used to be `--citrine-surface` / `--citrine-ink` — the PAGE's pair —
   which in dark is a dark slab at 1.1:1 on the rail. The rail's pair is the same in both themes.

   16px of clear space under it before the first group; it used to sit 8px above the first heading
   while every other heading had 20px, so "Home" read as a caption for the button. */
.citrine-sidehero {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--citrine-sp-2);
  min-height: 40px;
  margin: 0 var(--citrine-sp-3) var(--citrine-sp-4);
  padding: var(--citrine-sp-2) var(--citrine-sp-3);
  border: 0;
  /* `--citrine-r-sm`: the radius the rail's other rounded rectangles use — the 32px mark and the 30px
     toggle. (`--citrine-r-md` has never existed; an earlier version of this rule asked for it and
     drew square corners for as long as it did.) */
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-side-hero);
  color: var(--citrine-side-hero-ink);
  font-family: inherit;
  font-size: var(--citrine-font-size-13p5);
  font-weight: var(--citrine-font-weight-700);
  line-height: var(--citrine-line-height-1p25);
  text-decoration: none;
  cursor: pointer;
  flex: none;
  transition: background-color var(--citrine-duration-140) var(--citrine-sidebar-ease);
}

.citrine-sidehero:hover {
  background: var(--citrine-side-hero-2);
}

/* A hero that wears a hue — a section's own action, in that section's hue — is that section's
   primary button: the ramp's button PAIR, and its derived hover. Never the ramp's deep. */
.citrine-sidehero.citrine-hued {
  background: var(--citrine-hue-rail-hero);
  color: var(--citrine-hue-rail-hero-ink);
}
.citrine-sidehero.citrine-hued:hover {
  background: var(--citrine-hue-rail-hero-2);
}

.citrine-sidehero:focus-visible {
  outline: 2px solid var(--citrine-sidebar-ring);
  outline-offset: 2px;
}

.citrine-sidehero[aria-disabled='true'],
.citrine-sidehero:disabled {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none;
}

.citrine-sidehero-icon {
  display: flex;
  flex: none;
  width: 17px;
  height: 17px;
}

.citrine-sidehero-icon svg {
  width: 100%;
  height: 100%;
}

/* Collapsed it becomes a 40px square — the width of a collapsed row — still the loudest thing on the
   rail, just no longer wearing its words. */
.citrine-is-collapsed .citrine-sidehero {
  width: 40px;
  height: 40px;
  min-height: 0;
  margin: 0 auto var(--citrine-sp-3);
  padding: 0;
}

.citrine-is-collapsed .citrine-sidehero-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The pre-`SidebarBody` gutter class. Kept for a consumer that still writes `<div class="citrine-nav">`
   by hand; `SidebarBody` supersedes it. */
.citrine-nav {
  padding: 0 var(--citrine-sp-3) var(--citrine-sp-5);
  flex: 1;
}

/* ── The scrolling body ───────────────────────────────────────────────────────────────────────
   The ONE scroller: the top (toggle, brand, hero) and the footer never move. The fade is a mask on
   whichever edge has more beyond it — `SidebarBody` sets the two attributes — so the band says there
   is more without drawing anything over the rows when there is not. `black` in a mask is alpha, not
   a colour. */
.citrine-sidebody {
  --citrine-sidebody-fade-top: 0px;
  --citrine-sidebody-fade-bottom: 0px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* 4px at its foot, not 12: the footer's own 14px and its rule already part the list from the
     signed-in user, and those 8px were most of what a consuming app's live rail needed to fit a
     1440×900 desk (see the brand row). */
  padding: 0 var(--citrine-sp-3) var(--citrine-sp-1);
  scrollbar-width: thin;
  scrollbar-color: var(--citrine-sidebar-thumb) transparent;
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    black var(--citrine-sidebody-fade-top),
    black calc(100% - var(--citrine-sidebody-fade-bottom)),
    transparent 100%
  );
}

/* COLLAPSED, THE BAND DRAWS NO SCROLLBAR, and still scrolls — wheel, touch, and focus bringing a row
   into view — with the edge fade saying there is more. A desk browser that draws classic scrollbars
   takes the bar's width out of the band: measured on a consuming app's live rail, 10px of a 63px
   band, which left every collapsed row a 5px content box. Each 16px glyph was clipped to a sliver and
   the rail read as an empty column, the tiles sat 5px off the column's centre, and the bar drew a
   second line down a 64px rail. The expanded band keeps its thin bar. */
.citrine-sidebar.citrine-is-collapsed .citrine-sidebody {
  scrollbar-width: none;
}

.citrine-sidebar.citrine-is-collapsed .citrine-sidebody::-webkit-scrollbar {
  display: none;
}

.citrine-sidebody[data-more-above] {
  --citrine-sidebody-fade-top: 24px;
}

.citrine-sidebody[data-more-below] {
  --citrine-sidebody-fade-bottom: 28px;
}

/* ── Groups ───────────────────────────────────────────────────────────────────────────────────── */
.citrine-navgroup + .citrine-navgroup {
  margin-top: var(--citrine-sp-4);
}

/* Folding headings carry their own padding, so the gap between two of them is half a flat one. */
.citrine-navgroup-foldable + .citrine-navgroup-foldable {
  margin-top: var(--citrine-sp-2);
}

/* The flat heading. 11px, not the 9.5px it was — below every readable floor, and flagged as such by
   the detector on all seven of a consuming app's headings. */
.citrine-navgroup-label {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  font-size: var(--citrine-font-size-11);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--citrine-sidebar-head-ink);
  font-weight: var(--citrine-font-weight-700);
  padding: 0 var(--citrine-sp-3) var(--citrine-sp-2);
}

.citrine-navgroup-icon {
  display: flex;
  flex: none;
  width: 12px;
  height: 12px;
}

/* ── A group that wears a hue ─────────────────────────────────────────────────────────────────
   The heading gains a DOT in the ramp's MID — the vivid tone, kept in dark, where the ramp does not
   redefine it — with a soft halo of itself. The heading's WORDS stay the rail's heading ink: the comp
   (`.rail-head`) colours the dot, not the text. The rows' icons wear the dot's tone too; the current
   row takes the ramp's deep and wash below.

   Every rule here reads the scoped `--citrine-hue-rail-*`, which HueScope.css points at the declared
   RAIL hue roles (`--citrine-side-h{n}-dot`, `-ink`, `-current`, `-current-line`, `-hero`, …), never
   at the page ramp. Their token defaults alias the rail's own inks, so a hued group under a palette
   with no ramps draws exactly what a plain group draws. Aliasing the PAGE accent instead put navy on
   a navy rail, 1.1:1 (measured); verify-contrast.mjs asserts the defaults on the neutral rail. */
.citrine-hued .citrine-navgroup-dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 4px;
  background: var(--citrine-hue-rail-dot);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--citrine-hue-rail-dot) 22%, transparent);
}

/* A heading ICON takes the dot's seat, so it wears the dot's tone — the same 3:1 floor on the rail that
   verify-contrast.mjs asserts for `side-h{n}-dot` is the non-text floor a glyph needs. 14px, a step up
   from the plain 12px glyph, so a line drawing carries the colour a solid dot did. */
.citrine-hued .citrine-navgroup-icon {
  width: 14px;
  height: 14px;
  color: var(--citrine-hue-rail-dot);
}

.citrine-navgroup-icon > svg {
  width: 100%;
  height: 100%;
}

/* A row's icon wears its HEADING's tone — the dot's — so a section reads as one colour from its head
   down through every row it holds. It used to take the ramp's deep, which at 16px on the light rail
   read as near-black beside a vivid heading glyph, and the rows looked unrelated to their section.
   Same token, same 3:1 non-text floor verify-contrast.mjs asserts for the heading glyph. The row's
   WORDS, and the current row's, stay the rail ink. */
.citrine-navgroup.citrine-hued .citrine-navlink-icon {
  color: var(--citrine-hue-rail-dot);
}

/* (0,6,0): over the plain current row's (0,4,0). The fill, the words and a hairline ring — the current
   row is still told by more than a shade, as the plain one is by its weight. */
.citrine-sidebar .citrine-navgroup.citrine-hued .citrine-navmenu .citrine-navlink.citrine-is-active {
  background: var(--citrine-hue-rail-current);
  color: var(--citrine-hue-rail-ink);
  box-shadow: inset 0 0 0 1px var(--citrine-hue-rail-current-line);
}

/* ── A row that wears its own hue (`NavLink hue`) ─────────────────────────────────────────────
   The row's `<li>` carries the hue class, so its scoped `--citrine-hue-rail-*` shadow a hued group's
   on that row alone: inside a group the rule above already draws the row's own hue (it reads the
   same three variables, resolved on the row), and these two give a STANDALONE row — or a row in an
   unhued group — the same treatment. (0,5,0) over the plain current row's (0,4,0). */
.citrine-navlink.citrine-hued .citrine-navlink-icon {
  color: var(--citrine-hue-rail-dot);
}

.citrine-sidebar .citrine-navmenu .citrine-navlink.citrine-hued.citrine-is-active {
  background: var(--citrine-hue-rail-current);
  color: var(--citrine-hue-rail-ink);
  box-shadow: inset 0 0 0 1px var(--citrine-hue-rail-current-line);
}

/* ── A parent row (`NavGroup variant="parent"`) ────────────────────────────────────────────────
   The heading drawn as a ROW: the height, shape, padding, type and inks of a `NavLink` row, with the
   chevron trailing. The 12px start padding is the rows' `inlineIndent` (see `NavMenu`), written here
   because this is Citrine's own button, not an antd row. */
.citrine-navgroup-parentrow {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  width: 100%;
  min-height: var(--citrine-sidebar-row-height, 36px);
  margin: 0;
  padding-block: var(--citrine-sp-2);
  padding-inline: var(--citrine-sp-3);
  border: 0;
  border-radius: var(--citrine-r-xs);
  background: transparent;
  color: var(--citrine-sidebar-row-ink);
  font-family: inherit;
  font-size: var(--citrine-sidebar-row-size, var(--citrine-font-size-14));
  font-weight: var(--citrine-font-weight-400);
  line-height: var(--citrine-line-height-1p25);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--citrine-duration-140) var(--citrine-sidebar-ease),
    color var(--citrine-duration-140) var(--citrine-sidebar-ease);
}

.citrine-navgroup-parentrow:hover {
  background: var(--citrine-sidebar-hover);
}

.citrine-navgroup-parentrow:focus-visible {
  outline: 2px solid var(--citrine-sidebar-ring);
  outline-offset: -2px;
}

/* Every word fits, as on a row: a long label wraps rather than being cut. */
.citrine-navgroup-parentrow .citrine-navgroup-text {
  overflow-wrap: anywhere;
}

.citrine-navgroup-parentrow[aria-expanded='false'] .citrine-navgroup-chevron {
  transform: rotate(-90deg);
}

/* "The page you are on is in here": the parent's words in the rail's lightest ink at the current
   row's weight — quieter than the lit child row, which keeps its fill. Told by weight as well as
   colour, never by a shade alone. */
.citrine-navgroup-parentrow.citrine-holds-current .citrine-navgroup-text {
  color: var(--citrine-side-ink-2);
  font-weight: var(--citrine-font-weight-600);
}

.citrine-navgroup.citrine-hued .citrine-navgroup-parentrow.citrine-holds-current .citrine-navgroup-text {
  color: var(--citrine-hue-rail-ink);
}

/* ── A hued parent's colour flows down to its children ────────────────────────────────────────
   Owner ruling: a parent menu's colour visibly reaches EVERY child row, not only the current one.
   While the group is OPEN its words wear the hue's rail ink (weight 600 stays the "holds the
   current page" signal above, never the colour alone). Every child row's words take the same rail
   ink, and a hovered child the hue's rail current (its wash); the current child keeps its lit
   treatment — wash, line ring and weight 600 — from the hued current rule, which also reads the
   rail ink. A child with its own `NavLink hue` resolves these variables on its own `<li>`, so it
   wears its own hue. With no ramps supplied the rail roles alias the rail's own inks (the rail's
   lightest ink; the plain current fill). verify-contrast and the fixture tests hold the rail ink at
   4.5:1 on the rail and on the wash. Unhued parents are untouched. */
.citrine-navgroup-parent.citrine-hued
  > .citrine-navgroup-parentrow[aria-expanded='true']
  .citrine-navgroup-text {
  color: var(--citrine-hue-rail-ink);
}

/* (0,5,0): over the plain row's (0,3,0) and the plain current row's (0,4,0). */
.citrine-sidebar .citrine-navgroup-parent.citrine-hued .citrine-navmenu .citrine-navlink {
  color: var(--citrine-hue-rail-ink);
}

/* (0,8,0): over the plain hover's (0,6,0). */
.citrine-sidebar
  .citrine-navgroup-parent.citrine-hued
  .citrine-navmenu
  .citrine-navlink:not(.citrine-is-active):not([aria-disabled='true']):hover {
  background: var(--citrine-hue-rail-current);
  color: var(--citrine-hue-rail-ink);
}

.citrine-navgroup-parent + .citrine-navgroup-parent {
  margin-top: var(--citrine-space-2);
}

/* The child band: indented so a child's words start under the parent's words (row indent + the
   lead + the row gap), with the guide centred under the parent's icon. The guide is the band's
   own edge, drawn as a pseudo-element so a hued group can paint it in its own colour. Without a
   lead the arithmetic falls back to the bare glyph. */
.citrine-navgroup-children {
  position: relative;
  margin-top: var(--citrine-space-2);
  margin-inline-start: calc(
    var(--citrine-sp-3) +
      (
        var(--citrine-sidebar-lead, var(--citrine-sidebar-glyph-size, 16px)) -
          var(--citrine-sidebar-guide-width)
      ) /
      2
  );
  padding-inline-start: calc(
    (
        var(--citrine-sidebar-lead, var(--citrine-sidebar-glyph-size, 16px)) +
          var(--citrine-sidebar-guide-width)
      ) /
      2
  );
}

.citrine-navgroup-children::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--citrine-sidebar-guide-width);
  background: var(--citrine-sidebar-guide);
  pointer-events: none;
}

/* A hued parent's guide is the hue's MID — the rail dot, the tone its icon wears — so the parent's
   colour runs down beside its children. The line is decorative (the indent and the button's
   `aria-expanded`/`aria-controls` carry the structure), so a vivid mid that falls under 3:1 on a
   light rail loses nothing a reader needs. With no ramps it is the rail's own ink. */
.citrine-navgroup-parent.citrine-hued .citrine-navgroup-children::before,
.citrine-navgroup-guided.citrine-hued > .citrine-navgroup-children::before {
  background: var(--citrine-hue-rail-dot);
}

/* ── A heading group with a guide (`NavGroup guide`) ──────────────────────────────────────────
   Its rows take the parent's band. The band centres its guide on `--citrine-sidebar-lead` after the
   heading's 12px start padding, so the lead here is the width of whatever marker the HEADING draws —
   never the rail's tile: a `headingSize="row"` glyph (16px), a hued caption glyph (14px), a plain
   caption glyph (12px), the hue dot (10px). With no marker the guide sits under the words' start.
   `NavGroup` names the marker in a `citrine-navgroup-mark-*` class, so these are plain SIZE rules:
   they never select on `.citrine-hued`, whose rules are held to reading the rail hue roles. */
.citrine-navgroup-guided {
  --citrine-sidebar-lead: 0px;
}

.citrine-navgroup-guided.citrine-navgroup-mark-dot {
  --citrine-sidebar-lead: 10px;
}

.citrine-navgroup-guided.citrine-navgroup-mark-plain {
  --citrine-sidebar-lead: 12px;
}

.citrine-navgroup-guided.citrine-navgroup-mark-tinted {
  --citrine-sidebar-lead: 14px;
}

.citrine-navgroup-guided.citrine-navgroup-mark-row {
  --citrine-sidebar-lead: var(--citrine-sidebar-glyph-size, 16px);
}

/* ── The current-row marker (`NavGroup guide="marker"`) ───────────────────────────────────────
   A 2px guide, and a 6px pill on it beside the current row, centred on the guide: the row's start
   sits `(lead + guide) / 2` right of the guide's start, so the pill's start is `-(lead + pill) / 2`.
   The pill is the rail's lightest ink, or the hue's mid on a hued group or row — the guide's own
   tone, so the guide reads as one line that thickens where you are. Decorative: the row's
   `aria-current` and its lit fill already say which row is current. The row lets it overhang. */
.citrine-navgroup-marked {
  --citrine-sidebar-guide-width: var(--citrine-space-2);
}

.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup-marked
  .citrine-navgroup-children
  .citrine-navmenu
  .citrine-navlink.citrine-is-active {
  overflow: visible;
}

.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup-marked
  .citrine-navgroup-children
  .citrine-navmenu
  .citrine-navlink.citrine-is-active::before {
  content: '';
  position: absolute;
  inset-block: var(--citrine-space-6);
  inset-inline-start: calc(
    -1 * (var(--citrine-sidebar-lead, var(--citrine-sidebar-glyph-size, 16px)) + var(--citrine-space-6)) / 2
  );
  width: var(--citrine-space-6);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-side-ink-2);
  pointer-events: none;
}

.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup-marked.citrine-hued
  .citrine-navgroup-children
  .citrine-navmenu
  .citrine-navlink.citrine-is-active::before,
.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup-marked
  .citrine-navgroup-children
  .citrine-navmenu
  .citrine-navlink.citrine-hued.citrine-is-active::before {
  background: var(--citrine-hue-rail-dot);
}

/* ── The perforated guide (`NavGroup guide="perforated"`) ─────────────────────────────────────
   The band of `guide="line"`, its guide a thin run of short dashes: a mask over the same line, so
   it keeps the line's tone — the rail's edge, or the hue's mid on a hued group — and draws no
   marker. The width is set on the group so the band's centring arithmetic reads it too. */
.citrine-navgroup-perforated {
  --citrine-sidebar-guide-width: var(--citrine-sidebar-guide-perforated-width);
}

.citrine-navgroup-perforated > .citrine-navgroup-children::before {
  mask-image: repeating-linear-gradient(
    to bottom,
    black 0 var(--citrine-sidebar-guide-dash),
    transparent var(--citrine-sidebar-guide-dash)
      calc(var(--citrine-sidebar-guide-dash) + var(--citrine-sidebar-guide-gap))
  );
}

/* Folded, the band holds nothing and draws nothing — no gap and no guide stub under a closed
   parent. */
.citrine-navgroup-children:not(:has(> .citrine-navmenu:not([hidden]))) {
  margin-top: 0;
}

/* Collapsed, a parent is any other group: a labelled rule over a column of icons. */
.citrine-is-collapsed .citrine-navgroup-children {
  margin: 0;
  padding: 0;
}

.citrine-is-collapsed .citrine-navgroup-children::before {
  content: none;
}

/* ── Tiles (`Sidebar iconFrame="tile"`) ───────────────────────────────────────────────────────
   A TOP-LEVEL row's icon — a standalone `NavLink` (its menu is marked `-top`) and a parent row —
   sits in a small rounded square. Unhued: the rail's own second surface under its lightest ink.
   Hued: the hue's rail current (its wash) under its rail ink (its deep). Child rows draw no tile. */
.citrine-sidebar-tiles {
  --citrine-sidebar-lead: var(--citrine-sidebar-tile-size);
}

.citrine-sidebar-tiles .citrine-navmenu-top .citrine-navlink-icon,
.citrine-sidebar-tiles .citrine-navgroup-parentrow .citrine-navlink-icon {
  display: grid;
  place-items: center;
  width: var(--citrine-sidebar-tile-size);
  height: var(--citrine-sidebar-tile-size);
  border-radius: var(--citrine-sidebar-tile-radius);
  background: var(--citrine-sidebar-tile-bg);
  color: var(--citrine-sidebar-tile-ink);
}

.citrine-sidebar-tiles .citrine-navmenu-top .citrine-navlink-icon svg,
.citrine-sidebar-tiles .citrine-navgroup-parentrow .citrine-navlink-icon svg {
  width: var(--citrine-sidebar-tile-glyph-size);
  height: var(--citrine-sidebar-tile-glyph-size);
}

.citrine-sidebar-tiles .citrine-navmenu-top .citrine-navlink.citrine-hued .citrine-navlink-icon,
.citrine-sidebar-tiles
  .citrine-navgroup-parent.citrine-hued
  > .citrine-navgroup-parentrow
  .citrine-navlink-icon {
  background: var(--citrine-hue-rail-current);
  color: var(--citrine-hue-rail-ink);
}

/* A tile is taller than the bare glyph, so a tiled row gives the difference back from its padding
   and keeps the row's height. */
.citrine-sidebar.citrine-sidebar-tiles .citrine-navmenu-top .citrine-navlink,
.citrine-sidebar-tiles .citrine-navgroup-parentrow {
  padding-block: var(--citrine-space-6);
}

/* ── The folding heading ──────────────────────────────────────────────────────────────────────
   A real button with `aria-expanded`, in the group's own words — sentence weight, not shouted: 12px
   semibold, no capitals, no tracking. The chevron points down while open and turns to point at the
   words while folded. */
.citrine-navgroup-fold {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  width: 100%;
  /* A REAL CONTROL, so it gets a control's height. Measured on the deployed app 2026-09-12: all
     eight of the app's fold headings drew **27px** — the shortest press target in the whole shell,
     on every screen, at every width, in both themes.

     36px, not 44: the approved rail comp draws its own rows in a 32–40px band,
     and a 44px heading would add ~136px to a rail that already
     has to scroll on a laptop. This is the comp's number, not a floor invented here — and the rows
     these headings sit over are 40px, so the heading stays quieter than the links beneath it. */
  min-height: 36px;
  margin: 0;
  padding: var(--citrine-space-6) var(--citrine-sp-3);
  border: 0;
  border-radius: var(--citrine-r-xs);
  background: none;
  color: var(--citrine-sidebar-head-ink);
  font-family: inherit;
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-600);
  line-height: var(--citrine-line-height-1p25);
  text-align: left;
  cursor: pointer;
  transition:
    color var(--citrine-duration-140) var(--citrine-sidebar-ease),
    background-color var(--citrine-duration-140) var(--citrine-sidebar-ease);
}

.citrine-navgroup-fold:hover {
  color: var(--citrine-side-ink-2);
  background: var(--citrine-sidebar-hover);
}

.citrine-navgroup-fold:focus-visible {
  outline: 2px solid var(--citrine-sidebar-ring);
  outline-offset: -2px;
}

/* `headingSize="row"`: a foldable heading at a NavLink's SIZE — words, glyph, icon gap and height —
   so a folding group's icon and words line up with the plain links beside it. Weight and ink stay
   the heading's. (0,3,0) on the glyph, over the hued heading glyph's 14px (0,2,0). */
.citrine-navgroup-fold.citrine-navgroup-fold-row {
  gap: var(--citrine-sp-3);
  min-height: var(--citrine-sidebar-row-height, 36px);
  font-size: var(--citrine-sidebar-row-size, var(--citrine-font-size-14));
}

.citrine-navgroup .citrine-navgroup-fold-row .citrine-navgroup-icon {
  width: var(--citrine-sidebar-glyph-size, 16px);
  height: var(--citrine-sidebar-glyph-size, 16px);
}

.citrine-navgroup-text {
  flex: 1;
  min-width: 0;
}

.citrine-navgroup-chevron {
  display: flex;
  flex: none;
  width: 14px;
  height: 14px;
  transition: transform var(--citrine-duration-140) var(--citrine-sidebar-ease);
}

.citrine-navgroup-chevron svg {
  width: 100%;
  height: 100%;
}

.citrine-navgroup-fold[aria-expanded='false'] .citrine-navgroup-chevron {
  transform: rotate(-90deg);
}

/* `chevronTurn="flip"`: down while folded, up while open. */
.citrine-navgroup-chev-flip
  > :is(.citrine-navgroup-fold, .citrine-navgroup-parentrow)[aria-expanded='false']
  .citrine-navgroup-chevron {
  transform: none;
}
.citrine-navgroup-chev-flip
  > :is(.citrine-navgroup-fold, .citrine-navgroup-parentrow)[aria-expanded='true']
  .citrine-navgroup-chevron {
  transform: rotate(180deg);
}

/* Collapsed, the heading is a rule instead of a word. It keeps the grouping visible — without it a
   collapsed rail is an undifferentiated column of glyphs — and carries the group's name as its
   accessible label, so the structure survives for a screen reader too.

   FULL BLEED and tight: inset, it read as a short dash floating in a 64px column rather than as a
   division of it. */
.citrine-navgroup-rule {
  height: 1px;
  background: var(--citrine-sidebar-rule);
  margin: var(--citrine-sp-1) 0;
}

/* Inside the body band the rule reaches back out over the band's 12px gutter to both rail edges. */
.citrine-sidebody .citrine-navgroup-rule {
  margin: var(--citrine-sp-2) calc(-1 * var(--citrine-sp-3));
}

/* A rule SEPARATES, so it is DRAWN only for a group that has something before it to be separated
   from. Adjacent-sibling, not `:first-of-type` — which counts by TAG and so never matched a group.
   The top group's rule is hidden visually, not with `display: none`: on a collapsed rail it is that
   group's only heading (`role="separator"` + `aria-label`), and `display: none` dropped its name from
   the accessibility tree too. Clipped and out of flow, it draws nothing and takes no space. */
.citrine-navgroup:not(.citrine-navgroup + .citrine-navgroup) > .citrine-navgroup-rule {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

/* Collapsed, the rule is the whole separation. */
.citrine-is-collapsed .citrine-navgroup + .citrine-navgroup {
  margin-top: 0;
}

/* ── The rows ─────────────────────────────────────────────────────────────────────────────────
   antd's `Menu` draws the rows' STRUCTURE and BEHAVIOUR — `<ul role="menu">`, `<li role="menuitem">`,
   arrow keys, Enter. Their LOOK is Citrine's, in the rail's own inks (the top of this file), because
   antd's dark theme paints the current row in the page's action colour and makes hover and current
   look alike. Measured on the live rail before this: current fill 1.19:1, hover = the same white text.

   SPECIFICITY, stated because it is the whole mechanism. antd's cssinjs rules sit inside `:where()`
   and are PREPENDED to <head>, so this stylesheet wins every tie. The heaviest antd rule a row meets is
   its hover — `.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item:not(-selected):hover` at
   (0,5,0) — so the hover rule below is (0,6,0) and every other row rule is at least (0,3,0), against
   antd's (0,2,0)–(0,3,0) for geometry and colour. Not one `.ant-*` selector: the rows are addressed by
   the kit's own classes, which antd's `<li>` carries.

   The start padding (12px) is not here: rc-menu writes it as an INLINE style from `inlineIndent`
   (see `NavMenu`), so it is set there. */
.citrine-sidebar .citrine-navmenu .citrine-navlink {
  /* Citrine owns the containing block its own pieces depend on — the link's full-row hit area and
     the collapsed dot — rather than borrowing antd's `position: relative` on the item. */
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  height: auto;
  min-height: var(--citrine-sidebar-row-height, 36px);
  margin: 0;
  padding-block: var(--citrine-sp-2);
  padding-inline-end: var(--citrine-sp-3);
  border-radius: var(--citrine-r-xs);
  background: transparent;
  color: var(--citrine-sidebar-row-ink);
  font-size: var(--citrine-sidebar-row-size, var(--citrine-font-size-14));
  font-weight: 400;
  line-height: var(--citrine-line-height-1p25);
  white-space: normal;
  transition:
    background-color var(--citrine-duration-140) var(--citrine-sidebar-ease),
    color var(--citrine-duration-140) var(--citrine-sidebar-ease);
}

.citrine-sidebar .citrine-navmenu .citrine-navlink + .citrine-navlink {
  margin-top: 2px;
}

/* Hover: a faint fill only. It must never look like the current row, which it used to. */
.citrine-sidebar .citrine-navmenu .citrine-navlink:not(.citrine-is-active):not([aria-disabled='true']):hover {
  background: var(--citrine-sidebar-hover);
  color: var(--citrine-sidebar-row-ink);
}

/* The current row: a stronger fill, white words AND a heavier weight — never a shade alone. */
.citrine-sidebar .citrine-navmenu .citrine-navlink.citrine-is-active {
  background: var(--citrine-sidebar-current);
  color: var(--citrine-side-ink-2);
  font-weight: var(--citrine-font-weight-600);
}

/* The ring is drawn on the ROW — whether the row itself holds focus (an action row) or its link does
   (a link row; rc-menu focuses the link). Inset, so the row's own `overflow: hidden` cannot clip it. */
.citrine-sidebar .citrine-navmenu .citrine-navlink:not([aria-disabled='true']):focus-visible,
.citrine-sidebar .citrine-navmenu .citrine-navlink:has(.citrine-navlink-anchor:focus-visible) {
  outline: 2px solid var(--citrine-sidebar-ring);
  outline-offset: -2px;
}

.citrine-sidebar .citrine-navmenu .citrine-navlink-anchor:focus-visible {
  outline: none;
}

/* The menu itself passes focus straight on to a row (see `NavMenu`), so it never shows a ring of its
   own — the ring that used to wrap a whole group. */
.citrine-sidebar .citrine-navgroup .citrine-navmenu:focus-visible {
  outline: none;
}

.citrine-sidebar .citrine-navmenu[hidden] {
  display: none;
}

/* The row's own line: icon, words, count. antd's `itemContent` span is `flex: auto` with an ellipsis,
   not a flex CONTAINER, so the line needs declaring. Static, not relative: the collapsed dot and the
   link's hit area both position against the `<li>` itself. */
.citrine-navlink-body {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  min-width: 0;
}

/* A link row's anchor carries the row's line, and its `::before` covers the WHOLE row — padding
   included — so a click, a middle-click or a Ctrl-click anywhere on the row is a click on the link. */
.citrine-navlink-anchor {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  width: 100%;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.citrine-navlink-anchor::before {
  content: '';
  position: absolute;
  inset: 0;
}

.citrine-navlink-icon {
  display: flex;
  flex: none;
  width: var(--citrine-sidebar-glyph-size, 16px);
  height: var(--citrine-sidebar-glyph-size, 16px);
}

.citrine-navlink-icon svg {
  width: 100%;
  height: 100%;
}

/* EVERY WORD FITS. A label longer than the row wraps to a second line; only a third would be cut.
   It used to be cut mid-word on one line with no ellipsis ("Listings awaiting a visi"), because the
   row body's `display: flex` stops antd's ellipsis and the 24px indent ate the room. */
.citrine-sidebar .citrine-navmenu .citrine-navlink-label:not(.citrine-sr-only) {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  min-width: 0;
  overflow: hidden;
  overflow-wrap: anywhere;
  white-space: normal;
}

.citrine-nav-count {
  margin-left: auto;
  font-size: var(--citrine-font-size-11);
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}

/* WORK OWED, and nothing else. A count that is merely large stays the quiet default — if every
   number is red, red has stopped saying anything. */
.citrine-nav-count-bad {
  background: var(--citrine-bad);
  color: var(--citrine-ink-on-solid);
  border-radius: var(--citrine-r-pill);
  padding: 0 var(--citrine-sp-2);
  opacity: 1;
  font-weight: var(--citrine-font-weight-700);
}

/* ── Short bands ──────────────────────────────────────────────────────────────────────────────
   OWNER RULING, 2026-09-11 ("tighter rows on short screens"). A band under 640px tall — a 1366×768
   laptop (594px), 1280×800, a 1440×900 desk at 125% zoom — draws 32px rows, 4px heading padding and
   6px between groups, so the names of the folded groups still show under the open ones. Measured on
   a consuming app's live rail at 1366×768 with 36px rows: the first folded heading fell just past
   the band's edge and the fade covered empty navy, so a laptop reader could not see what else there
   was. Taller bands keep the 36px rows the rail was picked with.

   A CONTAINER query on the band, not a media query on the window: the band's own height is what runs
   out once the brand, the hero and the footer have taken their share — a rail with no hero has 56px
   more. Size containment is safe here because the band's size never came from its content: it is a
   `flex: 1 1 auto; min-height: 0` item in a full-height column. The collapsed rail's 40px squares are
   untouched (their rule is more specific). */
.citrine-sidebody {
  container: citrine-sidebody / size;
}

@container citrine-sidebody (max-height: 640px) {
  .citrine-sidebar .citrine-navmenu .citrine-navlink {
    min-height: 32px;
    padding-block: var(--citrine-space-6);
  }

  .citrine-navgroup-fold {
    padding-block: var(--citrine-sp-1);
  }

  .citrine-navgroup-foldable + .citrine-navgroup-foldable {
    margin-top: var(--citrine-space-6);
  }

  .citrine-navgroup-parentrow {
    min-height: var(--citrine-sidebar-compact-row-height);
    padding-block: var(--citrine-space-6);
  }

  .citrine-sidebar.citrine-sidebar-tiles .citrine-navmenu-top .citrine-navlink,
  .citrine-sidebar-tiles .citrine-navgroup-parentrow {
    padding-block: var(--citrine-sp-1);
  }
}

/* ── density="compact" ────────────────────────────────────────────────────────────────────────
   Tighter rows and gaps, ON A DESK. Below the md breakpoint a pointer is a finger, and the rows
   go back to the default; inside `AppShell`'s phone drawer they keep the 44px target (below). A
   collapsed rail keeps its 40px squares. */
@media not all and (max-width: 900px) {
  .citrine-sidebar.citrine-sidebar-compact:not(.citrine-is-collapsed) .citrine-navmenu .citrine-navlink,
  .citrine-sidebar-compact:not(.citrine-is-collapsed) .citrine-navgroup-parentrow {
    min-height: var(--citrine-sidebar-compact-row-height);
    padding-block: var(--citrine-sidebar-compact-row-pad);
  }

  .citrine-sidebar.citrine-sidebar-compact .citrine-navmenu .citrine-navlink + .citrine-navlink {
    margin-top: var(--citrine-sidebar-compact-row-gap);
  }

  .citrine-sidebar-compact:not(.citrine-is-collapsed) .citrine-navgroup + .citrine-navgroup {
    margin-top: var(--citrine-sidebar-compact-group-gap);
  }

  .citrine-sidebar-compact:not(.citrine-is-collapsed) .citrine-navgroup-foldable + .citrine-navgroup-foldable,
  .citrine-sidebar-compact:not(.citrine-is-collapsed) .citrine-navgroup-parent + .citrine-navgroup-parent {
    margin-top: var(--citrine-sidebar-compact-fold-gap);
  }

  .citrine-sidebar-compact .citrine-navgroup-fold {
    padding-block: var(--citrine-sp-1);
  }

  .citrine-sidebar.citrine-sidebar-compact.citrine-sidebar-tiles:not(.citrine-is-collapsed)
    .citrine-navmenu-top
    .citrine-navlink,
  .citrine-sidebar-compact.citrine-sidebar-tiles:not(.citrine-is-collapsed) .citrine-navgroup-parentrow {
    padding-block: var(--citrine-sp-1);
  }
}

/* ── density="roomy" ──────────────────────────────────────────────────────────────────────────
   40px rows ON A DESK; below the md breakpoint the default rows come back, as with `compact`. A
   collapsed rail keeps its 40px squares. */
@media not all and (max-width: 900px) {
  .citrine-sidebar.citrine-sidebar-roomy:not(.citrine-is-collapsed) .citrine-navmenu .citrine-navlink,
  .citrine-sidebar-roomy:not(.citrine-is-collapsed) .citrine-navgroup-parentrow,
  .citrine-sidebar-roomy:not(.citrine-is-collapsed) .citrine-navgroup-fold-row {
    min-height: 40px;
  }
}

/* ── rowSize="small" ──────────────────────────────────────────────────────────────────────────
   A group's rows a step down from a top-level row: 13px words and a 14px glyph everywhere, and 32px
   rows ON A DESK on every density — (0,6,0), after the density rules. Below the md breakpoint a
   pointer is a finger, so the rows keep the default height there, as `compact` does. A collapsed rail
   keeps its 40px squares. */
.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup.citrine-navgroup-small-rows
  .citrine-navmenu
  .citrine-navlink {
  font-size: var(--citrine-sidebar-child-row-size, var(--citrine-font-size-13));
}

@media not all and (max-width: 900px) {
  .citrine-sidebar:not(.citrine-is-collapsed)
    .citrine-navgroup.citrine-navgroup-small-rows
    .citrine-navmenu
    .citrine-navlink {
    min-height: 32px;
    padding-block: var(--citrine-space-6);
  }
}

.citrine-sidebar:not(.citrine-is-collapsed)
  .citrine-navgroup-small-rows
  .citrine-navmenu
  .citrine-navlink-icon {
  width: 14px;
  height: 14px;
}

/* ── Collapsed rows ───────────────────────────────────────────────────────────────────────────
   A 40px square with its 16px icon centred: 12px of inline-indent start, 12px of end padding, and the
   icon exactly fills what is left. (antd's 24 + 16 left a 0px content box, so all 21 icons of a
   consumer's collapsed rail were drawn into nothing.) The words come back beside the rail on hover
   and on focus — a Tooltip, from `NavMenu`. */
.citrine-sidebar.citrine-is-collapsed .citrine-navmenu .citrine-navlink {
  justify-content: center;
  height: 40px;
  min-height: 40px;
  padding-block: 0;
}

.citrine-is-collapsed .citrine-navlink-body,
.citrine-is-collapsed .citrine-navlink-anchor {
  justify-content: center;
  gap: 0;
}

/* A collapsed tile is wider than the 16px the row's padding leaves, so it overhangs that box evenly
   into the padding, centred. The line that holds it must not clip it — nor a bare glyph's: the band
   is 63px (the rail's 1px edge comes out of it), so a row's box is 15px, not 16, and a clipping line
   shaved half a pixel off each side of every child row's glyph. The words beside it are visually
   hidden and out of flow, so nothing else can spill. (0,4,0): antd's own clip on the line is
   `.ant-menu-… .ant-menu-title-content` at (0,3,0) once its `:where()` hash is discounted. */
.citrine-sidebar.citrine-is-collapsed .citrine-navmenu .citrine-navlink-body {
  overflow: visible;
}

/* THE 64px RAIL IS CITRINE'S, NOT ANTD'S. antd collapses an inline menu to its own `collapsedWidth`
   (80px) through `inlineCollapsed`, which this rail deliberately does not use — see the note on
   `NavMenu` in Sidebar.tsx. */
.citrine-is-collapsed .citrine-navmenu {
  padding-inline: 0;
}

/* The count survives a collapse as a DOT in the row's corner. There is no room for a figure at 64px,
   and the number is already in the row's accessible name — but losing every trace of it would hide
   the one reason to expand the rail again. */
.citrine-navlink-dot {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--citrine-side-ink-2);
}

.citrine-navlink-dot-bad {
  background: var(--citrine-bad);
}

/* ── The footer ───────────────────────────────────────────────────────────────────────────────
   `margin-top: auto` pins this to the BOTTOM of the rail whether or not a hero, a toggle or any
   groups are present; with a `SidebarBody` above it, it simply never scrolls. */
.citrine-sidefoot {
  margin-top: auto;
  padding: var(--citrine-space-14) var(--citrine-sp-4);
  border-top: 1px solid var(--citrine-sidebar-rule);
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  flex: none;
}

.citrine-is-collapsed .citrine-sidefoot {
  justify-content: center;
  padding-inline: 0;
}

/* The initials disc is drawn in the rail's own inks, like everything else on the rail. It used to take
   the PAGE's data green with the page's accent ink — fine with white in light, but in dark the data
   green turns light while the ink stays white: 1.9:1 (measured in a consuming app,
   2026-09-11). The rail is the same navy in both themes, so its disc is too — white on `side-2`,
   about 12:1 — and green stays the colour of figures. The 1px inner edge is what keeps a navy disc
   from sinking into a navy rail. */
.citrine-sidefoot-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-700);
  background: var(--citrine-side-2);
  color: var(--citrine-side-ink-2);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--citrine-side-ink-2) 18%, transparent);
}

/* Opt-in (`avatarHue`): the disc in a hue, the pair `<Avatar hue>` draws. It reads the RAIL's hue
   roles, like a hued hero: with ramps supplied they are the ramp's button fill and ink (4.5:1 or
   better for every hue in both modes, measured on the test fixture); with none they fall back to the
   hero's own rail pair, never to the page accent, which on a dark rail is navy on navy. The inner
   edge goes: it exists so a disc in the rail's OWN colour does not sink into the rail, and a hued
   fill is not the rail's colour. */
.citrine-sidefoot-avatar.citrine-hued {
  background: var(--citrine-hue-rail-hero);
  color: var(--citrine-hue-rail-hero-ink);
  box-shadow: none;
}

.citrine-sidefoot-text {
  min-width: 0;
}

.citrine-sidefoot-name {
  font-size: var(--citrine-font-size-12p5);
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-side-ink-2);
  line-height: var(--citrine-line-height-1p25);
}

/* 11px, up from 10.5 — the floor. */
.citrine-sidefoot-sub {
  font-size: var(--citrine-font-size-11);
  color: var(--citrine-sidebar-head-ink);
}

.citrine-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── In AppShell's phone drawer ───────────────────────────────────────────────────────────────
   The drawer owns the footprint and the edge: the rail fills it, top to bottom, and its body band is
   still the one scroller. A finger is the pointer here, so every row and every control in the rail
   holds the 44px target (`--citrine-target-min`), whatever `density` says. */
.citrine-sidebar.citrine-sidebar-in-drawer {
  position: static;
  width: 100%;
  flex: 1 1 auto;
  height: 100%;
}

.citrine-sidebar-in-drawer .citrine-sidebar-inner {
  border-right: 0;
}

.citrine-sidebar.citrine-sidebar-in-drawer:not(.citrine-is-collapsed) .citrine-navmenu .citrine-navlink,
.citrine-sidebar-in-drawer .citrine-navgroup-parentrow,
.citrine-sidebar-in-drawer .citrine-navgroup-fold {
  min-height: var(--citrine-target-min);
}

/* The close control keeps the toggle's 30px look, and reaches 44px around it. */
.citrine-sidebar-in-drawer .citrine-sidecollapse::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--citrine-target-min);
  height: var(--citrine-target-min);
  transform: translate(-50%, -50%);
}

@media (prefers-reduced-motion: reduce) {
  .citrine-sidebar,
  .citrine-sidebar-inner,
  .citrine-sidehero,
  .citrine-navgroup-fold,
  .citrine-navgroup-parentrow,
  .citrine-navgroup-chevron,
  .citrine-sidebar .citrine-navmenu .citrine-navlink {
    transition: none;
  }
}

/* The rail does not hide itself at any width. Where it gives way to a small-screen bar is the app
   frame's decision (`AppShell navSwitch`), made on the frame's own slot. */

/* ── Bold top-level rows (`Sidebar rowWeight="bold"`) ─────────────────────────────────────────
   A standalone row, a parent row and a row-sized folding heading draw their words at 500 (medium),
   so the top level reads above the rows inside a group, which keep their own weight. (0,5,0) and
   last in the file: over the current row's 600 and a parent's "holds the current page" 600, so the
   whole top level sits at one weight and the current row is told by its ground, not its weight. */
.citrine-sidebar.citrine-sidebar-bold-rows .citrine-navmenu.citrine-navmenu-top .citrine-navlink,
.citrine-sidebar.citrine-sidebar-bold-rows
  .citrine-navgroup
  .citrine-navgroup-parentrow
  .citrine-navgroup-text,
.citrine-sidebar.citrine-sidebar-bold-rows
  .citrine-navgroup
  .citrine-navgroup-fold.citrine-navgroup-fold-row {
  font-weight: var(--citrine-font-weight-500);
}

/* ── SignInForm ────────────────────────────────────────────────────────────────────────── */
/* UI kit — SignInForm. The sign-in form as a consumer's mockups drew it, on `--citrine-signin-*` tokens.

   Every measure is the mockups' own. Every colour, face, corner and duration is a token with a neutral
   default; a consumer sets its brand on the tokens, never on these rules. The form's parts rise into
   place once, when the form first appears (the mockups' `.anim .lf > *`); the form itself is mounted
   once and stays, so that happens once. */

.citrine-signin {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  font-family: var(--citrine-signin-font);
  /* The mockups' body measure, so the form reads the same whatever the host document sets. */
  font-size: 15px;
  line-height: 1.5;
  color: var(--citrine-signin-ink);
}

.citrine-signin h2,
.citrine-signin h3,
.citrine-signin p {
  margin: 0;
}

.citrine-signin :focus-visible {
  outline: 2px solid var(--citrine-signin-accent);
  outline-offset: 2px;
}

/* Each part rises into place, one 60ms after the last. */
.citrine-signin > * {
  animation: citrine-signin-rise var(--citrine-signin-rise) var(--citrine-signin-easing) both;
  animation-delay: calc(var(--citrine-signin-n, 0) * 60ms);
}

.citrine-signin > :nth-child(2) {
  --citrine-signin-n: 1;
}
.citrine-signin > :nth-child(3) {
  --citrine-signin-n: 2;
}
.citrine-signin > :nth-child(4) {
  --citrine-signin-n: 3;
}
.citrine-signin > :nth-child(5) {
  --citrine-signin-n: 4;
}
.citrine-signin > :nth-child(6) {
  --citrine-signin-n: 5;
}
.citrine-signin > :nth-child(7) {
  --citrine-signin-n: 6;
}

@keyframes citrine-signin-rise {
  from {
    transform: translateY(12px);
  }
}

/* ── the head ─────────────────────────────────────────────────────────────────────────────────── */

.citrine-signin-head h2 {
  font-family: var(--citrine-signin-display);
  font-weight: 800;
  font-size: var(--citrine-signin-title-size);
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.citrine-signin-head p {
  margin-top: 6px;
  color: var(--citrine-signin-muted);
  font-size: 14px;
  overflow-wrap: anywhere;
}

.citrine-signin-keyword {
  background: linear-gradient(90deg, var(--citrine-signin-keyword-start), var(--citrine-signin-keyword-end));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The head's eyebrow and logo, when given: a quiet line in capitals, then the lockup at the height
   the consumer passed (its width follows), never wider than the form. Each is its own block at the
   start of the head, so a head without them lays out exactly as before. */
.citrine-signin-eyebrow {
  display: block;
  width: fit-content;
  margin-bottom: var(--citrine-space-10);
  font-size: var(--citrine-font-size-10);
  font-weight: var(--citrine-font-weight-700);
  letter-spacing: var(--citrine-signin-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--citrine-signin-muted);
}

.citrine-signin-logo {
  display: block;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
  margin-bottom: var(--citrine-space-4);
}

/* ── a field ──────────────────────────────────────────────────────────────────────────────────── */

.citrine-signin-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.citrine-signin-field label {
  font-size: 13px;
  font-weight: 700;
}

/* A required field's star, before the label. Generated, with empty alternative text, so it is neither
   in the label's words nor in the field's name ("Tenant", never "* Tenant"). */
.citrine-signin-field--required label::before {
  content: '*' / '';
  margin-right: var(--citrine-space-4);
  color: var(--citrine-signin-error);
}

.citrine-signin-input {
  display: flex;
  align-items: center;
  background: var(--citrine-signin-bg);
  border: var(--citrine-signin-border-width) solid var(--citrine-signin-border);
  border-radius: var(--citrine-signin-radius);
  transition:
    border-color 0.15s,
    box-shadow 0.15s;
}

.citrine-signin-input:focus-within {
  border-color: var(--citrine-signin-accent);
  box-shadow: 0 0 0 3px var(--citrine-signin-ring);
}

.citrine-signin-input input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 15px;
  padding: 12px 14px;
  outline: none;
}

/* The field shows focus as a whole — its rounded edge in the accent and the soft ring around it
   (`:focus-within` above). The form-wide `:focus-visible` outline outranked the input's own
   `outline: none` and drew a square box around the bare input inside that rounded field. */
.citrine-signin-input input:focus-visible {
  outline: none;
}

.citrine-signin-input input::placeholder {
  color: var(--citrine-signin-muted);
  opacity: 0.75;
}

.citrine-signin-input input:-webkit-autofill {
  -webkit-text-fill-color: var(--citrine-signin-ink);
  transition: background-color 99999s ease-in-out 0s;
}

.citrine-signin-eye {
  background: none;
  border: 0;
  color: var(--citrine-signin-muted);
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  padding: 8px 14px;
  cursor: pointer;
  border-radius: var(--citrine-signin-radius);
}

.citrine-signin-eye:hover {
  color: var(--citrine-signin-ink);
}

.citrine-signin-field--bad .citrine-signin-input {
  border-color: var(--citrine-signin-error);
}

.citrine-signin-err {
  color: var(--citrine-signin-error);
  font-size: 12.5px;
  font-weight: 600;
}

/* ── the row, the box, the text action ────────────────────────────────────────────────────────── */

.citrine-signin-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px 16px;
  flex-wrap: wrap;
}

.citrine-signin-check {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  font-size: 13.5px;
  cursor: pointer;
}

.citrine-signin-check input {
  accent-color: var(--citrine-signin-accent);
  width: 16px;
  height: 16px;
  margin: 0;
}

.citrine-signin-link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--citrine-signin-link);
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-align: left;
}

.citrine-signin-link:hover {
  text-decoration-thickness: 2px;
}

/* ── the buttons ──────────────────────────────────────────────────────────────────────────────── */

.citrine-signin-actions {
  display: flex;
  gap: 10px;
}

.citrine-signin-btn {
  flex: 1;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  background: var(--citrine-signin-button);
  color: var(--citrine-signin-accent-ink);
  border: var(--citrine-signin-border-width) solid transparent;
  border-radius: var(--citrine-signin-button-radius);
  padding: 13px 18px;
  font: inherit;
  font-weight: 800;
  font-size: 15px;
  cursor: pointer;
  /* A gradient fill slides under the pointer; a plain fill simply darkens. */
  background-size: 160% 100%;
  background-position: 0 0;
  box-shadow: var(--citrine-signin-button-shadow);
  transition:
    background-position 0.4s,
    filter 0.15s,
    transform 0.1s,
    box-shadow 0.2s;
}

.citrine-signin-btn:hover {
  background-position: 100% 0;
  filter: brightness(0.9);
}

.citrine-signin-btn:active {
  transform: translateY(1px);
}

.citrine-signin-btn:disabled {
  cursor: progress;
  filter: saturate(0.7) brightness(0.95);
}

.citrine-signin-btn--busy::before {
  content: '';
  width: 15px;
  height: 15px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: citrine-signin-spin var(--citrine-signin-spin) linear infinite;
}

.citrine-signin-btn--ghost {
  flex: 0 0 auto;
  background: transparent;
  color: var(--citrine-signin-ink);
  border: var(--citrine-signin-border-width) solid var(--citrine-signin-border);
  box-shadow: none;
}

@keyframes citrine-signin-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ── the alert and the foot ───────────────────────────────────────────────────────────────────── */

.citrine-signin-alert {
  border: 1px solid color-mix(in srgb, var(--citrine-signin-error) 45%, transparent);
  background: color-mix(in srgb, var(--citrine-signin-error) 12%, transparent);
  color: var(--citrine-signin-error);
  border-radius: min(var(--citrine-signin-radius), 10px);
  padding: 10px 12px;
  font-size: 13.5px;
  font-weight: 600;
}

/* A word of help rather than a fault: the same box in the accent. */
.citrine-signin-alert--info {
  border-color: color-mix(in srgb, var(--citrine-signin-accent) 45%, transparent);
  background: color-mix(in srgb, var(--citrine-signin-accent) 12%, transparent);
  color: var(--citrine-signin-accent);
}

.citrine-signin-foot {
  font-size: 12.5px;
  color: var(--citrine-signin-muted);
}

.citrine-signin-foot--center {
  text-align: center;
}

/* ── middle: the kit's standard control size ──────────────────────────────────────────────────── */
/* Opt-in (`size="middle"`): 32px fields with 6px corners, 14px words, labels in the regular weight
   and a 40px button, so the form sits beside an app's other forms. Only measures change. */

.citrine-signin--middle .citrine-signin-field label {
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-400);
}

.citrine-signin--middle .citrine-signin-input {
  border-radius: var(--citrine-radius-6);
}

.citrine-signin--middle .citrine-signin-input input {
  font-size: var(--citrine-font-size-14);
  line-height: var(--citrine-signin-middle-leading);
  padding: var(--citrine-space-4) var(--citrine-space-11);
}

.citrine-signin--middle .citrine-signin-eye {
  padding: var(--citrine-space-4) var(--citrine-space-11);
  border-radius: var(--citrine-radius-6);
}

.citrine-signin--middle .citrine-signin-btn {
  min-height: var(--citrine-signin-middle-button-height);
  padding: 0 var(--citrine-signin-middle-button-pad);
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-600);
  border-radius: var(--citrine-radius-6);
}

/* ── the dialog over the page ─────────────────────────────────────────────────────────────────── */

.citrine-signin-modal {
  position: fixed;
  inset: 0;
  z-index: var(--citrine-z-sticky);
  background: var(--citrine-signin-overlay);
  display: grid;
  place-items: center;
  padding: 20px;
}

.citrine-signin-dialog {
  width: 100%;
  max-width: 400px;
  background: var(--citrine-signin-surface);
  color: var(--citrine-signin-ink);
  border: var(--citrine-signin-border-width) solid var(--citrine-signin-border);
  border-radius: var(--citrine-signin-card-radius);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: var(--citrine-signin-dialog-shadow);
  font-family: var(--citrine-signin-font);
}

.citrine-signin-dialog h3 {
  font-family: var(--citrine-signin-display);
  font-size: 20px;
  font-weight: 800;
}

.citrine-signin-dialog p {
  color: var(--citrine-signin-muted);
  font-size: 14px;
}

/* ── reduced motion ───────────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .citrine-signin > *,
  .citrine-signin-btn--busy::before {
    animation: none;
  }
}

/* ── Skeleton ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Skeleton / SkeletonText. Group B wave 3: the shimmer gradient and duration now
   reproduce antd's own `<Skeleton active>` formula exactly (`skeleton/style/index.js`) —
   `linear-gradient(90deg, gradientFromColor 25%, gradientToColor 37%, gradientFromColor 63%)` at
   `background-size: 400% 100%`, 1.4s. `--citrine-skel-from`/`-to` are set inline by Skeleton.tsx
   from `theme.useToken()`'s `colorFillContent`/`colorFill` (see Skeleton.tsx's module comment for
   why the DOM stays bespoke while the colour source does not). */

.citrine-skel-group {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-2);
}

.citrine-skel-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.citrine-skel {
  display: block;
  background-image: linear-gradient(
    90deg,
    var(--citrine-skel-from) 25%,
    var(--citrine-skel-to) 37%,
    var(--citrine-skel-from) 63%
  );
  background-size: 400% 100%;
  border-radius: var(--citrine-radius-8);
  height: 12px;
  animation: citrine-skel-shimmer 1.4s ease infinite;
}

@keyframes citrine-skel-shimmer {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .citrine-skel {
    animation: none;
  }
}

/* ── Spinner ───────────────────────────────────────────────────────────────────────────── */
.citrine-spinner {
  display: inline-block;
  box-sizing: border-box;
  flex: none;
  width: var(--citrine-space-20);
  height: var(--citrine-space-20);
  border: var(--citrine-space-2) solid currentColor;
  border-right-color: transparent;
  border-radius: var(--citrine-r-pill);
  animation: citrine-spinner-rotate calc(var(--citrine-duration-150) * 6) linear infinite;
}

.citrine-spinner-sm {
  width: var(--citrine-space-16);
  height: var(--citrine-space-16);
}

.citrine-spinner-lg {
  width: var(--citrine-space-24);
  height: var(--citrine-space-24);
}

.citrine-spinner-reduced-motion {
  border-style: dashed;
  border-right-color: currentColor;
  animation: none;
}

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

@media (prefers-reduced-motion: reduce) {
  .citrine-spinner {
    border-style: dashed;
    border-right-color: currentColor;
    animation: none;
  }
}

/* ── Stack ─────────────────────────────────────────────────────────────────────────────── */
/* Stack and Row carry no rules of their own — they are antd `Flex` boxes and their layout comes
   from antd's stylesheet plus the inline styles each component pins. This file holds the two things
   antd does not get right: its vertical Divider in a row of controls, and which side of a Spread
   gives way. */

/* A VERTICAL RULE BELONGS IN A ROW OF CONTROLS, NOT IN A LINE OF TEXT.
   antd draws `height: 0.9em` on a vertical divider — font-relative, about 12.6px at the 14px base
   — which reads short against a 32px control and was drawn for a rule between words. A partition
   in a toolbar wants to be shorter than the controls it separates and taller than the text inside
   them, and the way to say that without inventing a pixel is to stretch to the row and inset.

   `min-height` is the fallback, and it is load-bearing: `align-self` does nothing outside a flex
   container, and with `height: auto` alone an empty inline-block there would collapse to 0 and
   draw nothing at all. */
.citrine-divider-vertical {
  align-self: stretch;
  height: auto;
  min-height: 1.4em;
  margin-block: var(--citrine-space-4, 4px);
}

/* A SIDE OF A SPREAD CAN GIVE WAY. `Spread` is a wrapping flex row. A wrapping flex container
   decides its lines BEFORE anything shrinks, from each item's flex basis — `auto`, a side's full
   one-line width — so a long body fills a line of its own and the other side (usually a price and
   its action) drops underneath it. `min-width: 0` alone does not stop that: it only lets an item
   shrink once it is already on a line. Measured by a consumer on every row of a catalogue.

   So in a two-sided Spread the FIRST side gives way: a zero basis puts both sides on one line, and
   growing into the free space gives the first side back the room the second does not use, up to
   its own one-line width (`max-content`) and no further — so a short side, or a control such as a
   button, keeps its natural size and nothing moves. The second side keeps its own width at the far
   end, as before. Only two-sided: with three or more children, growing the first would pull the
   middle ones off centre, so those keep `space-between` as they were.

   The first side narrows down to its longest word (`min-content`) and no further. Below that the
   row still wraps and the second side drops under it, which is what `wrap` is for; a floor of 0
   there would instead squeeze the words out over the second side. Every other side keeps
   `min-width: 0`. */
.citrine-spread > * {
  min-width: 0;
}
.citrine-spread > :first-child:nth-last-child(2) {
  flex: 1 1 0;
  min-width: min-content;
  max-width: max-content;
}

/* `Stack fill` — the last child takes the height the column has left, so two columns of cards in
   one stretched row end on the same line. Only the last: growing every child would spread the
   spare height between them and open gaps inside the column. */
.citrine-stack-fill > :last-child {
  flex: 1 1 auto;
}

/* ── StackedMeter ──────────────────────────────────────────────────────────────────────── */
/* UI kit — StackedMeter. One thin bar split into coloured parts. The root is the empty track; each
   part is a span as wide as its share, which the component hands over as the part's own
   `--citrine-stackedmeter-share`. Parts are clipped to the track's rounded ends. The inset hairline is
   a shadow, not a border, so a part's share is a share of the whole track. Nothing animates. */
.citrine-stackedmeter {
  display: flex;
  inline-size: 100%;
  block-size: var(--citrine-stackedmeter-height);
  overflow: hidden;
  border-radius: var(--citrine-stackedmeter-radius);
  background: var(--citrine-stackedmeter-track);
  box-shadow: inset 0 0 0 var(--citrine-stackedmeter-track-line-width) var(--citrine-stackedmeter-track-line);
}

.citrine-stackedmeter-sm {
  block-size: var(--citrine-stackedmeter-height-sm);
}

.citrine-stackedmeter-seg {
  flex: 0 0 var(--citrine-stackedmeter-share);
  min-inline-size: 0;
  background: var(--citrine-data);
}

.citrine-stackedmeter-seg-accent {
  background: var(--citrine-accent);
}

.citrine-stackedmeter-seg-ok {
  background: var(--citrine-ok);
}

.citrine-stackedmeter-seg-warn {
  background: var(--citrine-warn);
}

.citrine-stackedmeter-seg-bad {
  background: var(--citrine-bad);
}

.citrine-stackedmeter-seg-info {
  background: var(--citrine-info);
}

.citrine-stackedmeter-seg-neutral {
  background: var(--citrine-stackedmeter-neutral);
}

/* ── StageStrip ────────────────────────────────────────────────────────────────────────── */
/* UI kit — StageStrip. Every stage of a process on one strip, each with its count.

   Two groupings, chosen on the root. The cell's type and the pressable form are shared; each grouping
   owns what is drawn around the cells.

   ── BOXES (default) ─────────────────────────────────────────────────────────────────────────────
   Groups of label → count cells side by side, each in a box of its own under its label, wrapping
   whole groups onto a second line before any cell is squeezed. A money group is tinted; an ended
   group is dashed on no ground; a stage holding nothing is a dashed outline.

   ── STRIP (opt-in, `grouping="strip"`) ──────────────────────────────────────────────────────────
   A run of stages is ONE rounded container divided by hairlines. The money stretch is said once, by a
   tinted face on its cells inside the strip — not by a box and a caption of its own. The stages that
   END it are a second strip, set apart by space and by their word over it (an uppercase eyebrow),
   with the same solid cells. The groups keep their own `<dl>`/`<ul>` in the document; `display:
   contents` takes their boxes out of the layout so every cell of the run flows in one line and WRAPS
   AS ONE BLOCK on a phone (4 + 3), rather than one box per group.

   Under 480px of the strip's own width it does NOT fold by default: every stage stays and the cells
   get denser. `narrow="chips"` keeps the old fold for a host that wants it. */

.citrine-stagestrip {
  container: citrine-stagestrip / inline-size;
  min-width: 0;
}

.citrine-stagestrip-wide {
  display: flex;
  flex-wrap: wrap;
}

/* Pressable form: a <ul> of <li>, because a <button> cannot be a <dl>'s child. */
ul.citrine-stagestrip-cells {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The cell reads the way the document does: the label, then its count. */
.citrine-stagestrip-cell {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--citrine-space-2);
}
/* `lead="figure"`: the count painted over the label. Reversed in the paint and NOT in the markup: the
   reading form is a description list, where a `<dd>` before its `<dt>` is invalid and would hand a
   screen reader the count before the thing it counts. The document stays term-then-description. */
.citrine-stagestrip-lead-figure .citrine-stagestrip-cell {
  flex-direction: column-reverse;
}
.citrine-stagestrip-cell dt,
.citrine-stagestrip-label {
  font-size: var(--citrine-font-size-12);
  white-space: nowrap;
  color: var(--citrine-ink-3);
}
.citrine-stagestrip-cell dd,
.citrine-stagestrip-count {
  margin: 0;
  font-size: var(--citrine-font-size-20, 20px);
  font-weight: var(--citrine-font-weight-700);
  font-variant-numeric: tabular-nums;
  color: var(--citrine-data);
}
/* A COUNT INSIDE THE MONEY STRETCH IS ON THE WASH, NOT THE SURFACE, and the data green is chosen to be
   text-safe against the surface: `#1F7A4A` measures 5.02:1 on white and **4.47:1** on
   `--citrine-data-wash`. 20px/700 is 15pt, just over the 14pt-bold line — but the green is kept on
   the stricter 4.5:1 floor. `--citrine-data-2` is the palette's own deeper step: 6.7:1 on the light
   wash, 8.9:1 on the dark. */
.citrine-stagestrip-money .citrine-stagestrip-cell:not(.citrine-stagestrip-empty) dd,
.citrine-stagestrip-money .citrine-stagestrip-cell:not(.citrine-stagestrip-empty) .citrine-stagestrip-count {
  color: var(--citrine-data-2);
}
/* An empty stage ranks by its figure: muted and lighter. */
.citrine-stagestrip-empty dd,
.citrine-stagestrip-empty .citrine-stagestrip-count {
  font-weight: var(--citrine-font-weight-500);
  color: var(--citrine-ink-3);
}

/* ══ BOXES (default) ═════════════════════════════════════════════════════════════════════════════ */

.citrine-stagestrip-boxes .citrine-stagestrip-wide {
  gap: var(--citrine-sp-3) var(--citrine-space-10);
}

.citrine-stagestrip-boxes .citrine-stagestrip-group {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-space-6);
  min-width: 0;
}

/* Every group draws its label line, empty or not, so the boxes' tops line up. */
.citrine-stagestrip-boxes .citrine-stagestrip-grouplabel {
  min-height: var(--citrine-stagestrip-boxes-label-height);
  font-size: var(--citrine-font-size-12);
  line-height: var(--citrine-stagestrip-boxes-label-height);
  color: var(--citrine-ink-3);
}
.citrine-stagestrip-boxes .citrine-stagestrip-money .citrine-stagestrip-grouplabel {
  color: var(--citrine-data);
  font-weight: var(--citrine-font-weight-600);
}

.citrine-stagestrip-boxes .citrine-stagestrip-cells {
  margin: 0;
  display: flex;
  overflow: hidden;
  background: var(--citrine-surface);
  border: 1px solid var(--citrine-line);
  border-radius: var(--citrine-r-sm);
}
.citrine-stagestrip-boxes .citrine-stagestrip-money .citrine-stagestrip-cells {
  background: var(--citrine-data-wash);
  border-color: var(--citrine-data-line);
}
/* The ENDED group — the stages that finish a process, set apart rather than emphasised. A dashed edge
   on no ground is the quietest way to say "outside the run" — it reads as a boundary, not as another
   band competing with the money group's wash. */
.citrine-stagestrip-boxes .citrine-stagestrip-ended .citrine-stagestrip-cells {
  background: transparent;
  border-style: dashed;
}
.citrine-stagestrip-boxes .citrine-stagestrip-cells > li {
  display: flex;
}

.citrine-stagestrip-boxes .citrine-stagestrip-cell {
  padding: var(--citrine-space-8) var(--citrine-space-10) var(--citrine-space-9);
  /* A FLOOR, not a size. Without it a cell is only as wide as its word, so a short stage name drew
     a 42px cell — under any reasonable target and visibly narrower than its neighbours in a row
     that is meant to read as one scale. */
  min-width: var(--citrine-stagestrip-boxes-cell-min-width);
  min-height: var(--citrine-stagestrip-boxes-cell-min-height);
}
.citrine-stagestrip-boxes .citrine-stagestrip-cell dt,
.citrine-stagestrip-boxes .citrine-stagestrip-label {
  line-height: 1.3;
}
.citrine-stagestrip-boxes .citrine-stagestrip-cell dd,
.citrine-stagestrip-boxes .citrine-stagestrip-count {
  line-height: 1.2;
}
/* AN EMPTY STAGE IS OUTLINED, not merely greyed: the same sentence as the ended group's dashed frame,
   one level down, for a cell holding nothing. Without it, "nothing here yet" and "a small number"
   read alike. Transparent ground, so it reads as an outline waiting to be filled. The reading form
   only: a pressable cell keeps its button's flat edge. Declared BEFORE the rule between cells, so
   that rule keeps the leading edge solid. */
.citrine-stagestrip-boxes .citrine-stagestrip-empty:not(.citrine-stagestrip-pressable) {
  background: transparent;
  border: 1px dashed var(--citrine-line);
}
/* The rule between cells. Two selectors because the two forms nest differently: the reading form's
   cells are siblings, the pressable form's each sit in their own <li>. */
.citrine-stagestrip-boxes .citrine-stagestrip-cell + .citrine-stagestrip-cell,
.citrine-stagestrip-boxes .citrine-stagestrip-cells > li + li {
  border-left: 1px solid var(--citrine-line);
}
.citrine-stagestrip-boxes .citrine-stagestrip-money .citrine-stagestrip-cell + .citrine-stagestrip-cell,
.citrine-stagestrip-boxes .citrine-stagestrip-money .citrine-stagestrip-cells > li + li {
  border-left-color: var(--citrine-data-line);
}
.citrine-stagestrip-boxes .citrine-stagestrip-ended .citrine-stagestrip-cell + .citrine-stagestrip-cell,
.citrine-stagestrip-boxes .citrine-stagestrip-ended .citrine-stagestrip-cells > li + li {
  border-left-style: dashed;
}

/* ══ STRIP (opt-in) ══════════════════════════════════════════════════════════════════════════════ */

.citrine-stagestrip-strip .citrine-stagestrip-wide {
  /* The strips' feet line up whether or not one carries a word over it. */
  align-items: flex-end;
  gap: var(--citrine-sp-3) var(--citrine-sp-5);
}

.citrine-stagestrip-run {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--citrine-sp-1);
  min-width: 0;
  max-width: 100%;
}

/* The run's word: an eyebrow, not a caption box. */
.citrine-stagestrip-runlabel {
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-500);
  line-height: 1;
  letter-spacing: var(--citrine-stagestrip-label-tracking);
  text-transform: uppercase;
  color: var(--citrine-ink-3);
}

.citrine-stagestrip-box {
  max-width: 100%;
  overflow: hidden;
  background: var(--citrine-surface);
  border: 1px solid var(--citrine-stagestrip-hairline);
  border-radius: var(--citrine-stagestrip-radius);
}

/* Every cell carries a hairline on its leading edge and the flow is pulled 1px left under the box's
   clipped edge, so the first cell of EVERY row — the first line and each wrapped one — loses its
   hairline to the border, and no row starts with a double line. */
.citrine-stagestrip-flow {
  display: flex;
  flex-wrap: wrap;
  margin-left: -1px;
}
.citrine-stagestrip-strip .citrine-stagestrip-group,
.citrine-stagestrip-strip .citrine-stagestrip-cells {
  display: contents;
}
.citrine-stagestrip-strip .citrine-stagestrip-cells > * {
  display: flex;
  border-left: 1px solid var(--citrine-stagestrip-hairline);
}
/* The money stretch: its cells take the data wash, inside the one strip. On the ITEM (the reading
   form's cell, the pressable form's <li>) so a button's own reset ground does not cover it. */
.citrine-stagestrip-strip .citrine-stagestrip-money .citrine-stagestrip-cells > * {
  background: var(--citrine-stagestrip-money-face);
}

/* An empty stage stays solid and in line — inside one strip a dashed cell breaks the hairline
   rhythm, and the ended strip's cells are solid too. */
.citrine-stagestrip-strip .citrine-stagestrip-cell {
  align-items: flex-start;
  padding: var(--citrine-space-9) var(--citrine-space-14);
  /* A floor, not a size: the cell is as wide as its words, and never under a target. */
  min-width: var(--citrine-target-min);
  min-height: var(--citrine-target-min);
}
.citrine-stagestrip-strip .citrine-stagestrip-cell dt,
.citrine-stagestrip-strip .citrine-stagestrip-label {
  font-weight: var(--citrine-font-weight-500);
  line-height: 1.25;
}
.citrine-stagestrip-strip .citrine-stagestrip-cell dd,
.citrine-stagestrip-strip .citrine-stagestrip-count {
  line-height: 1.1;
}

/* ── The cell as a control ──────────────────────────────────────────────────────────────────────
   A <button> takes its font, colour and ground from the UA unless every one of them is said here,
   so this is a full reset, not a tidy-up. It fills its <li> so the whole cell is the target, not
   just the words inside it. */
.citrine-stagestrip-pressable {
  appearance: none;
  width: 100%;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
  background: none;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}
.citrine-stagestrip-pressable:not(:disabled):hover {
  background: var(--citrine-accent-wash);
}
.citrine-stagestrip-money .citrine-stagestrip-pressable:not(:disabled):hover {
  background: var(--citrine-data-line);
}
/* The kit draws its own ring on a control it draws itself: 2px of the action colour, 2px in — the
   cells are flush inside a rounded, clipped strip, so a ring drawn OUTSIDE would be cut off. */
.citrine-stagestrip-pressable:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: -2px;
}
/* Chosen is the ACTION colour, never the data green. Green here counts things; navy is what you
   pressed. A green fill on a cell full of green figures says "a big count", not "this one". */
.citrine-stagestrip-pressable[aria-pressed='true'],
.citrine-stagestrip-money .citrine-stagestrip-pressable[aria-pressed='true'] {
  background: var(--citrine-accent);
}
.citrine-stagestrip-pressable[aria-pressed='true'] .citrine-stagestrip-label,
.citrine-stagestrip-pressable[aria-pressed='true'] .citrine-stagestrip-count {
  color: var(--citrine-accent-ink);
}
/* A stage that cannot be chosen is still READ: the words and the figure stay, quieter. Nothing is
   hidden — a board that drops its empty stages stops being a picture of the whole process. */
.citrine-stagestrip-pressable:disabled {
  cursor: default;
  opacity: 0.6;
}

.citrine-stagestrip-narrow {
  display: none;
}
.citrine-stagestrip-chips {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--citrine-sp-2, 8px);
}
/* THE GROUND, NOT THE INK — measured. The data green a count is drawn in clears 5.02:1 on
   `--citrine-surface` and only **4.31:1** on `--citrine-accent-wash`, so a chip on the wash put its
   own figure under the floor. The cells in the wide form have always sat on the surface with a line
   around them; a chip is the same cell in a different shape, so it sits on the same ground and the
   green is readable again without touching the palette. */
.citrine-stagestrip-chips li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid var(--citrine-line, #e3e8f0);
  border-radius: 999px;
  background: var(--citrine-surface, #ffffff);
  font-size: var(--citrine-font-size-13, 13px);
  color: var(--citrine-ink-2, #45516a);
}
.citrine-stagestrip-chips b {
  font-variant-numeric: tabular-nums;
  color: var(--citrine-data);
}
/* Pressable narrow form: the pill look moves OFF the <li> and onto the button inside it, so the whole
   pill is the target rather than a smaller box sitting in a padded one. A class on the <ul> says
   which form this is — `:has()` would say the same thing and this does not need it. */
.citrine-stagestrip-chips-pressable li {
  min-height: 0;
  padding: 0;
  background: none;
}
.citrine-stagestrip-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 28px was under any target floor. A chip is a control wherever it is pressable. */
  min-height: 44px;
  margin: 0;
  padding: 0 12px;
  border: 1px solid var(--citrine-line, #e3e8f0);
  border-radius: 999px;
  background: var(--citrine-surface, #ffffff);
  font-family: inherit;
  font-size: var(--citrine-font-size-13, 13px);
  color: var(--citrine-ink-2, #45516a);
  cursor: pointer;
}
.citrine-stagestrip-chip:not(:disabled):hover {
  background: var(--citrine-accent-wash, #eef2f9);
}
.citrine-stagestrip-chip:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: 2px;
}
.citrine-stagestrip-chip[aria-pressed='true'] {
  background: var(--citrine-accent);
  border-color: var(--citrine-accent);
  color: var(--citrine-accent-ink, #ffffff);
}
.citrine-stagestrip-chip[aria-pressed='true'] b {
  color: var(--citrine-accent-ink, #ffffff);
}
.citrine-stagestrip-chip:disabled {
  cursor: default;
  opacity: 0.6;
}
.citrine-stagestrip-summary {
  margin: 0;
  font-size: var(--citrine-font-size-14, 14px);
  line-height: 1.5;
  color: var(--citrine-ink-3, #6b768b);
}

/* ── Narrow ─────────────────────────────────────────────────────────────────────────────────────
   The default keeps the board.

   Boxes: groups sit beside each other and wrap rather than each taking a whole row, the cells flex
   down to a floor and stand at a thumb's target, and the padding comes in.

   Strip: the run is still one strip — its cells wrap inside the one border (four and three, say),
   never into a box per group, and the ended strip drops under it. The cells come in to a thumb's
   target and the corners tighten. */
@container citrine-stagestrip (max-width: 479px) {
  .citrine-stagestrip-boxes .citrine-stagestrip-wide {
    gap: var(--citrine-sp-2) var(--citrine-space-6);
  }
  .citrine-stagestrip-boxes .citrine-stagestrip-group {
    flex: 1 1 auto;
    min-width: 0;
  }
  .citrine-stagestrip-boxes .citrine-stagestrip-cells {
    flex-wrap: wrap;
  }
  .citrine-stagestrip-boxes .citrine-stagestrip-cells > li {
    flex: 1 1 auto;
  }
  .citrine-stagestrip-boxes .citrine-stagestrip-cell {
    min-width: var(--citrine-stagestrip-boxes-cell-min-width-narrow);
    min-height: var(--citrine-stagestrip-boxes-cell-min-height-narrow);
    padding: var(--citrine-space-4) var(--citrine-space-8);
  }

  .citrine-stagestrip-box {
    border-radius: var(--citrine-stagestrip-radius-narrow);
  }
  .citrine-stagestrip-strip .citrine-stagestrip-cell {
    padding: var(--citrine-space-7) var(--citrine-space-11);
  }
}

/* The old behaviour, opt-in: fold to chips and sum the rest up in a line. */
@container citrine-stagestrip (max-width: 479px) {
  .citrine-stagestrip-foldchips .citrine-stagestrip-wide {
    display: none;
  }
  .citrine-stagestrip-foldchips .citrine-stagestrip-narrow {
    display: flex;
    flex-direction: column;
    gap: var(--citrine-sp-2);
  }
}

/* ── FILLING A PANEL ─────────────────────────────────────────────────────────────────────────────
   The groups (or strips) give up their content sizing and share the width they are given, and the
   cells inside them do the same. Only under `.citrine-stagestrip-fill`, so a strip in open page space
   is untouched. */
.citrine-stagestrip-boxes.citrine-stagestrip-fill .citrine-stagestrip-group {
  flex: 1 1 auto;
}
.citrine-stagestrip-boxes.citrine-stagestrip-fill .citrine-stagestrip-cell,
.citrine-stagestrip-boxes.citrine-stagestrip-fill .citrine-stagestrip-cells > li {
  flex: 1 1 auto;
}
.citrine-stagestrip-strip.citrine-stagestrip-fill .citrine-stagestrip-run {
  flex: 1 1 auto;
  align-items: stretch;
}
.citrine-stagestrip-strip.citrine-stagestrip-fill .citrine-stagestrip-cells > * {
  flex: 1 1 auto;
}

/* ── StatTile ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — StatTile styles. Source: artifact `.tile` family.
   Structure only below — surface, radii, spacing, type scale and status colours are seeded inline
   from `theme.useToken()` in StatTile.tsx (see `useStatTileTokens`), not declared here.

   `--citrine-data`/`--citrine-data-wash` on `.citrine-tile-ico-is-violet` are the ONE exception:
   the ratified palette carries two accents and antd carries one, so no antd token means "the data
   accent". Same category as a rail width or a z-index layer — a value with no antd equivalent to
   draw from, so it keeps. The DEFAULT tone has an equivalent and takes it, exactly as `Chip`'s
   `accent` tone does (`colorPrimaryBg` / `colorPrimaryText`). */

.citrine-tile {
  background: var(--citrine-tile-bg);
  border: 1px solid var(--citrine-tile-line);
  border-radius: var(--citrine-tile-r);
  padding: var(--citrine-tile-pad);
  display: flex;
  flex-direction: column;
  gap: var(--citrine-tile-gap);
  box-shadow: var(--citrine-tile-sh);
}

/* `hoverable` — antd's `Card hoverable` lift: the stronger card shadow while pointed at. */
.citrine-tile-is-hoverable {
  transition: box-shadow var(--citrine-tile-motion) ease;
}

.citrine-tile-is-hoverable:hover {
  box-shadow: var(--citrine-tile-sh-hover);
}

@media (prefers-reduced-motion: reduce) {
  .citrine-tile-is-hoverable {
    transition: none;
  }
}

/* `.citrine-row` used to be redeclared here, byte-for-byte identical to `Stack.css`'s rule. Removed
   rc.7 — `StatTile.tsx` now composes `Stack`'s own `Row` instead of a raw `.citrine-row` div, so
   this file owns no duplicate of a rule Citrine already ships once. */

.citrine-tile-ico {
  width: 38px;
  height: 38px;
  border-radius: var(--citrine-tile-r-sm);
  flex: none;
  display: grid;
  place-items: center;
  background: var(--citrine-tile-ico-bg);
  color: var(--citrine-tile-ico-ink);
}

.citrine-tile-ico svg {
  width: 18px;
  height: 18px;
}

.citrine-tile-ico-is-violet {
  background: var(--citrine-data-wash);
  color: var(--citrine-data);
}

/* Status tones — antd's status bg/text pairs, seeded in StatTile.tsx. */
.citrine-tile-ico-is-ok {
  background: var(--citrine-tile-ico-ok-bg);
  color: var(--citrine-tile-ico-ok-ink);
}

.citrine-tile-ico-is-warn {
  background: var(--citrine-tile-ico-warn-bg);
  color: var(--citrine-tile-ico-warn-ink);
}

.citrine-tile-ico-is-bad {
  background: var(--citrine-tile-ico-bad-bg);
  color: var(--citrine-tile-ico-bad-ink);
}

.citrine-tile-ico-is-info {
  background: var(--citrine-tile-ico-info-bg);
  color: var(--citrine-tile-ico-info-ink);
}

.citrine-tile-label {
  font-size: var(--citrine-tile-label-size);
  color: var(--citrine-tile-label-ink);
  font-weight: var(--citrine-tile-value-weight);
}

.citrine-tile-value {
  font-size: var(--citrine-tile-value-size);
  font-weight: var(--citrine-tile-value-weight);
  line-height: 1;
  /* Tracking, not colour or scale — kept as a literal. It is a readability tweak for tabular
     digits at a large size, not a brand decision, and antd exposes no letter-spacing token to draw
     it from either way. */
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
  color: var(--citrine-tile-value-ink);
}

.citrine-tile-skel {
  height: 32px;
  width: 60%;
  border-radius: var(--citrine-tile-r-sm);
  background: var(--citrine-tile-skel-bg);
}

.citrine-tile-foot {
  font-size: var(--citrine-tile-label-size);
  color: var(--citrine-tile-label-ink);
  margin-top: auto;
}

.citrine-tile-trend {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-tile-gap-xs);
  font-size: var(--citrine-tile-trend-size);
  font-weight: var(--citrine-tile-trend-weight);
  width: fit-content;
}

.citrine-tile-trend-up {
  color: var(--citrine-tile-ok);
}

.citrine-tile-trend-down {
  color: var(--citrine-tile-bad);
}

.citrine-tile-trend-flat {
  color: var(--citrine-tile-label-ink);
}

/* ── StepShell ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — StepShell. One section of a form per screen, and the trail of answers beside it.

   Colours come from antd's live theme via `antdTheme.useToken()` in `StepShell.tsx`, handed down as
   `--citrine-stepshell-*` / `--citrine-steptrail-*` custom properties. Control SIZES are not here at
   all — they are antd tokens set by the component's scoped ConfigProvider (see its header). This sheet
   is layout, type, and the one part antd has no component for: where you are — a track of segments
   by default, or a spine of steps (`progress="spine"`).

   Layout reads the shell's OWN width through container queries (the Own-Width Rule), never the
   window: the aside moves beside the card from 840px of the shell, and the actions stack once the
   card is under 480px. */

.citrine-stepshell {
  container: citrine-stepshell / inline-size;
}

.citrine-stepshell-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--citrine-sp-4);
  align-items: start;
}

@container citrine-stepshell (min-width: 840px) {
  .citrine-stepshell-grid {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }
}

.citrine-stepshell-cardslot {
  min-width: 0;
  container: citrine-stepcard / inline-size;
}

.citrine-stepshell-main {
  padding: 20px 24px 24px;
}

/* ── where you are ────────────────────────────────────────────────────────────────────────────── */

/* THE TRACK (the default): the count, and beside it one short segment per step, the walked ones lit.
   The segments are `aria-hidden`; the count line says the same in words. */
.citrine-stepshell-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--citrine-stepshell-top-gap);
  margin: 0 0 var(--citrine-stepshell-top-space);
}

.citrine-stepshell-top .citrine-stepshell-count {
  flex: none;
  margin: 0;
}

/* Segments shrink before they wrap: thirteen of them still fit a phone's card. */
.citrine-stepshell-track {
  display: flex;
  flex: 0 1 auto;
  justify-content: flex-end;
  gap: var(--citrine-stepshell-seg-gap);
  min-width: 0;
}

/* A DASH, AND IT STAYS ONE. A 4px floor is a square with a 2px radius — a dot — and ten dots read as
   nothing. `--citrine-stepshell-seg-min` (12px) is the narrowest this may shrink to and still be a
   dash: thirteen of them with their gaps need 228px, which a 390 phone's card still has beside the
   count line. */
.citrine-stepshell-seg {
  flex: 0 1 var(--citrine-stepshell-seg-w);
  min-width: var(--citrine-stepshell-seg-min);
  height: var(--citrine-stepshell-seg-h);
  border-radius: var(--citrine-stepshell-seg-r);
  background: var(--citrine-stepshell-seg-rest);
}

.citrine-stepshell-seg-on {
  background: var(--citrine-stepshell-seg-on);
}

/* The steps' names on the track: read out, drawn nowhere. Clipped, not `display: none`, which would
   drop them from the tree as well. */
.citrine-stepshell-sronly {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  list-style: none;
}

/* THE SPINE (`progress="spine"`): the count, then one node per step across the card's top row. Every
   value is a declared `--citrine-stepshell-*` token; the geometry was measured on a reference mockup at
   1440 (18px nodes, 24px current, 2px links 3px clear of each node, 12px names 7px under their node).
   The spine is its own container, so with `names="auto"` the names leave by the spine's width. */
.citrine-stepshell-spine {
  container: citrine-stepshell-spine / inline-size;
  margin: 0 0 var(--citrine-stepshell-spine-gap);
}

.citrine-stepshell-count {
  margin: 0 0 var(--citrine-stepshell-head-gap);
  font-size: var(--citrine-font-size-13);
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  color: var(--citrine-stepshell-ink-2, var(--citrine-ink-3));
}

/* No total and nothing walked: the count stands alone and keeps the row's own spacing. */
.citrine-stepshell-spine-bare .citrine-stepshell-count {
  margin: 0;
}

/* THE THREE STATES, one look across every journey in the app (LifecycleSteps draws the same):
   done — a solid dot in `stepshell-done` green, joined to the next done one by a green link;
   now  — a hollow ring in the page ACCENT with a soft wash of it around, and its name in the accent,
          bold. Read here, on the component, not in a root token: inside a `HueScope` the accent IS
          the section's colour (its deep, 4.5:1 on the card both ways), and a root token would have
          resolved to the app accent before any scope was entered;
   ahead — a hollow ring and link in `stepshell-rest`, which clears 3:1 on the card.
   Done and now differ in SHAPE (solid dot / ring with a halo), never only in colour. */
.citrine-stepshell-nodes {
  --citrine-stepshell-d: var(--citrine-stepshell-dot);
  --citrine-stepshell-now: var(--citrine-accent);
  --citrine-stepshell-now-halo: var(--citrine-accent-wash);
  display: flex;
  align-items: flex-start;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Equal seats, so half a seat is exactly the gap to the neighbour. */
.citrine-stepshell-node {
  position: relative;
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

/* The link runs from the previous node's edge to this one's, clear of both. */
.citrine-stepshell-node::before {
  content: '';
  position: absolute;
  top: calc(var(--citrine-stepshell-d) / 2 - var(--citrine-stepshell-link) / 2);
  left: calc(-50% + var(--citrine-stepshell-d) / 2 + var(--citrine-stepshell-link-gap));
  right: calc(50% + var(--citrine-stepshell-d) / 2 + var(--citrine-stepshell-link-gap));
  height: var(--citrine-stepshell-link);
  border-radius: var(--citrine-stepshell-link);
  background: var(--citrine-stepshell-rest);
}

.citrine-stepshell-node:first-child::before {
  display: none;
}

/* A link is green only between two DONE steps: the line runs green up to the last finished dot and
   grey from there on, into the current ring and past it. */
.citrine-stepshell-node-done::before {
  background: var(--citrine-stepshell-done);
}

.citrine-stepshell-dot {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--citrine-stepshell-d);
  height: var(--citrine-stepshell-d);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-stepshell-surface, var(--citrine-surface));
  box-shadow: inset 0 0 0 var(--citrine-stepshell-ring) var(--citrine-stepshell-rest);
}

/* A done node is a plain solid dot — the solid fill against the hollow rings is the mark. With
   `doneMark="tick"` a tick is drawn inside, in `color`. */
.citrine-stepshell-node-done .citrine-stepshell-dot {
  background: var(--citrine-stepshell-done);
  color: var(--citrine-stepshell-tick);
  box-shadow: none;
}

.citrine-stepshell-tick {
  width: var(--citrine-stepshell-tick-size);
  height: var(--citrine-stepshell-tick-size);
}

/* The current node: bigger, ringed in the accent with a soft wash of it around (and `aria-current`).
   The negative margin keeps its layout box the size of the others, so the links stay on one line; the
   halo is a shadow, outside the box, so it moves nothing either. */
.citrine-stepshell-node-now .citrine-stepshell-dot {
  width: calc(var(--citrine-stepshell-d) + var(--citrine-stepshell-dot-grow));
  height: calc(var(--citrine-stepshell-d) + var(--citrine-stepshell-dot-grow));
  margin: calc(var(--citrine-stepshell-dot-grow) / -2);
  box-shadow:
    inset 0 0 0 var(--citrine-stepshell-ring-now) var(--citrine-stepshell-now),
    0 0 0 var(--citrine-stepshell-halo) var(--citrine-stepshell-now-halo);
}

/* Names are often questions, so they wrap to two lines and clamp there. */
.citrine-stepshell-name {
  display: -webkit-box;
  max-width: 100%;
  margin-top: var(--citrine-stepshell-name-gap);
  padding-inline: var(--citrine-stepshell-name-inset);
  overflow: hidden;
  font-size: var(--citrine-stepshell-name-size);
  line-height: 1.3;
  text-align: center;
  overflow-wrap: break-word;
  color: var(--citrine-stepshell-ink-2, var(--citrine-ink-3));
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.citrine-stepshell-node-now .citrine-stepshell-name {
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-stepshell-now);
}

.citrine-stepshell-name:empty {
  display: none;
}

/* ── A STEP'S OWN COLOUR (`stepHues`, opt-in) ─────────────────────────────────────────────────────
   Only a DONE step wears its hue: its dot takes the ramp's button fill (a done node carries
   `citrine-hue-h{n}`, which sets that ramp on it), with the tick in the button ink the palette pairs
   with it; and the link running OUT of it takes the same fill. That link is drawn as the NEXT node's
   `::before`, so the component hands the next node the colour in `--citrine-stepshell-link-in`. The
   current ring and the rings ahead keep the standard look. (0,3,0) / (0,2,0) over the standard rules. */
.citrine-stepshell-node-hued.citrine-stepshell-node-done .citrine-stepshell-dot {
  background: var(--citrine-hue-btn, var(--citrine-stepshell-done));
  color: var(--citrine-hue-btn-ink, var(--citrine-stepshell-tick));
  box-shadow: none;
}

/* `currentMark="fill"`: the current step solid, no mark inside, still bigger and still haloed — in the
   accent by default, in its own hue's button fill under `stepHues`. */
.citrine-stepshell-node-nowfill .citrine-stepshell-dot {
  background: var(--citrine-stepshell-now);
  box-shadow: 0 0 0 var(--citrine-stepshell-halo) var(--citrine-stepshell-now-halo);
}

.citrine-stepshell-node-hued.citrine-stepshell-node-nowfill .citrine-stepshell-dot {
  background: var(--citrine-hue-btn, var(--citrine-stepshell-now));
  box-shadow: 0 0 0 var(--citrine-stepshell-halo) var(--citrine-hue-wash, var(--citrine-stepshell-now-halo));
}

.citrine-stepshell-node.citrine-stepshell-node-linkhued::before {
  background: var(--citrine-stepshell-link-in, var(--citrine-stepshell-done));
}

/* NO TOTAL: only the walked ground, from the left, each node at the start of its own seat, and
   nothing running on past the last one. */
.citrine-stepshell-spine-open .citrine-stepshell-node {
  flex: 0 0 auto;
  align-items: flex-start;
  min-width: var(--citrine-stepshell-seat-min);
  max-width: var(--citrine-stepshell-seat-max);
}

.citrine-stepshell-spine-open .citrine-stepshell-name {
  padding-inline: 0 var(--citrine-stepshell-head-gap);
  text-align: start;
}

.citrine-stepshell-spine-open .citrine-stepshell-node::before {
  display: block;
  left: calc(var(--citrine-stepshell-d) + var(--citrine-stepshell-link-gap));
  right: calc(-1 * var(--citrine-stepshell-link-gap));
}

.citrine-stepshell-spine-open .citrine-stepshell-node:last-child::before {
  display: none;
}

/* Too many names to read (`names="auto"`), none to draw, or `names="never"`: the names leave the row
   and stay in the tree. */
.citrine-stepshell-spine-dense .citrine-stepshell-name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ⚠ 520px IS A LITERAL because `var()` cannot appear in a container query — the same reason the
   breakpoints are `@custom-media`. Measured on a reference mockup. The nodes narrow whatever `names`
   says; only `names="auto"` lets the width take the names out (`always` keeps them, and `never` has
   already taken them out above). */
@container citrine-stepshell-spine (max-width: 520px) {
  .citrine-stepshell-nodes {
    --citrine-stepshell-d: var(--citrine-stepshell-dot-narrow);
  }

  .citrine-stepshell-spine-auto .citrine-stepshell-name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* ── the question ─────────────────────────────────────────────────────────────────────────────── */

.citrine-stepshell-title {
  margin: 0 0 6px;
  font-size: 22px;
  line-height: 1.3;
  font-weight: var(--citrine-font-weight-600, 600);
  text-wrap: balance;
  color: var(--citrine-stepshell-ink, var(--citrine-ink));
}

/* Focus arrives here by script, when the step changes; the heading is not a control. */
.citrine-stepshell-title:focus {
  outline: none;
}

.citrine-stepshell-sub {
  max-width: 70ch;
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--citrine-stepshell-ink-2, var(--citrine-ink-3));
}

.citrine-stepshell-form {
  margin: 18px 0 0;
}

/* ── the way on ───────────────────────────────────────────────────────────────────────────────── */

/* THE WAY ON SITS IN FLOW AT THE END OF THE CARD, ON A DESK (`tail="flow"`, the default): Back and
   Next where the card ends, 16px under the body, with the quiet line 14px under them. A phone keeps
   its own tail — see the card's narrow container query below. */
.citrine-stepshell-tail {
  margin: var(--citrine-stepshell-tail-gap) 0 0;
}

/* `tail="sticky"` — THE WAY ON STAYS IN VIEW, ON A DESK (the 4.x tail). A section is as long as the
   flow makes it, and a 1496px step was once measured with its own last word below the fold at 1366,
   1440 AND 1920, so neither the action nor its held state could be read at all. `position: sticky`
   costs a short step nothing: it pins only while the tail would otherwise have scrolled away, and
   settles into its own place at the end of the card.

   It spans the card's padding (the negative inline margins) so the ground behind it reaches the
   edges, and carries a hairline because it is drawing over the body's own words. Desk only — the
   container query keeps it off the phone, whose tail is static unless `tailOffset` pins it. */
@container citrine-stepcard (min-width: 480px) {
  .citrine-stepshell-tail-sticky {
    position: sticky;
    inset-block-end: 0;
    z-index: 1;
    margin: var(--citrine-sp-5) calc(-1 * var(--citrine-sp-6)) 0;
    padding: var(--citrine-sp-3) var(--citrine-sp-6) 0;
    border-block-start: 1px solid var(--citrine-stepshell-split, var(--citrine-line));
    background: var(--citrine-stepshell-surface, var(--citrine-surface));
  }

  /* Its own floor, or the pinned block ends flush with the last thing in it and the card's padding
     shows through under it. */
  .citrine-stepshell-tail-sticky > .citrine-stepshell-foot,
  .citrine-stepshell-tail-sticky > .citrine-stepshell-actions:last-child {
    padding-block-end: var(--citrine-sp-3);
  }
}

.citrine-stepshell-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.citrine-stepshell-next {
  margin-inline-start: auto;
}

/* `tailOffset="bottombar"`: the offset is the app frame's own measure of its bar, not a number the
   caller carries. `AppShell` exposes it as a declared token (`--citrine-app-bottom-inset`, 0px
   wherever no bar is showing), so this sheet names no class of the bar's. It only bites where a
   rule below pins the tail — on a phone. */
.citrine-stepshell-tailbar {
  --citrine-stepshell-tail-offset: var(--citrine-app-bottom-inset);
}

.citrine-stepshell-foot {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--citrine-stepshell-ink-2, var(--citrine-ink-3));
}

/* ── one section, `soloActions="bar"`: the way on in a bar of its own ────────────────────────────
   Not inside the card: under it, in the card's column, the primary first. It is the grid's last item, so on one column — a phone — it comes after the
   aside; beside the aside, the aside spans both rows so the bar sits right under the card rather
   than under whichever of the two is taller. */
.citrine-stepshell-tail.citrine-stepshell-solobar {
  grid-column: 1;
  margin: calc(var(--citrine-stepshell-solo-gap) - var(--citrine-sp-4)) 0 0;
}

.citrine-stepshell-solobar .citrine-stepshell-next {
  margin-inline-start: 0;
}

@container citrine-stepshell (min-width: 840px) {
  .citrine-stepshell-solo .citrine-stepshell-aside {
    grid-row: span 2;
  }
}

@container citrine-stepshell (max-width: 479px) {
  .citrine-stepshell-solobar .citrine-stepshell-actions {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .citrine-stepshell-solobar .citrine-stepshell-actions > .citrine-btn {
    width: 100%;
  }

  /* Pinned on a phone only when the app says what sits at the foot of its screen (`tailOffset`), as
     the card's own tail is. Then it is a real bar and spans the page's full width: the app's content
     element exposes its side padding as `--citrine-app-gutter`, and the bar bleeds by exactly that. */
  .citrine-stepshell-tailpin .citrine-stepshell-solobar {
    position: sticky;
    inset-block-end: var(--citrine-stepshell-tail-offset, 0px);
    z-index: 1;
    margin-inline: calc(-1 * var(--citrine-app-gutter, 0px));
    padding: var(--citrine-sp-3) var(--citrine-app-gutter, 0px);
    border-block-start: 1px solid var(--citrine-stepshell-split, var(--citrine-line));
    background: var(--citrine-stepshell-surface, var(--citrine-surface));
  }

  /* Standing on the app's bar, the bar's ground reaches down to it over the hero's rise — see the
     card's tail below for why. */
  .citrine-stepshell-tailbar .citrine-stepshell-solobar {
    padding-block-end: calc(var(--citrine-sp-3) + var(--citrine-app-bottom-rise));
  }

  /* `backNarrow="icon"` in the bar — the same row as the card's, below. */
  .citrine-stepshell-backicon .citrine-stepshell-solobar .citrine-stepshell-actions {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
  }

  .citrine-stepshell-backicon
    .citrine-stepshell-solobar
    .citrine-stepshell-actions
    > .citrine-stepshell-back {
    flex: none;
    width: var(--citrine-target-min);
    min-width: var(--citrine-target-min);
    min-height: var(--citrine-target-min);
  }

  .citrine-stepshell-backicon
    .citrine-stepshell-solobar
    .citrine-stepshell-actions
    > .citrine-stepshell-next {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    min-height: var(--citrine-target-min);
  }

  .citrine-stepshell-backicon .citrine-stepshell-solobar .citrine-stepshell-backlabel {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* ── a thumb's target for the two controls antd draws small ───────────────────────────────────────
   A flow section asks real questions with both — "Is this offered in separate parts?", the
   amenities list — and they drew at 24px and 14px (finish review, fix 3). The SIZES are antd tokens,
   scoped by the shell's own ConfigProvider; what has to happen here is lifting the kit's own 42×24
   pin on the track, because no token outranks a width. The TARGET is the row: antd keeps the words
   inside the `<label>`, so 44px of row is 44px of target. */
/* ⚠ LIFT THE PIN TO A SIZE, NEVER TO `auto`. `min-width: auto` here outranks antd's own
   `min-width: <trackMinWidth>` as well as the kit's pin, so the track collapses onto its handle and
   every toggle draws as a bare circle that reads as an unselected radio (round 3 of the finish
   review, `refer-1440-light-10-step.png`). The numbers come from `StepShell.tsx`'s `SWITCH_TRACK`,
   which is also what the scoped antd theme is given — one source, so the track and its handle can
   never disagree. 44×22 is the reference mockup and antd's stock shape alike. */
.citrine-stepshell .citrine-switch-md {
  min-width: var(--citrine-stepshell-switch-w, 44px);
  width: auto;
  height: var(--citrine-stepshell-switch-h, 22px);
}

.citrine-stepshell .citrine-switch-wrap,
.citrine-stepshell .citrine-check {
  align-items: center;
  min-height: 44px;
}

/* A switch asked as its own QUESTION has no row to grow: `FieldShell` puts the label above it, so
   `Switch` renders the bare track and the 44px rule above has nothing to apply to. The track stays
   the comp's 32px and the TARGET is pushed out to 44 with a pseudo-element — 6px of reach above and
   below, invisible, and a press anywhere in it lands on the switch.

   Both pseudo-slots on the track's root are free: antd puts its own `::before` on the HANDLE
   (`switch/style/index.js:123`) and its `overflow: hidden` on the INNER (`:169`), so nothing here is
   overwritten and nothing clips the reach. Checked in antd 6.2.3's own source, not assumed. */
.citrine-stepshell .citrine-switch {
  position: relative;
  /* The reach is taken from the FLOW as well as painted, so the pad never lands underneath a
     neighbouring field. Without this the probe measured a full target on a switch with room around
     it and a short one on three that sat inside a two-column claim section — the pad was there and
     the next field's label was drawn over it. */
  margin-block: var(--citrine-stepshell-switch-reach, 11px);
}

.citrine-stepshell .citrine-switch::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block: calc(-1 * var(--citrine-stepshell-switch-reach, 11px));
  /* Above whatever is painted beside it. Safe only BECAUSE of the margin above: the band this covers
     is space the switch reserved for itself, so the pad can never take a press meant for a
     neighbour. Without it the probe measured the full target on two switches and the bare track on
     three more, in the same section — the difference was only which sibling happened to paint last. */
  z-index: 1;
}

@container citrine-stepcard (max-width: 479px) {
  .citrine-stepshell-main {
    padding: 16px;
  }

  /* THE PHONE'S OWN TAIL, whatever `tail` says on a desk: the look it has always had here — spanning the card's 16px padding, a hairline over it, the card's ground under it, and a
     floor of its own under the last thing in it. (The one-section bar sits outside the card, so none
     of this reaches it.) */
  .citrine-stepshell-tail {
    z-index: 1;
    margin: 20px -16px 0;
    padding: 12px 16px 0;
    border-block-start: 1px solid var(--citrine-stepshell-split, var(--citrine-line));
    background: var(--citrine-stepshell-surface, var(--citrine-surface));
  }

  .citrine-stepshell-tail > .citrine-stepshell-foot,
  .citrine-stepshell-tail > .citrine-stepshell-actions:last-child {
    padding-block-end: 12px;
  }

  .citrine-stepshell-title {
    font-size: 20px;
  }

  /* ⚠ NOT STICKY ON A PHONE, and that is the whole of it. The foot of a phone's view belongs to the
     app: `BottomBar` sits there with a raised hero rising above it, and a tail pinned to
     `inset-block-end: 0` lands underneath both — round 3 of the finish review caught the primary
     clipped at the edge with the hero drawn over its centre, and Back gone entirely
     (`refer-390-light-08-step.png`).

     Offsetting the tail by the bar's height was the alternative and it is worse: this shell would
     have to know what furniture the app puts at the foot of its screens, which is a fact no kit
     component can hold. On a phone the actions are already where the reference puts them: at the
     end of the card, full width, the primary over Back. (`tail="sticky"` does not reach here: its
     rule is gated to a card of 480px and up — see "the way on" above.) */
  .citrine-stepshell-tail {
    position: static;
  }

  /* …UNLESS THE APP SAYS WHAT IS DOWN THERE (2026-09-14). Everything above still holds: the shell
     cannot know the furniture, so it does not guess. It is now given the number. `tailOffset` sets
     `--citrine-stepshell-tail-offset` and this class, and the tail pins just above whatever the app
     named. Callers who say nothing keep the static tail exactly as it is.

     The cost of the static tail was real on the other side — a form's primary action measured at
     y 1020.7 in an 844px viewport, 758px of scrolling from rest — which is why the third option
     (raise it, and let the app supply the one fact) beat both of the first two. */
  .citrine-stepshell-tailpin .citrine-stepshell-tail {
    position: sticky;
    inset-block-end: var(--citrine-stepshell-tail-offset, 0px);
  }

  /* …OR SAYS "THE BAR" (`tailOffset="bottombar"`), and the frame supplies the number. The tail stands
     on the bar's top edge (`--citrine-app-bottom-inset`, set at the top of this sheet). A `BottomBar`
     hero stands up out of that edge, by `--citrine-app-bottom-rise`, and at the edge alone it would be
     drawn over the primary's centre — exactly round 3's defect. Standing the tail higher by the rise
     instead left a strip of page scrolling by between the tail and the bar. So the rise goes into the
     tail's own floor: its ground reaches the bar, the circle nests in it, and the actions sit clear
     above. 0 with no hero, so a bar without one costs the tail nothing. */
  .citrine-stepshell-tailbar .citrine-stepshell-tail > .citrine-stepshell-foot,
  .citrine-stepshell-tailbar .citrine-stepshell-tail > .citrine-stepshell-actions:last-child {
    padding-block-end: calc(12px + var(--citrine-app-bottom-rise));
  }

  /* The primary on top, where the thumb is; Back under it. DOM order stays Back, then the primary. */
  .citrine-stepshell-actions {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .citrine-stepshell-actions > .citrine-btn {
    width: 100%;
    margin-inline-start: 0;
  }

  /* `backNarrow="icon"` — BACK AS ITS ARROW, beside the primary. One row, not the stack: Back a
     44px square at the start (the target floor, whatever height the device step gives the
     controls), the primary taking the rest, so the thumb's action keeps nearly the whole width. The
     words are clipped, not removed — they are still the button's name. */
  .citrine-stepshell-backicon .citrine-stepshell-actions {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
  }

  .citrine-stepshell-backicon .citrine-stepshell-actions > .citrine-stepshell-back {
    flex: none;
    width: var(--citrine-target-min);
    min-width: var(--citrine-target-min);
    min-height: var(--citrine-target-min);
  }

  .citrine-stepshell-backicon .citrine-stepshell-actions > .citrine-stepshell-next {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    min-height: var(--citrine-target-min);
  }

  .citrine-stepshell-backicon .citrine-stepshell-backlabel {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* ── beside the step ──────────────────────────────────────────────────────────────────────────── */

.citrine-stepshell-aside {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

/* ── a named group inside one step ────────────────────────────────────────────────────────────── */

.citrine-stepgroup-title {
  margin: 0;
  font-size: 17px;
  line-height: 1.35;
  font-weight: var(--citrine-font-weight-600, 600);
  color: var(--citrine-stepshell-ink, var(--citrine-ink));
}

.citrine-stepgroup-sub {
  max-width: 70ch;
  margin: 4px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--citrine-stepshell-ink-2, var(--citrine-ink-3));
}

.citrine-stepgroup-body {
  margin-top: 12px;
}

.citrine-steptrail-list {
  margin: 0;
}

.citrine-steptrail-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--citrine-steptrail-line, var(--citrine-line));
  font-size: 14px;
  line-height: 1.45;
}

.citrine-steptrail-row:first-child {
  padding-top: 0;
}

.citrine-steptrail-row:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.citrine-steptrail-row dt {
  flex: none;
  max-width: 50%;
  color: var(--citrine-steptrail-ink-2, var(--citrine-ink-3));
}

.citrine-steptrail-row dd {
  min-width: 0;
  margin: 0;
  text-align: end;
  font-weight: var(--citrine-font-weight-600, 600);
  overflow-wrap: anywhere;
  color: var(--citrine-steptrail-ink, var(--citrine-ink));
}

/* THE CURRENT ROW. The approved comp tints the section the form is sitting on. The tint is only half
   of it — `aria-current="step"` carries the same fact to assistive tech, because colour as the sole
   carrier is exactly the WCAG 1.4.1 failure this kit avoids everywhere else. */
.citrine-steptrail-row-current {
  background: var(--citrine-steptrail-current-bg, var(--citrine-surface-3));
}

/* A ROW STILL WAITING. Its `value` holds a status ("Still needed", "Optional", "Nothing to fill"),
   not an answer, so it reads quieter than a real one — otherwise a column of statuses looks exactly
   like a column of answers. Only the value moves; the label keeps its own ink so the list of
   sections stays scannable. */
.citrine-steptrail-row-awaiting dd {
  color: var(--citrine-steptrail-awaiting-ink, var(--citrine-ink-3));
}

/* `marks` — A MARK PER ROW (opt-in). A ticked disc for a row that holds an answer, an empty ring for
   one still waiting. It sits INSIDE the `dt`, held in the label's own padding, rather than as a flex
   child of the row: the row aligns its term and answer on the text baseline, and a mark has none, so
   as a sibling it would drag the row's alignment. Here the baseline is untouched and a label that
   wraps hangs clear of the mark. Everything is scoped under `.citrine-steptrail-marks`, so a trail
   without the prop keeps every measure it had. The mark is `aria-hidden`: the row's words say it. */
.citrine-steptrail-marks dt {
  position: relative;
  padding-inline-start: calc(var(--citrine-stepshell-mark) + var(--citrine-stepshell-mark-gap));
}

/* Centred on the label's FIRST line (`1lh`), as the reference draws it, however many lines follow. */
.citrine-steptrail-mark {
  position: absolute;
  top: calc((1lh - var(--citrine-stepshell-mark)) / 2);
  inset-inline-start: 0;
  display: grid;
  place-items: center;
  width: var(--citrine-stepshell-mark);
  height: var(--citrine-stepshell-mark);
  border-radius: var(--citrine-r-pill);
}

.citrine-steptrail-mark-waiting {
  box-shadow: inset 0 0 0 var(--citrine-stepshell-mark-ring) var(--citrine-stepshell-mark-rest);
}

/* The spine's done node in small: the walked colour's start under the spine's tick colour, a pair
   already measured to read in both themes. Shape and fill both differ from the ring, not a shade. */
.citrine-steptrail-mark-done {
  background: var(--citrine-stepshell-mark-done);
  color: var(--citrine-stepshell-tick);
}

.citrine-steptrail-mark .citrine-stepshell-tick {
  width: var(--citrine-stepshell-mark-tick-size);
  height: var(--citrine-stepshell-mark-tick-size);
}

/* ── StickyPane ────────────────────────────────────────────────────────────────────────── */
/* UI kit — StickyPane. Pinned under the top bar, no taller than the window, scrolling its own
   content. `align-self: start` so a grid or flex row does not stretch it to the page's height (a
   stretched box has nothing to stick with). `overscroll-behavior: contain` keeps a wheel at the
   rail's end from scrolling the page underneath. */

.citrine-stickypane {
  /* Room for what a control draws OUTSIDE its own box — antd's focus ring reaches 4px out (a 3px
     line, 1px off the edge). The rail clips at its edges, so a search box or a button flush with
     one lost its ring on that side: a focused field showed a ring along its bottom only. The body
     below is padded by this much and the rail reaches out by the same, so the content sits where
     it always did, and the ring is drawn inside the clip. */
  --citrine-stickypane-ring: 4px;
  position: sticky;
  top: calc(var(--citrine-stickypane-top, 16px) - var(--citrine-stickypane-ring));
  align-self: start;
  margin: calc(-1 * var(--citrine-stickypane-ring));
  max-block-size: calc(
    100dvh - var(--citrine-stickypane-top, 16px) - var(--citrine-stickypane-gap, 16px) + 2 *
      var(--citrine-stickypane-ring)
  );
  overflow-y: auto;
  /* A rail scrolls DOWN, never sideways. `overflow-y: auto` alone turns the other axis to `auto`
     too, so anything poking past the rail's edge — a small icon button's invisible 44px press
     target at the end of a header row reaches ~10px out — made the whole rail scroll sideways and
     its content sit off-centre. `hidden` stops a person scrolling it; the body below stops there
     being anything to scroll at all (a focus or `scrollIntoView` still could, under `hidden`). */
  overflow-x: hidden;
  /* Whether the rail is taller than the window depends on the zoom, and a desk browser's scrollbar
     takes its width out of the rail only while it shows — so the content's width and its spacing to
     either side changed from one zoom level to the next. The gutter is held either way. */
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
}

/* The content's own box clips sideways (`clip`, which makes no scroll box of its own), so nothing
   in it adds width the rail could scroll to. Vertically it is untouched: the rail scrolls it. It
   clips at its padding's outer edge, so the padding is where a focus ring has room. */
.citrine-stickypane-body {
  padding: var(--citrine-stickypane-ring);
  overflow-x: clip;
}

/* ── Switch ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Switch styles.

   RULING D14 (Group A audit, finding F-A6, 2026-09-10): the track colours are gone.
   `.citrine-switch { background: var(--citrine-line-2) }` and
   `.citrine-switch.citrine-switch-is-on { background: var(--citrine-accent) }` repainted antd's own
   track, which already runs `colorTextQuaternary` → `colorPrimary`. Two declarations duplicating
   what antd draws anyway, so they went.

   THE SIZE PINS BELOW STAY, and that is a judgement call written down rather than made silently.
   `-md` is 42×24 against antd's own 44×22. That is a dimension, not a colour, and it sits close
   enough to antd's that changing it shifts the layout of every form row a switch sits in — for a
   two-pixel gain in stock-ness. Kept for the same reason, and in the same words, as the `TriState`
   changeset kept its own 28px-vs-26px delta. If a screenshot pass decides otherwise, this is one
   rule to delete. The pin holds under a `componentSize="small"` provider only because `Switch.tsx`
   resets antd's size context round the track: antd's `.ant-switch.ant-switch-small` would otherwise
   outrank this one-class rule on height, knob and margins.

   `-sm` (32×18) is DEAD as shipped, and was left alone deliberately: `Switch.tsx:46` applies
   `citrine-switch-md` unconditionally and never emits `-sm`, so nothing in the kit can reach it. It
   stays because a consumer may still apply the class through `className`, which makes it a public
   hook rather than dead code — but it is the one rule in this file that nothing renders.

   The wrap, label and disabled rules are Citrine's OWN markup: `Switch.tsx` renders its own
   `<label>` around antd's track whenever a `label` is given, and antd has no equivalent. */

.citrine-switch-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  cursor: pointer;
  font-size: var(--citrine-font-size-13p5);
  color: var(--citrine-ink);
}

/* `labelPosition="above"`: the label over the track, spaced like a form field's label — and, like a
   field, the field rhythm below it (antd's `Form.Item` margin), so a form ending on a switch keeps
   the same air under it as one ending on an input. */
.citrine-switch-wrap.citrine-switch-above {
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: var(--ant-form-item-margin-bottom, 24px);
}

/* `help`: a quiet line under the switch. With it the outer box is the field — it takes the field
   rhythm, and the label row inside gives its own back. */
.citrine-switch-field {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--citrine-sp-2);
}
.citrine-switch-field.citrine-switch-field-above {
  margin-bottom: var(--ant-form-item-margin-bottom, 24px);
}
.citrine-switch-field > .citrine-switch-wrap.citrine-switch-above {
  margin-bottom: 0;
}
.citrine-switch-help {
  margin: 0;
  font-size: var(--citrine-font-size-12p5);
  color: var(--citrine-ink-3);
}
.citrine-switch-above .citrine-switch-label {
  font-size: var(--citrine-font-size-14, 14px);
}

.citrine-switch-label {
  font-size: var(--citrine-font-size-13p5);
  color: var(--citrine-ink);
}

.citrine-switch-is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.citrine-switch-md {
  min-width: 42px;
  width: 42px;
  height: 24px;
}
/* `onText` / `offText`: the track grows to fit its words ("OFF" was clipped to "OF" at 42px), never
   narrower than the bare track. */
.citrine-switch-md.citrine-switch-has-text {
  width: auto;
}

.citrine-switch-sm {
  min-width: 32px;
  width: 32px;
  height: 18px;
}

/* ── Table ─────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Table. Source: artifact `.tblwrap`, `.rowlead`, `.rowav`, `.rowmain`, `.rowsub`.

   RULING D14 (Group A audit, finding F-A4, 2026-09-10): the table repaint is gone. What went, and
   what each rule was doing:

     `.citrine-table.citrine-table thead th` — the class written TWICE, purely to out-rank antd's own
       header rule. It set uppercase, `letter-spacing: 0.07em`, an 11px size, weight 700,
       `var(--citrine-ink-4)`, a `var(--citrine-surface-2)` background and its own bottom border. A
       doubled selector is a confession: antd was already drawing a header, and this painted over it.
     `.citrine-table.citrine-table tbody td` — the same doubling, with a LITERAL `13px` padding (the
       one hardcoded pixel value in the file), its own font size, line height and bottom border.
     `.citrine-table tbody tr:last-child td` — existed only to undo the bottom border above it.
     `.citrine-table tbody tr:hover` — antd already paints a row hover from `colorBgTextHover`.
     `.citrine-table table { border-collapse: collapse }` — the quietest of them and worth naming:
       antd's table is `border-collapse: separate; border-spacing: 0`, which is what its bordered and
       sticky-header rendering rests on. Forcing `collapse` was not a colour change at all.
     `.citrine-table { font-size: … }` — a 13px type override on antd's own 14.
     `.citrine-table-tight tbody td` — RETARGETED rather than deleted. `density="tight"` is a real
       Citrine feature, so `Table.tsx` now passes antd's own `size="small"` for it. Deleting the rule
       without that would have left `density` declared, accepted, compiling green and doing nothing —
       the exact bug class this branch has already fixed five times.

   KEPT DELIBERATELY, each for a reason rather than by omission:

     `.citrine-tblwrap` — Citrine's OWN `<div>`, not an antd node. Its `overflow: hidden` +
       `overflow-x: auto` pair is load-bearing (its own comment below says why), and its border and
       radius frame Citrine's wrapper rather than antd's table.
     `.citrine-table { width: 100% }` — layout, and antd's wrapper is width-100% anyway.
     `.citrine-table tbody tr.citrine-is-selected` — **the one override in this file I could not
       clear, kept on purpose.** antd paints `.ant-table-row-selected` itself, but ONLY from
       `rowSelection`, which `Table.tsx` builds only when `selectable` is true. `rowClassName` applies
       `.citrine-is-selected` from `selectedKeys` REGARDLESS of `selectable` — so a caller passing
       `selectedKeys` without `selectable` has no antd paint at all, and stripping this would
       silently lose their highlight. A wrongly kept override costs a pixel; a wrongly stripped one
       costs behaviour nothing here can see.
     `.citrine-table-clickable tbody tr:focus-visible` — WCAG 2.1.1. Substance, not skin.
     `.citrine-num` / `-acts` / `-k` / `-rowlead` / `-rowav` / `-rowmain` / `-rowsub` — helpers for
       consumer-authored cell content and for `RowLead`'s own markup. Citrine's, not antd's.

   Every class above is still emitted; the stripped ones simply carry no declaration of their own. */

.citrine-tblwrap {
  border-radius: var(--citrine-r-sm);
  border: 1px solid var(--citrine-line);
  /* `hidden` first, so the rounded corner still clips vertically; then X released to scroll.
     WHY THIS IS NOT JUST `hidden`: `thead th` below sets `white-space: nowrap`, so a table with
     enough columns CANNOT squeeze to fit a narrow viewport — it overflows. Under plain
     `overflow: hidden` that overflow was CLIPPED with no scrollbar, which means columns a phone user
     could never reach and no indication they existed. Scrolling is the honest answer for a table
     that is genuinely wider than the screen. */
  overflow: hidden;
  overflow-x: auto;
}

.citrine-table {
  width: 100%;
}

/* A CELL WHOSE ONLY CONTENT IS A LINK IS A TARGET, not a line of text that happens to be clickable.
   Measured on a consumer's deals list: the six property-name links were **17px** boxes with no
   stretched hit area, inside 62px rows — under the approved comp's own 25px, and well under what
   this kit already gives the same link on a phone, where `.citrine-ledger-link::after` reaches
   64px. The desk table had no equivalent, so the same record was a comfortable target in one layout
   and a sliver in the other.

   `:only-child` is the whole guard and it is deliberate. A cell holding a link AND something else —
   two links, a link beside a chip, a link followed by a note — must NOT have one of them silently
   swallow the entire cell, so the rule simply does not apply there. A cell that is nothing but its
   link has no such ambiguity: the cell IS the link. */
.citrine-table tbody td:has(> a:only-child) {
  position: relative;
}
.citrine-table tbody td > a:only-child::after {
  content: '';
  position: absolute;
  inset: 0;
}

.citrine-table tbody tr.citrine-is-selected {
  background: var(--citrine-data-wash);
}

/* `highlightedKey`: one row the screen points back at. After `is-selected`, so a row that is both
   reads as highlighted — the more recent signal. */
.citrine-table tbody tr.citrine-is-highlighted {
  background: var(--citrine-table-row-highlight);
}

.citrine-table-clickable tbody tr {
  cursor: pointer;
}

/* FIX 1 (WCAG 2.1.1): clickable rows gain `tabIndex={0}` in Table.tsx, so they need a visible
   keyboard-focus indicator too — matched to the kit's existing `:focus-visible` pattern
   (`Chip.css` `.citrine-chip-close:focus-visible`), not invented. */
.citrine-table-clickable tbody tr:focus-visible {
  outline: 2px solid var(--citrine-data);
  outline-offset: -2px;
}

/* Column-content helpers — apply as a column's own `className`. */
.citrine-num {
  display: block;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.citrine-acts {
  display: flex;
  justify-content: flex-end;
  gap: var(--citrine-sp-2);
  white-space: nowrap;
}

.citrine-k {
  font-family: var(--citrine-mono);
  font-size: var(--citrine-font-size-11p5);
  color: var(--citrine-ink-2);
}

/* RowLead — avatar + main/sub line pair. */
.citrine-rowlead {
  display: flex;
  align-items: center;
  gap: var(--citrine-space-11);
}

.citrine-rowav {
  width: 34px;
  height: 34px;
  border-radius: var(--citrine-r-xs);
  flex: none;
  display: grid;
  place-items: center;
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-700);
  background: var(--citrine-data-wash);
  /* THE DEEPER DATA STEP, not the text-safe one — measured. `--citrine-data` is chosen to be
     text-safe against the SURFACE (5.02:1 on white) and this tile is not on the surface, it is on
     the data wash, where the same green measures **4.47:1** at 12px/700. Just under, and it drew
     that way on thirteen rows of a consuming app. `--citrine-data-2` is the palette's own deeper
     step and exists for this: 6.8:1 on the light wash and 9.4:1 on the dark one. Same fix as the
     StageStrip chip, one component along. */
  color: var(--citrine-data-2, var(--citrine-data));
}

.citrine-rowmain {
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-650);
}

.citrine-rowsub {
  font-size: var(--citrine-font-size-11p5);
  color: var(--citrine-ink-3);
}

/* ── THE HEADER, AND ONLY THE HEADER ─────────────────────────────────────────────────────────────
   Ruling D14 removed a Citrine header repaint that shouted over its own rows — uppercase, 11px,
   700 — and the file header above still quotes it so nobody restores it by accident. This is NOT
   that rule coming back. The 2026-09-14 ruling fixes the opposite fault: stock antd draws the
   header at body size in near-black, so nothing separates the column names from the data.

   Size only lives here. The ink and the ground are antd's stock header (5.0.0: the transparent,
   muted header of 4.x was one consumer's look and became opt-in — an app restores it with
   `CitrineProvider antdTheme={{ components: { Table: { headerBg, headerColor } } }}`). antd has no
   header-only font-size token — `cellFontSize` moves the body with it — which is the one reason
   this selector exists at all. The body is left entirely alone. */
.citrine-tblwrap .citrine-table thead th {
  font-size: var(--citrine-font-size-13, 13px);
}

/* ── frame="none": no box of its own, the header on the surface at body weight ─────────────────
   For a table that is the whole of a card's body. antd paints the header ground and weight on its
   own `th`, so the two values are retuned through antd's own variables on the wrapper (inherited by
   the table's root), and the weight — which antd takes from the global `fontWeightStrong` with no
   per-table token — by the wrapper's two own classes, which out-rank antd's `th` rule. */
.citrine-tblwrap.citrine-tblwrap-bare {
  border: 0;
  border-radius: 0;
}
/* The header takes the SURFACE, not no ground: on a card it reads the same, and a pinned column's
   label (`fixed`) sits over the labels scrolling under it — with no ground they showed through it
   ("Company Name" printed under "Actions"). */
.citrine-tblwrap-bare [class*='css-var-'] {
  --ant-table-header-bg: var(--citrine-surface);
  /* The pager row sits on the card too, under a hairline, not on a grey band. */
  --ant-table-footer-bg: transparent;
}
.citrine-tblwrap.citrine-tblwrap-bare .citrine-table thead tr th {
  background: var(--citrine-surface);
  font-weight: 400;
}

/* A bare table runs to the card's edge, so its first and last cells keep the card's own inset: the
   tick (or the first label) sits in line with the toolbar above it instead of against the card's
   border, and the last column's actions keep the same room on the right. The tick column is widened
   to match in `Table.tsx` (`columnWidth`). One class over antd's selection-column padding rule. */
.citrine-tblwrap-bare {
  --citrine-table-edge-inset: 24px;
}
.citrine-tblwrap.citrine-tblwrap-bare .citrine-table .ant-table-thead > tr > :first-child,
.citrine-tblwrap.citrine-tblwrap-bare .citrine-table .ant-table-tbody > tr > :first-child {
  padding-inline-start: var(--citrine-table-edge-inset);
}
.citrine-tblwrap.citrine-tblwrap-bare .citrine-table .ant-table-thead > tr > :last-child,
.citrine-tblwrap.citrine-tblwrap-bare .citrine-table .ant-table-tbody > tr > :last-child {
  padding-inline-end: var(--citrine-table-edge-inset);
}

/* `head="tinted"`: the label row on the page hue's wash in its deep ink, at medium weight — the
   column labels read apart from the rows. The washes are opaque, so a pinned label row still hides
   the rows scrolling under it. After the bare rule, at its weight, so it wins in either frame. */
.citrine-tblwrap-head-tinted [class*='css-var-'] {
  --ant-table-header-bg: var(--citrine-hue-wash, var(--citrine-accent-wash));
  --ant-table-header-color: var(--citrine-hue-deep, var(--citrine-accent-deep));
}
.citrine-tblwrap.citrine-tblwrap-head-tinted .citrine-table thead tr th {
  background: var(--citrine-hue-wash, var(--citrine-accent-wash));
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
  font-weight: 500;
}

/* ── sticky: the labels pinned under the top bar, the scrollbar pinned to the window's bottom ─────
   A sticky element sticks inside its nearest CLIPPING ancestor, and this frame clips (`overflow`
   above, for its rounded corner and narrow-screen scroll) — so it lets go while the table is sticky;
   antd's own body is the sideways scroller then. (The labels sit over the scrolling rows; a bare
   frame's labels already wear the surface — see above.) */
.citrine-tblwrap.citrine-tblwrap-sticky {
  overflow: visible;
}

/* ── narrow="cards": each row a bordered card on a phone (opt-in) ────────────────────────────────
   On a desk this is the ordinary table. The per-cell labels `Table.tsx` renders are not drawn and,
   being `display: none`, not read either: the header names each column there.

   At and below `sm` the header row goes, each row becomes a card with its cells stacked, and each
   value sits under its column's title. The parts keep their table roles whatever `display` they take
   (see `CARD_COMPONENTS` in `Table.tsx`), and the label is real text, so a screen reader still hears
   which column a value belongs to once the header is gone.

   Every selector is a Citrine class `Table.tsx` puts on the node (`onCell`, `onHeaderRow`,
   `rowClassName`, the wrapper), never an `.ant-*` one. Three classes deep so the cell's padding and
   edge out-rank antd's own cell rule without doubling a class. The wrapper drops its frame there:
   each card draws its own, and a frame around a stack of frames is a box in a box. */
.citrine-table-card-label {
  display: none;
}

/* The card form wraps each value in its own span, so the link-only cell above no longer matches.
   The same rule, one level in: a value that is nothing but its link makes the whole cell the target,
   in both forms. The `position: relative` mirrors the rule above; antd 6.2.3 already positions its
   cells (removing it changes nothing a test can see), so it is a guard against an antd upgrade that
   stops doing so, not the thing that holds the reach to the cell today. */
.citrine-table-cards .citrine-table-card-cell:has(> .citrine-table-card-value > a:only-child) {
  position: relative;
}
.citrine-table-cards .citrine-table-card-value > a:only-child::after {
  content: '';
  position: absolute;
  inset: 0;
}

@media (max-width: 820px) {
  .citrine-tblwrap.citrine-tblwrap-cards {
    border: 0;
    border-radius: 0;
    overflow: visible;
  }

  .citrine-tblwrap-cards .citrine-table-cards table {
    display: block;
  }

  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-head {
    display: none;
  }

  .citrine-tblwrap-cards .citrine-table-cards table > tbody {
    display: block;
  }

  /* A margin between cards rather than a `gap` on the body: antd also renders its own zero-height,
     aria-hidden measure row in the body, and a gap would open space around it too. */
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card + .citrine-table-card {
    margin-block-start: var(--citrine-table-card-gap);
  }

  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card {
    display: grid;
    gap: var(--citrine-table-card-cell-gap);
    padding: var(--citrine-table-card-pad);
    border: var(--citrine-table-card-edge-width) solid var(--citrine-table-card-edge);
    border-radius: var(--citrine-table-card-radius);
    /* Each record a raised card of its own, on the page's ground below (the lead list's look). */
    background: var(--citrine-surface);
    box-shadow: var(--citrine-sh);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card.citrine-is-selected {
    background: var(--citrine-data-wash);
  }

  /* The cards lie on the PAGE's ground, not on the white of the card the list sits in: white cards
     edged in a hairline on white read as one long block, with nothing between one record and the
     next. antd's own table ground is cleared so the wrapper's shows through. */
  .citrine-tblwrap.citrine-tblwrap-cards {
    padding: var(--citrine-sp-3, 12px);
    border-radius: var(--citrine-table-card-radius);
    background: var(--citrine-page);
  }
  .citrine-tblwrap-cards .citrine-table-cards .ant-table {
    background: transparent;
  }

  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-cell {
    display: block;
    padding: 0;
    border: 0;
  }

  /* A column's `align` reaches the cell as an inline `text-align` (rc-table writes it as a style),
     which no stylesheet rule on the cell can beat. In a card every value starts at the card's edge,
     so the start alignment goes on the label and the value inside it instead. */
  .citrine-tblwrap-cards .citrine-table-cards :is(.citrine-table-card-label, .citrine-table-card-value) {
    display: block;
    text-align: start;
  }

  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-label {
    font-size: var(--citrine-table-card-label-size);
    color: var(--citrine-table-card-label-ink);
  }

  /* The tick is the card's first line: the box alone at the card's start, with none of the table
     form's padding, centring or rule under it (which drew an empty boxed band atop every card). */
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-tick {
    display: block;
    width: auto;
    padding: 0;
    border: 0;
    text-align: start;
  }

  /* ── The record card (`CitrineColumn.card`) ─────────────────────────────────────────────────────
     First line: the tick, the TITLE (bold, its label kept for screen readers only) and the ACTIONS at
     the end, over a rule; under it each FIELD as one line, label at the start and value at the end.
     The rule is the card's own `::after`, placed on row 2 — a grid places it before the fields, which
     flow on from row 3. Margins, not padding, space the first line: the bare frame's edge rule owns
     the first and last cells' inline padding. */
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card {
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 0;
    align-items: center;
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card::after {
    content: '';
    grid-row: 2;
    grid-column: 1 / -1;
    border-block-start: 1px solid var(--citrine-table-card-edge);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-tick {
    grid-row: 1;
    grid-column: 1;
    margin-inline-end: var(--citrine-sp-3, 12px);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-title {
    grid-row: 1;
    grid-column: 2;
    display: flex;
    align-items: center;
    gap: var(--citrine-sp-2, 8px);
    min-width: 0;
    font-weight: var(--citrine-font-weight-600, 600);
    color: var(--citrine-ink);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-actions {
    grid-row: 1;
    grid-column: 3;
    margin-inline-start: var(--citrine-sp-2, 8px);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-field {
    grid-column: 1 / -1;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--citrine-sp-3, 12px);
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-field > .citrine-table-card-label {
    flex: 0 1 auto;
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-field > .citrine-table-card-value {
    min-width: 0;
    text-align: end;
    overflow-wrap: anywhere;
  }
  .citrine-tblwrap-cards .citrine-table-cards .citrine-table-card-hidden {
    display: none;
  }
  .citrine-tblwrap-cards
    .citrine-table-cards
    :is(.citrine-table-card-title, .citrine-table-card-actions)
    > .citrine-table-card-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* antd grounds each CELL on hover and on selection. Stacked in a card that is a grey block per
     field — and on a phone the hover sticks after a tap. The cells stay clear; the card itself wears
     the selection (`tr.citrine-is-selected` above). Through antd's own variables, as the bare frame. */
  .citrine-tblwrap-cards [class*='css-var-'] {
    --ant-table-row-hover-bg: transparent;
    --ant-table-row-selected-bg: transparent;
    --ant-table-row-selected-hover-bg: transparent;
  }
}

/* A GROUPING COLUMN (`CitrineColumn.group`). The column a report's rows are grouped by is drawn as
   one band down the table: the page hue's wash under its label and every cell, in the hue's deep
   ink, so it reads as the grouping and not as one more data column. Outside a `HueScope` the accent
   wash stands in. Two classes over antd's cell selectors, so neither the header fill (bare frame
   included) nor the row hover repaints the band. */
.citrine-tblwrap .citrine-table .ant-table-thead > tr > th.citrine-table-group-col,
.citrine-tblwrap .citrine-table .ant-table-tbody > tr > td.citrine-table-group-col {
  background: var(--citrine-hue-wash, var(--citrine-accent-wash));
  color: var(--citrine-hue-deep, var(--citrine-accent-deep));
}
.citrine-tblwrap .citrine-table .ant-table-thead > tr > th.citrine-table-group-col {
  font-weight: 600;
}

/* The phone card form draws each cell as a block with no padding of its own (the card holds the
   inset); the bare frame's edge inset is for the table form only, so it is taken back here. */
@media (max-width: 820px) {
  .citrine-tblwrap.citrine-tblwrap-bare.citrine-tblwrap-cards
    .citrine-table
    .ant-table-tbody
    > tr
    > :first-child,
  .citrine-tblwrap.citrine-tblwrap-bare.citrine-tblwrap-cards
    .citrine-table
    .ant-table-tbody
    > tr
    > :last-child {
    padding-inline: 0;
  }
}

/* ── TagInput ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — TagInput. The box wears the shared `.citrine-ctrl` skin (border, radius, focus halo, the
   error and disabled looks) that `TagInput.tsx` puts on it; this file lays out what is inside: the
   chips, then a borderless text input that takes the rest of the line and wraps under them. */

.citrine-taginput {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--citrine-space-4);
  min-height: var(--citrine-space-32);
  padding-block: var(--citrine-space-3);
  cursor: text;
}

.citrine-taginput-tags {
  display: contents;
  margin: 0;
  padding: 0;
  list-style: none;
}

.citrine-taginput-tag {
  display: inline-flex;
  max-width: 100%;
}

.citrine-taginput-input {
  flex: 1 1 var(--citrine-space-78);
  min-width: 0;
  min-height: var(--citrine-space-24);
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: inherit;
  font: inherit;
}

.citrine-taginput-input::placeholder {
  color: var(--citrine-ink-4);
}

/* The shared skin's disabled look, drawn here because the box is a <div>, which has no
   `[disabled]` for `.citrine-ctrl[disabled]` to match. */
.citrine-taginput.citrine-taginput-disabled {
  background: var(--citrine-surface-3);
  color: var(--citrine-ink-3);
  cursor: not-allowed;
}

/* A phone: the box is at least a thumb tall, like every other control on a phone. */
@media (max-width: 820px) {
  .citrine-taginput {
    min-height: var(--citrine-target-min);
  }
}

/* ── TenantSwitch ──────────────────────────────────────────────────────────────────────── */
/* UI kit — TenantSwitch. The organisation block at the leading end of the top bar. */

.citrine-tenantsw {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 4px 8px 4px 4px;
  border: 0;
  border-radius: var(--citrine-r-sm, 8px);
  background: transparent;
  color: var(--citrine-ink);
  font: inherit;
  text-align: start;
}

.citrine-tenantsw-button {
  cursor: pointer;
}
.citrine-tenantsw-button:hover {
  background: var(--citrine-surface-3);
}
.citrine-tenantsw-button:focus-visible {
  outline: 2px solid var(--citrine-focus-ring);
  outline-offset: 2px;
}

.citrine-tenantsw-tile {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 32px;
  block-size: 32px;
  border-radius: var(--citrine-r-sm, 8px);
  background: var(--citrine-side);
  color: var(--citrine-side-hero);
  font-size: 16px;
}

.citrine-tenantsw-words {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

.citrine-tenantsw-name {
  overflow: hidden;
  font-weight: 700;
  font-size: 15px;
  color: var(--citrine-side-hero);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.citrine-tenantsw-sub {
  font-size: 11.5px;
  color: var(--citrine-ink-3);
}

.citrine-tenantsw-chev {
  font-size: 11px;
  color: var(--citrine-ink-3);
}

.citrine-tenantsw-label {
  font-size: var(--citrine-font-size-11);
  color: var(--citrine-ink-3);
}

/* PILL (`variant="pill"`): one line inside a light outline — glyph, name, chevron. */
.citrine-tenantsw-pill {
  gap: var(--citrine-sp-2);
  min-height: var(--citrine-tenantswitch-pill-height);
  padding: var(--citrine-space-3) var(--citrine-space-12) var(--citrine-space-3) var(--citrine-space-4);
  border: var(--citrine-space-1) solid var(--citrine-line);
  border-radius: var(--citrine-r-pill);
  background: var(--citrine-surface);
}
.citrine-tenantsw-pill .citrine-tenantsw-tile {
  inline-size: var(--citrine-tenantswitch-pill-glyph-size);
  block-size: var(--citrine-tenantswitch-pill-glyph-size);
  border-radius: var(--citrine-r-pill);
  font-size: var(--citrine-font-size-14);
}
.citrine-tenantsw-pill .citrine-tenantsw-name {
  font-size: var(--citrine-font-size-14);
  font-weight: var(--citrine-font-weight-600);
}
.citrine-tenantsw-pill.citrine-tenantsw-button:hover {
  border-color: var(--citrine-ink-4);
  background: var(--citrine-surface);
}

/* SEARCHABLE list (`searchable`): the popover's search box over the organisations. */
.citrine-tenantsw-search {
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-2);
  inline-size: var(--citrine-tenantswitch-search-width);
}
.citrine-tenantsw-list {
  max-height: var(--citrine-tenantswitch-list-max);
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}
.citrine-tenantsw-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--citrine-sp-3);
  inline-size: 100%;
  min-height: var(--citrine-tenantswitch-option-height);
  padding: var(--citrine-space-6) var(--citrine-space-10);
  border: 0;
  border-radius: var(--citrine-r-xs);
  background: transparent;
  color: var(--citrine-ink);
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.citrine-tenantsw-option:hover,
.citrine-tenantsw-option:focus-visible {
  background: var(--citrine-surface-3);
  outline: none;
}
.citrine-tenantsw-option-current {
  background: var(--citrine-accent-wash);
  color: var(--citrine-accent-deep);
  font-weight: var(--citrine-font-weight-600);
}
.citrine-tenantsw-option-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.citrine-tenantsw-option-sub {
  flex: none;
  font-size: var(--citrine-font-size-12);
  color: var(--citrine-ink-3);
}
.citrine-tenantsw-none {
  margin: var(--citrine-space-4) var(--citrine-space-10);
  font-size: var(--citrine-font-size-13);
  color: var(--citrine-ink-3);
}

/* A phone: the pill and each option hold the 44px target. */
@media (max-width: 900px) {
  .citrine-tenantsw-pill,
  .citrine-tenantsw-option {
    min-height: var(--citrine-target-min);
  }
}

/* ── Text ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Text styles. Source: artifact helper classes (citrine-reference.css 263-269).

   Group B wave 7: this stylesheet is deliberately kept IN FULL. antd's `Typography` expresses none
   of it — there is no font-size, font-family, letter-spacing, text-transform or numeric-variant
   prop on `BlockProps` (`typography/Base/index.d.ts:43-57`), antd's `strong` is weight 600
   (`theme/util/alias.js:82`) against Citrine's 700, and antd's `code` chip is painted from
   hard-coded rgba rather than tokens (`typography/style/mixins.js:61-70`). The three colour rules
   below (`.citrine-eyebrow`, `.citrine-muted`, `.citrine-code`) are the only lines a
   `theme.useToken()` restyle could reach, and each antd token differs in VALUE from what is
   rendered today, which ruling R60 forbids changing without an owner ruling. Withheld, with a
   before/after table, in this wave's report. Recovered from this file: 0 lines, against the plan's
   estimate of ~15. See `Text.tsx` for the full per-helper evidence. */
.citrine-eyebrow {
  font-size: var(--citrine-font-size-10);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--citrine-ink-4);
  font-weight: var(--citrine-font-weight-700);
}
.citrine-muted {
  color: var(--citrine-ink-3);
}
.citrine-small {
  font-size: var(--citrine-font-size-12p5);
}
.citrine-tiny {
  font-size: var(--citrine-font-size-11p5);
}
.citrine-mono {
  font-family: var(--citrine-mono);
  font-size: 0.92em;
}
.citrine-tnum {
  font-variant-numeric: tabular-nums;
}
/* Weight from the scale, not the browser's `bolder`. Size and colour are inherited on purpose — inline emphasis
   must follow its parent (a 13.5px control, a 26px heading); see Text.tsx and the `.citrine-body` baseline. */
.citrine-strong {
  font-weight: var(--citrine-font-weight-700);
}
/* rc.5 — a 600 option, for the sites that need strong emphasis a notch lighter than the 700
   default. Compound selector so it wins regardless of the two classes' order in the DOM. */
.citrine-strong.citrine-strong-600 {
  font-weight: var(--citrine-font-weight-600);
}
.citrine-em {
  font-style: italic;
}
.citrine-code {
  font-family: var(--citrine-mono);
  font-size: var(--citrine-font-size-11p5);
  background: var(--citrine-surface-3);
  border: 1px solid var(--citrine-line);
  border-radius: var(--citrine-radius-5);
  padding: var(--citrine-space-1) var(--citrine-space-6);
  color: var(--citrine-ink-2);
}
/* Visually hides content while keeping it in the accessibility tree. Byte-identical to the rule
   still duplicated in Kanban.css, WorkflowCanvas.css, Sidebar.css (twice) and Dot.css, and to
   Skeleton.css's `.citrine-skel-sr` — this is the one components outside this file should reach for
   via `VisuallyHidden`, rather than adding a seventh copy of the same nine declarations.
   (Was: ".../Sidebar.css/Chart.css/Dot.css". Chart was deleted at `9f6a22c`; Matrix dropped its copy
   onto this file at `3703c8f` and says so at Matrix.css:14; Skeleton was never listed. Re-derived by
   searching for the `clip: rect(0, 0, 0, 0)` declaration, not for a mention of the class name — see
   Text.tsx for the line-level list. Group B wave 7.) */
.citrine-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The two inks the palette gives a MEANING to, reachable from a text primitive for the first time.
   Opt-in only: `Strong` still inherits its colour by default, so emphasis inside a toned Note is
   not quietly repainted. `--citrine-data-2` is the text-safe step of the counting green — the same
   one StageStrip's figure takes — because small bold text on the lighter step is not text-safe. */
.citrine-strong-data {
  color: var(--citrine-data-2, var(--citrine-data));
}
.citrine-strong-accent {
  color: var(--citrine-accent);
}

/* ── TextInput ─────────────────────────────────────────────────────────────────────────── */
/* UI kit — TextInput styles. Source: artifact `.ctrl` / `.ctrl.is-error` / `.ctrl-borderless`.

   `.citrine-ctrl` is defined ONCE here and re-used, unmodified, by every other wrapped field control in
   the kit (Textarea, NumberInput, Select, DatePicker all apply this same class to their own antd
   root node, then add their own small delta class on top — exactly like the reference CSS defines
   `.ctrl` once and layers `textarea.ctrl` / `.ctrl-sel` / `.ctrl.is-num` on top of it). Every
   control folder in this kit is always imported together through the shared `ui/` barrel, so this
   file is always present alongside theirs.

   HEIGHT IS ANT DESIGN'S JOB NOW (ruling D9). This rule used to force 42px three times over —
   `min-height: 42px`, a 9px vertical padding and a 13.5px font size — while `adapters/antd/theme.ts`
   sets no `controlHeight` at all, so the theme says antd's stock 32 and the CSS said 42. Deleting
   the `min-height` alone would NOT have fixed that: 13.5px x antd's `lineHeight` 1.571429 = 21.2px
   of line box, plus 9+9 padding and 1+1 border, is 41.2px on its own. All three had to go, and the
   horizontal padding (14px) is the only part of the old shorthand kept — hence `padding-inline`.

   What governs the height instead, all read from the installed antd 6.2.3:

     - `<input>` / `<textarea>` (`.ant-input`): `padding: paddingBlock paddingInline`, where
       `paddingBlock = round((controlHeight - fontSize x lineHeight) / 2 x 10) / 10 - lineWidth`
       (`input/style/token.js:35,39`) and `paddingInline = paddingSM - lineWidth`
       (`:42`). At stock that is 4px / 11px, so 22 + 8 + 2 = 32.
     - `Select` (`.ant-select`): `--ant-select-height: controlHeight` and
       `--ant-select-padding-vertical: (height - font-height) / 2 - border-size`, applied to the
       root as `paddingBlock` (`select/style/select-input.js:85,87,113`).
     - `DatePicker` (`.ant-picker`): `genPickerPadding(token.paddingBlock, token.paddingInline)` on
       the root (`date-picker/style/index.js:13-16,82`).
     - `InputNumber` (`.ant-input-number`): the root is explicitly `paddingBlock: 0`
       (`input-number/style/index.js:53`) and hands the vertical padding to the inner
       `.ant-input-number-input` through `--ant-input-number-input-padding-block`
       (`:48,103`). Citrine's 9px sat on the ROOT, i.e. OUTSIDE antd's own 32px box, so this
       wrapper was never 42 — it was ~49. The pin was not even the binding declaration here.
     - and the row underneath all five: `FieldShell` mounts an antd `Form.Item`, and
       `.ant-form-item-control-input` is `min-height: controlHeight`
       (`form/style/index.js:54,63-65`) — the same floor `Field.css` already relies on.

   The remaining declarations below still beat antd's own at equal specificity because antd inserts
   its cssinjs rules at the TOP of `<head>` (`prepend: 'queue'` / `data-rc-order: prependQueue`,
   `@ant-design/cssinjs/es/hooks/useStyleRegister.js:278,339`), so a consumer sheet loaded normally
   comes later and wins. That is why the border, radius, background and ink here are still visible —
   and it is exactly why the height declarations had to be deleted rather than merely reduced.

   `textarea.citrine-ctrl { min-height: 84px }` in `Textarea/Textarea.css` is element+class, higher
   specificity, and is untouched: a textarea keeps its own floor by design. */

.citrine-ctrl {
  width: 100%;
  padding-inline: var(--citrine-space-14);
  border: 1px solid var(--citrine-line-2);
  border-radius: var(--citrine-r-sm);
  background: var(--citrine-surface);
  color: var(--citrine-ink);
  font-family: var(--citrine-sans);
}

.citrine-ctrl::placeholder {
  color: var(--citrine-ink-4);
}

.citrine-ctrl:hover {
  border-color: var(--citrine-ink-4);
}

/* Focus is the ACTION navy, not the data green (2026-09-12). A field is something you act
   in, and the Two Inks Rule gives green to figures only — the green edge was the kit's one standing
   breach of it, already overridden on the Entrance. The same 3px halo, at 22% of the accent. */
.citrine-ctrl:focus,
.citrine-ctrl:focus-within {
  border-color: var(--citrine-accent);
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--citrine-accent) 22%, transparent);
}

.citrine-ctrl[disabled],
.citrine-ctrl.citrine-is-disabled {
  background: var(--citrine-surface-3);
  color: var(--citrine-ink-3);
  cursor: not-allowed;
}

.citrine-ctrl.citrine-is-error {
  border-color: var(--citrine-bad);
  box-shadow: 0 0 0 3px var(--citrine-bad-wash);
}

/* `status="warn"` (TextInput, NumberInput): the same ring as the error state, in the warn tone. Opt-in;
   an error on the same field wins, because the component never emits both classes. */
.citrine-ctrl.citrine-is-warn {
  border-color: var(--citrine-warn);
  box-shadow: 0 0 0 3px var(--citrine-warn-wash);
}

.citrine-ctrl-borderless {
  border-color: transparent;
  background: transparent;
  font-weight: var(--citrine-font-weight-700);
  padding-left: 0;
}

.citrine-ctrl-borderless:hover,
.citrine-ctrl-borderless:focus,
.citrine-ctrl-borderless:focus-within {
  border-color: var(--citrine-line-2);
  padding-left: var(--citrine-space-14);
  background: var(--citrine-surface);
}

/* The show-password toggle in a password field's suffix (see `TextInput.tsx`). A real button: it
   takes focus, so it draws its own ring. The glyph is 1em, so the button's font-size sizes it. */
.citrine-ctrl-reveal {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  /* The button is a bigger target than the text line it sits on: the negative block margin keeps it
     from growing the field (a stock 32px field has a 22px content box — 28 - 2 x 3 = 22). */
  margin-block: -3px;
  margin-inline-end: -6px;
  padding: 0;
  border: 0;
  border-radius: var(--citrine-r-xs, 6px);
  background: none;
  color: var(--citrine-ink-3);
  font-size: 18px;
  cursor: pointer;
}

.citrine-ctrl-reveal:hover {
  color: var(--citrine-ink);
}

.citrine-ctrl-reveal:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: 1px;
}

.citrine-ctrl-reveal:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* ── TextLink ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — TextLink. The focus ring here is the reason the component exists: `reset.css` has no
   `focus-visible` rule at all, and every one of the 34 in `styles.css` is `.citrine-*`-scoped, so a
   plain <a> in a consuming app has no ring and no way to ask for one. */
.citrine-textlink {
  color: var(--citrine-accent);
  /* Kept at rest, not revealed on hover. A link marked by colour alone fails WCAG 1.4.1 inside body
     copy, because nothing but a hue separates it from the words around it. */
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  border-radius: 2px;
}

/* The underline is restated on hover: antd's own link reset drops it under `a:hover`, a selector
   that outranks the bare `.citrine-textlink` above — so the link lost its underline exactly when the
   pointer was on it (measured: `text-decoration-line: none` while hovered, in a table cell). */
.citrine-textlink:hover {
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
}

/* `as="button"` — a link-looking action that is not a navigation. The browser draws a native button
   (grey fill, border, padding, its own font) unless told otherwise, and `reset.css` does not touch
   buttons, so without this a TextLink rendered as a <button> looked like a raw form control. */
button.citrine-textlink {
  appearance: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  line-height: inherit;
  text-align: inherit;
  cursor: pointer;
}

/* `tone="inherit"` — the value keeps the look it had as plain text (its colour, no underline) and
   only gains the action. NO hover underline either: on a touchscreen a tap leaves `:hover` stuck on
   the link until the next tap elsewhere, so a hover rule showed up as an underline after every call
   (owner, 2026-10-05). The focus ring below is unchanged. See the prop's note for why this is not for
   links inside a sentence. */
.citrine-textlink--inherit,
.citrine-textlink--inherit:hover {
  color: inherit;
  text-decoration: none;
}

/* The ring itself. `outline` rather than `box-shadow`, so it follows the link across a line break
   instead of drawing one rectangle around both fragments, and so it survives forced-colours mode
   where a shadow is dropped. The 2px offset keeps it clear of the underline. */
.citrine-textlink:focus-visible {
  outline: 2px solid var(--citrine-accent);
  outline-offset: 2px;
}

/* ── Textarea ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Textarea styles. Source: artifact `textarea.ctrl`.
   `.citrine-ctrl` base (including the `.citrine-ctrl.citrine-is-error` error style) lives in
   `TextInput/TextInput.css`. Ant Design's `Input.TextArea` renders a real `<textarea>` as its root
   node (unlike Select/InputNumber/DatePicker, which wrap in a div), so — exactly like the
   reference CSS — the delta here is a plain element+class compound selector. */
textarea.citrine-ctrl {
  min-height: 84px;
  resize: vertical;
}

/* ── TileGrid ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — TileGrid. Source: the navigation artifact `.gcard` / `.gtiles` / `.gtile` block.

   Shell-tokens wave: structure unchanged — a titled card of real icon+label+count controls has no
   antd equivalent — but every colour and measurement comes from antd's live theme via
   `antdTheme.useToken()` in `TileGrid.tsx`, handed down as `--citrine-tilegrid-*` custom properties
   seeded on the root `<section>`.

   2.1: the `plain` variant and `columns="auto"`; a 48px glyph square in the accent wash with a 22px
   glyph; a 12px, 500-weight label; and no card shadow — DESIGN.md's Flat Rule, and the card was the
   last shadowed surface on a consumer's More screen.

   ── What deliberately did NOT move ──────────────────────────────────────────────────────────────

   · `min-height: 44px` on a tile — WCAG 2.5.5 target size. This grid only ever exists on a touch
     screen.
   · `line-height: 1.25` on a tile label. Structural: it is the box a two-line clamp measures, not a
     typographic choice.
   · `opacity: 0.42` on a disabled tile, and the full-round `999px` on the count pill — antd has no
     token for either.
   · The count's 9.5px. FITTING CONSTANT, exactly as `BottomBar`'s badge is under the owner ruling of
     2026-09-10: a 17px pill has ~13px of inner box, and at antd's 12px floor a two-digit count
     widens it into an oval riding over the glyph it is pinned to. */

.citrine-tilegrid {
  background: var(--citrine-tilegrid-bg);
  border: 1px solid var(--citrine-tilegrid-line);
  border-radius: var(--citrine-tilegrid-r);
  padding: var(--citrine-tilegrid-pad-y) var(--citrine-tilegrid-pad-x) var(--citrine-tilegrid-pad-bottom);
}

.citrine-tilegrid + .citrine-tilegrid {
  margin-top: var(--citrine-tilegrid-pad-y);
}

.citrine-tilegrid-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--citrine-tilegrid-pad-x);
  padding: 0 var(--citrine-tilegrid-pad-x);
  margin-bottom: var(--citrine-tilegrid-pad-x);
}

.citrine-tilegrid-title {
  font-size: var(--citrine-tilegrid-title-size);
  font-weight: var(--citrine-tilegrid-title-weight);
  color: var(--citrine-tilegrid-ink);
  margin: 0;
}

/* ── Plain: a titled set straight on the page ────────────────────────────────────────────────────
   No surface, edge or padding of its own; the heading is a Launcher set title (15px, 700, the
   action colour). Sets stand apart by space, not by boxes. */
.citrine-tilegrid--plain {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}

/* The space between sets is the container's (a `Stack`): a margin here would add to its gap. */
.citrine-tilegrid--plain + .citrine-tilegrid--plain {
  margin-top: 0;
}

.citrine-tilegrid--plain .citrine-tilegrid-head {
  padding: 0 2px;
  margin-bottom: 8px;
}

.citrine-tilegrid--plain .citrine-tilegrid-title {
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--citrine-tilegrid-set-ink);
}

.citrine-tilegrid-tiles {
  display: grid;
  /* Driven by the `--citrine-tilegrid-cols` custom property the component sets inline, so a caller
     choosing a different width does not need a new class here. */
  grid-template-columns: repeat(var(--citrine-tilegrid-cols, 4), minmax(0, 1fr));
}

/* `columns="auto"`: as many tiles of at least 80px as the grid's own width holds, at most eight.
   Four across on a 360–412px phone; no window breakpoint involved. */
.citrine-tilegrid-tiles--auto {
  gap: 2px;
  grid-template-columns: repeat(auto-fill, minmax(max(80px, calc((100% - 7 * 2px) / 8)), 1fr));
}

.citrine-tilegrid-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  padding: 6px 2px 8px;
  border: 0;
  background: none;
  border-radius: 12px;
  font-family: inherit;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  position: relative;
  -webkit-tap-highlight-color: transparent;
  /* 44px is the WCAG 2.5.5 target size, and this grid only ever exists on a touch screen. */
  min-height: 44px;
}

.citrine-tilegrid-tile:hover {
  background: var(--citrine-tilegrid-tile-hover);
}

.citrine-tilegrid-tile:focus-visible {
  outline: 2px solid var(--citrine-tilegrid-pin-ring);
  outline-offset: -2px;
}

.citrine-tilegrid-tile:disabled,
.citrine-tilegrid-tile[aria-disabled='true'] {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none;
}

/* The glyph square: 48px with 14px corners and a 22px glyph — the Launcher door's square, one step
   smaller. */
.citrine-tilegrid-tile-icon {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: var(--citrine-tilegrid-ico-bg);
  color: var(--citrine-tilegrid-ico-ink);
  display: grid;
  place-items: center;
  /* A ring, so a pinned tile can grow one without changing size and shifting the row. */
  box-shadow: 0 0 0 0 transparent;
}

.citrine-tilegrid-tile-icon svg {
  width: 22px;
  height: 22px;
}

.citrine-tilegrid-tile-label {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* 12px: two lines of a menu label fit an 80px tile ("Recommend a / partner"), and it is antd's own
     floor, so the label is no longer a size below the type scale. */
  font-size: 12px;
  font-weight: 500;
  /* Structural — the box a two-line clamp measures, not a typographic choice. */
  line-height: var(--citrine-line-height-1p25);
  color: var(--citrine-tilegrid-ink);
  word-break: normal;
}

.citrine-tilegrid-tile-count {
  position: absolute;
  top: var(--citrine-tilegrid-gap-xs);
  right: var(--citrine-tilegrid-pad-y);
  min-width: 17px;
  height: 17px;
  border-radius: 999px;
  background: var(--citrine-tilegrid-count-bg);
  color: var(--citrine-tilegrid-count-ink);
  /* FITTING CONSTANT — see the header note. */
  font-size: 9.5px;
  font-weight: var(--citrine-tilegrid-title-weight);
  line-height: 17px;
  text-align: center;
  padding: 0 var(--citrine-tilegrid-gap-xs);
}

/* ── Quiet ────────────────────────────────────────────────────────────────────────────────────
   Reachable but empty. Dashed and desaturated so it reads as "nothing here yet" rather than as
   "you may not" — which is what a greyed-out DISABLED tile would say. */
.citrine-is-quiet .citrine-tilegrid-tile-icon {
  background: var(--citrine-tilegrid-quiet-bg);
  color: var(--citrine-tilegrid-quiet-ink);
  border: 1px dashed var(--citrine-tilegrid-quiet-line);
}

.citrine-is-quiet .citrine-tilegrid-tile-label {
  color: var(--citrine-tilegrid-quiet-ink);
}

/* ── Pinned ───────────────────────────────────────────────────────────────────────────────────
   A ring on the glyph rather than a badge: a badge would collide with the count, and the ring reads
   as "held in place", which is what pinning is. The word "pinned" is in the accessible name. */
.citrine-is-pinned .citrine-tilegrid-tile-icon {
  box-shadow: 0 0 0 2px var(--citrine-tilegrid-pin-ring);
}

/* ── A tile that wears a hue (opt-in `hue`) ────────────────────────────────────────────────────
   The glyph square and the rings are seeded ONCE on the section, where no hue is in scope, so a
   per-tile hue would never reach them. The tile restates them on itself: inside `.citrine-hued` the
   accent roles ARE the hue (HueScope.css), so they resolve to its ramp here. A quiet tile keeps its
   neutral dashed square — the rule above is the more specific one. */
.citrine-tilegrid-tile.citrine-hued,
.citrine-tilegrid.citrine-hued .citrine-tilegrid-tile {
  --citrine-tilegrid-ico-bg: var(--citrine-accent-wash);
  --citrine-tilegrid-ico-ink: var(--citrine-accent-deep);
  --citrine-tilegrid-pin-ring: var(--citrine-accent);
}

/* ── A set that wears a hue (opt-in `hue` on TileGrid) ─────────────────────────────────────────
   Keyed on the SECTION carrying `.citrine-hued`, never on an ancestor scope: a set that merely sits
   inside a `HueScope` must draw exactly what it drew before this prop existed.

   The heading takes the ramp's deep ink. A plain set's heading already reads `set-ink`, which the
   section seeds as `var(--citrine-accent-deep)` and the hue class remaps on the same element; a
   card's heading reads the neutral ink, so the rule names `set-ink` for both.

   The glyph square gains a hairline in the ramp's line colour, so a pale wash still reads as a chip
   on the page. A border, not a ring: the pinned ring is the square's `box-shadow`, and the square is
   `border-box`, so the edge costs no size and the row does not shift. `--citrine-accent-line`
   resolves at the square, so a tile with its own hue draws its own ramp's line. A quiet tile keeps
   its neutral dashed edge — it is excluded rather than out-ranked. */
.citrine-tilegrid.citrine-hued .citrine-tilegrid-title {
  color: var(--citrine-tilegrid-set-ink);
}

.citrine-tilegrid.citrine-hued .citrine-tilegrid-tile:not(.citrine-is-quiet) .citrine-tilegrid-tile-icon {
  border: var(--citrine-space-1) solid var(--citrine-accent-line);
}

/* ── TimePicker ────────────────────────────────────────────────────────────────────────── */
/* UI kit — TimePicker styles. The `.citrine-ctrl` base (including `.citrine-ctrl.citrine-is-error`)
   lives in `TextInput/TextInput.css`; the column popup's chrome comes from `adapters/antd/theme.ts`.
   antd sizes its time picker to its content, so this makes it fill its field, as DatePicker does. */
.citrine-ctrl-timefield {
  width: 100%;
}

/* ── Timeline ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Timeline. antd's Timeline now draws the rail, the ringed dot and the item spacing
   itself; the only thing left here is the small uppercase kind label ("CREATED"/"UPDATED"/
   "DELETED") rendered ahead of the event text, which has no antd slot of its own.
   Tokens only, no raw hex. */

/* The event line, then the who-and-when line under it. Both sit in antd's `content` slot — see the
   note on `Timeline` for why nothing may go in `title`. */
.citrine-tl-what {
  color: var(--citrine-ink);
}

.citrine-tl-meta {
  margin-top: var(--citrine-sp-1);
  font-size: var(--citrine-font-size-12);
  color: var(--citrine-ink-3);
}

.citrine-tl-kind-label {
  display: inline-block;
  margin-right: var(--citrine-sp-2);
  font-size: var(--citrine-font-size-10);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: var(--citrine-font-weight-700);
  color: var(--citrine-ink-3);
}

.citrine-tl-kind-create {
  color: var(--citrine-ok);
}

.citrine-tl-kind-update {
  color: var(--citrine-data);
}

.citrine-tl-kind-delete {
  color: var(--citrine-bad);
}

/* A toned item's filled dot (`tone`), drawn the size of antd's own ring so the rail still lines up. */
.citrine-tl-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.citrine-tl-dot-ok {
  background: var(--citrine-ok);
}

.citrine-tl-dot-data {
  background: var(--citrine-data);
}

.citrine-tl-dot-bad {
  background: var(--citrine-bad);
}

/* ── Toolbar ───────────────────────────────────────────────────────────────────────────── */
/* UI kit — Toolbar.
   Citrine 2.0: `.citrine-toolbar` and `.citrine-toolbar-end` no longer have rules here. Both are
   antd `Flex` boxes now — the bar's `display`, `align-items`, `flex-wrap`, `gap` and the end
   slot's auto start-margin all come from antd's own flex stylesheet plus the two inline styles
   `Toolbar.tsx` pins. The two class names survive as stable hooks for consuming stylesheets and
   test selectors; they simply no longer carry the layout.

   What is left is `CountLine`, which is typography, not layout. See the header of `Toolbar.tsx`
   for why it waits on the `Text` -> `Typography` ruling in wave 7. */

.citrine-countline {
  font-size: var(--citrine-font-size-12p5);
  color: var(--citrine-ink-3);
}

/* `sticky="bottom"` — the bar pinned to the bottom of the nearest scrolling ancestor, on the page
   ground so the content scrolling under it is covered. Opt-in. */
.citrine-toolbar-sticky {
  position: sticky;
  bottom: 0;
  z-index: var(--citrine-z-sticky);
  background: var(--citrine-toolbar-sticky-ground);
  padding-block: var(--citrine-toolbar-sticky-pad);
}

/* ── Topbar ────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Topbar. Source: artifact `.topbar` block (citrine-reference.css). Tokens only. */

.citrine-topbar {
  height: var(--citrine-topbar-height);
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-3);
  padding: 0 var(--citrine-sp-6);
  background: var(--citrine-surface);
  border-bottom: 1px solid var(--citrine-line);
  position: sticky;
  top: 0;
  z-index: var(--citrine-z-sticky);
}

/* `brand`: the leading edge, apart from the search. It keeps its own width at every size — a logo is
   not squeezed — and the search slot after it yields instead. */
.citrine-topbar-brand {
  display: flex;
  align-items: center;
  flex: none;
  margin-inline-end: var(--citrine-topbar-brand-gap);
}

.citrine-topsearch {
  display: flex;
  align-items: center;
  /* More than one control in the slot (a menu button beside a back button) sits a step apart and
     on one line; a single search box is unaffected. */
  gap: var(--citrine-sp-1);
  flex: 1;
  max-width: var(--citrine-topbar-search-max);
  /* May shrink below its content's natural width, so a long organisation name in it ellipsizes on
     a phone instead of pushing the avatar past the screen's edge. */
  min-width: 0;
}

/* `searchWidth="wide"`: a higher cap on a desk. Below it the slot still yields to the bar. */
.citrine-topsearch-wide {
  max-width: var(--citrine-topbar-search-max-wide);
}

.citrine-topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  /* May shrink below its content's natural width, so a field in it (a `Select width="fixed"`
     picker) gives way on a phone instead of pushing the avatar past the screen's edge. */
  min-width: 0;
}

/* A field in the bar (a Select, a TextInput — e.g. a workspace or organisation picker). antd's
   Form.Item keeps a bottom margin for the message line under a form field; in a bar there is no such
   line, and the margin made the field's box taller than its control, so centring put the control
   above the avatar and buttons beside it. The bar sets antd's own Form.Item token rather than
   reaching into the field shell's classes (component-scope rule); antd declares the token on each
   component root, hence the attribute selector, which outranks antd's `:where(.css-var-…)`. */
.citrine-topbar [class*='css-var-'] {
  --ant-form-item-margin-bottom: 0px;
}

/* RULING D14 (Group A audit, finding F-A7, 2026-09-10): `.citrine-iconbtn` used to repaint the antd
   Button that `IconButton` wraps — 40×40, `var(--citrine-r-pill)`, its own border, background and
   ink, plus a hover repaint. That is the SAME skin on the SAME antd component as `Button`'s own
   (F-A2 / ruling D13), which is why the audit said to fold this into that decision rather than treat
   it as its own. `Button.css` is now deleted, so this went with it.

   The pill was redundant twice over: `Topbar.tsx` already passes antd's own `shape="circle"`, so the
   round shape was never this rule's to supply.

   The class is still emitted — it is a public styling hook — it simply carries no declaration.

   The rest of this file stays. `.citrine-topbar`, `.citrine-topsearch`, `.citrine-topbar-right` and
   `.citrine-topuser*` style the shell markup this component renders ITSELF; none of them lands on an
   antd node, and the review already scoped shell components out of the rebuild. */

.citrine-topuser {
  display: flex;
  align-items: center;
  gap: var(--citrine-space-9);
  padding: var(--citrine-sp-1) var(--citrine-sp-3) var(--citrine-sp-1) var(--citrine-sp-1);
  border-radius: var(--citrine-r-pill);
  border: 1px solid var(--citrine-line);
  background: none;
  cursor: pointer;
  text-align: left;
}

.citrine-topuser:hover {
  background: var(--citrine-surface-3);
}

.citrine-topuser-av {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--citrine-data);
  color: var(--citrine-accent-ink);
  display: grid;
  place-items: center;
  font-size: var(--citrine-font-size-11p5);
  font-weight: var(--citrine-font-weight-700);
  flex: none;
}

.citrine-topuser-who {
  font-size: var(--citrine-font-size-12p5);
  font-weight: var(--citrine-font-weight-600);
  line-height: var(--citrine-line-height-1p2);
  color: var(--citrine-ink);
}

.citrine-topuser-who i {
  display: block;
  font-style: normal;
  font-size: var(--citrine-font-size-10p5);
  color: var(--citrine-ink-3);
  font-weight: var(--citrine-font-weight-500);
}

@media (max-width: 900px) {
  .citrine-topbar {
    padding: 0 var(--citrine-sp-4);
  }
}

/* `density="compact"`: a lower bar, desk and phone each from their own token. */
.citrine-topbar-compact {
  height: var(--citrine-topbar-compact-height);
}

@media (max-width: 900px) {
  .citrine-topbar-compact {
    height: var(--citrine-topbar-compact-height-narrow);
  }
}

/* ── `user` ───────────────────────────────────────────────────────────────────────────────────────
   Both forms are in the markup; exactly one is shown, switched at the shell's own md breakpoint (the
   width at which the rail gives way to the bottom bar). `display: none` also takes the other form
   out of the accessibility tree, so a screen reader meets one account control, not two. */

.citrine-topbar-user {
  display: flex;
  align-items: center;
  gap: var(--citrine-topbar-user-gap);
}

.citrine-topbar-user-avatar {
  display: flex;
  flex: none;
}

.citrine-topbar-user-name {
  font-size: var(--citrine-topbar-user-name-size);
  color: var(--citrine-ink-2);
  white-space: nowrap;
}

.citrine-topbar-account {
  display: none;
}

@media (max-width: 900px) {
  .citrine-topbar-user {
    display: none;
  }

  .citrine-topbar-account {
    display: flex;
  }
}

/* The avatar IS the button on a phone, so it carries a press target of its own: the drawn disc
   stays its size, and a painted-nothing `::after` holds the kit's 44px floor open around it — the
   pattern `Button.css` uses for small icon buttons. */
.citrine-topbar-account-btn {
  position: relative;
  display: flex;
  padding: 0;
  border: 0;
  border-radius: var(--citrine-r-pill);
  background: none;
  cursor: pointer;
}

.citrine-topbar-account-btn::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--citrine-target-min));
  height: max(100%, var(--citrine-target-min));
  transform: translate(-50%, -50%);
  background: none;
  border-radius: inherit;
}

.citrine-topbar-account-btn:focus-visible {
  outline: 2px solid var(--citrine-focus-ring);
  outline-offset: 2px;
}

/* ── Tree ──────────────────────────────────────────────────────────────────────────────── */
/* UI kit — Tree. Rebuilt on antd `Tree` (Group B wave 5): 79 lines down to this.
   Row padding, hover, focus, indentation and the connector lines are antd's now — the hand-drawn
   monospace ASCII guides and the three hard-coded depth-indent classes are gone with them.

   What survives is the two markers this component adds and antd has no slot for: the "you are
   here" wash and the withdrawn state, plus the trailing meta chip. `.citrine-is-current` and
   `.citrine-is-hidden` are set on the treeitem through antd's per-node `className`, so they still
   compose with antd's own row styling rather than fighting it. */

.citrine-tree .citrine-is-current {
  background: var(--citrine-accent-wash);
}

.citrine-tree .citrine-is-hidden {
  opacity: 0.5;
}

.citrine-tree-meta {
  margin-left: auto;
  font-size: var(--citrine-font-size-10);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--citrine-ink-4);
  font-weight: var(--citrine-font-weight-700);
}

.citrine-tree-hidden-badge {
  font-size: var(--citrine-font-size-10);
  font-weight: var(--citrine-font-weight-700);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--citrine-ink-3);
  background: var(--citrine-surface-3);
  border: 1px solid var(--citrine-line-2);
  border-radius: var(--citrine-r-xs);
  padding: var(--citrine-space-1) var(--citrine-space-6);
}

/* ── TriState ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — TriState styles.
   Source: artifact `.tri` and its `[data-state]` buttons.

   Group B wave 7: the component stayed bespoke (see the head of TriState.tsx for why neither
   `Segmented` nor `Radio.Group` can carry its blocked-option contract) and this stylesheet was
   restyled onto Ant Design's tokens. Every value below now arrives as a `--citrine-tri-*` custom
   property that TriState.tsx sets from `theme.useToken()`; not one `--citrine-*` brand token is
   read here any more. The one kit token read is `--citrine-target-min`, the 44px target floor —
   an accessibility floor, not a brand value. The shape of the rules is unchanged, so every class this file has ever
   carried still exists and still means the same thing. */

/* ── mode="pills" ──────────────────────────────────────────────────────────────────────────────
   Stock antd `Segmented`'s track, value for value: `padding: trackPadding`,
   `background: trackBg` (= `colorBgLayout`), `borderRadius`
   (`antd/es/segmented/style/index.js:36-39,202-209`). */

.citrine-tri {
  display: inline-flex;
  background: var(--citrine-tri-track-bg);
  border-radius: var(--citrine-tri-track-radius);
  padding: var(--citrine-tri-track-pad);
}

/* The item. The corner is `borderRadiusSM` (`:78`), the resting ink is `itemColor` =
   `colorTextLabel` (`:204`). The height starts from antd's own `controlHeight - 2 × trackPadding`
   (`index.js:29`, 28px) and is held open to the kit's target floor (`--citrine-target-min`, 44px):
   each pill is a press target, and that floor is an accessibility default for everyone. A real
   height rather than a painted-nothing `::after` overlay (the way `Button.css` opens an icon
   button), because the pills sit side by side with no gap, so an overlay would reach into its
   neighbours and two pills would claim the same point.

   THE BRAND SKIN THAT LEFT: the uppercase, the 0.04em letter-spacing, the 700 weight and the 11px
   size. Stock antd sets a segment in ordinary sentence case at `fontSize`, and those four were the
   whole of what made this control look hand-made beside one. */
.citrine-tri-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  color: var(--citrine-tri-ink);
  cursor: pointer;
  font-size: var(--citrine-tri-font-size);
  padding: 0 var(--citrine-tri-pill-pad);
  height: max(var(--citrine-tri-pill-height), var(--citrine-target-min));
  border-radius: var(--citrine-tri-pill-radius);
}

.citrine-tri-btn:hover:not(:disabled):not([aria-disabled='true']):not(.citrine-tri-btn-is-on) {
  background: var(--citrine-tri-hover-bg);
}

/* The selected item. antd raises its own with `background: itemSelectedBg` (= `colorBgElevated`)
   plus `boxShadowTertiary` (`index.js:15-16,207`), and that is the whole of the `neutral` look.
   The four semantic tones swap the wash and the hairline for antd's status pair — the `*Bg` and
   `*Border` its `Alert` paints with — and leave the WORD at `colorText` at every tone, because
   `colorWarning` on white is about 1.9:1 and the word is the signal that must never be lost.
   `inset` hairline rather than a border: a real border would grow the item and a row of
   selected pills would sit 2px taller than a row of unselected ones. */
.citrine-tri-btn-is-on {
  color: var(--citrine-tri-ink-on);
  background: var(--citrine-tri-fill);
  box-shadow:
    inset 0 0 0 1px var(--citrine-tri-edge),
    var(--citrine-tri-shadow);
}

/* The tone-keyed (`-is-on-ok`) and value-keyed (`-is-on-rw`) classes have no rules any more.
   They are still EMITTED by TriState.tsx and are still part of the public surface — the tone now
   arrives as the inline `--citrine-tri-fill`/`--citrine-tri-edge` pair above, which is what a
   custom vocabulary paints with too. Same treatment `Dot` had in wave 3. */

/* Blocked is not disabled: the option keeps its place in the tab order so a reader can reach it and
   be told it is blocked. antd expresses "you cannot have this" as `colorTextDisabled` ink rather
   than as opacity, so the old 0.32 wash is gone. */
.citrine-tri-btn-is-blocked,
.citrine-tri-btn:disabled {
  color: var(--citrine-tri-ink-off);
  cursor: not-allowed;
}

/* THE FOCUS RING, on both shapes — now antd's own rather than the kit's.
   `lineWidthFocus` solid `colorPrimaryBorder`, which is what every stock antd control draws.
   `:focus-visible`, not `:focus`: a mouse click on a cell should not leave a ring behind. */
.citrine-tri-btn:focus-visible,
.citrine-tri-cycle:focus-visible {
  outline: var(--citrine-tri-focus-width) solid var(--citrine-tri-focus);
  outline-offset: 1px;
}

/* ── mode="cycle" ──────────────────────────────────────────────────────────────────────────────
   One cell you click to advance, for grids where three pills will not fit. The tone colours the
   MARK rather than filling a pill: at grid density a filled cell per row would read as a heat map
   nobody asked for. The word still reaches a reader through the accessible name and `title`.

   Sized from `controlHeightSM`, not `controlHeight` — this is the dense shape, and the smaller of
   antd's two control heights is what a grid cell wants — but never below the kit's target floor
   (`--citrine-target-min`, 44px) either way, in height or in width: a cell is a press target, and
   in a grid of them an overlay reaching past the cell would land on the next one. */

.citrine-tri-cycle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--citrine-tri-cell-gap);
  min-width: max(var(--citrine-tri-cell-min), var(--citrine-target-min));
  height: max(var(--citrine-tri-cell-height), var(--citrine-target-min));
  padding: 0 var(--citrine-tri-cell-pad);
  border: 0;
  background: none;
  border-radius: var(--citrine-tri-cell-radius);
  cursor: pointer;
  font-size: var(--citrine-tri-font-size);
  color: var(--citrine-tri-mark);
}

.citrine-tri-cycle:hover:not(:disabled):not([aria-disabled='true']) {
  background: var(--citrine-tri-hover-bg);
}

.citrine-tri-cycle:disabled,
.citrine-tri-cycle[aria-disabled='true'] {
  cursor: not-allowed;
}

/* Same reasoning as the blocked pill: antd's disabled ink, not an opacity wash. */
.citrine-tri-cycle:disabled {
  color: var(--citrine-tri-ink-off);
}

/* The `.citrine-tri-cycle-<tone>` classes are likewise still emitted and no longer paint: the
   mark's colour is the inline `--citrine-tri-mark` above, from antd's solid status tokens. */

/* ── fill="tonal" ──────────────────────────────────────────────────────────────────────────────
   The cell takes the tone's wash and a matching hairline, for screens holding a FEW of these rather
   than a grid full — where the ghost shape is so quiet it stops reading as clickable.

   The two custom properties survive the restyle unchanged in mechanism; only their source moved,
   from a rule per tone in this file to one `toneFill()` call in the component. That is what keeps
   the hover rule to a single line: the base `:hover` sets `background: var(--citrine-tri-hover-bg)`
   and outranks a plain `.a.b` selector (four pseudo-classes' worth of specificity), so without
   re-asserting the fill, hovering a tonal cell would grey it out. Re-asserting the SAME variable
   means hover changes nothing rather than needing five more rules to undo.

   `box-shadow: inset`, not `border`: the cell is a fixed height and a real border would grow it, so
   a row of tonal cells would sit 2px taller than a row of ghost ones. */
.citrine-tri-cycle-is-tonal {
  background: var(--citrine-tri-fill);
  box-shadow: inset 0 0 0 1px var(--citrine-tri-edge);
}

.citrine-tri-cycle-is-tonal:hover:not(:disabled):not([aria-disabled='true']) {
  background: var(--citrine-tri-fill);
}

.citrine-tri-cycle-mark {
  display: inline-flex;
  align-items: center;
}

/* The optional caption beside the mark — an action code in a list with no column headers. It is
   secondary to the mark, so it takes the body ink rather than the state's colour. */
.citrine-tri-cycle-label {
  font-family: var(--citrine-tri-caption-font);
  font-size: var(--citrine-tri-caption-size);
  color: var(--citrine-tri-caption-ink);
}

/* ── Truncate ──────────────────────────────────────────────────────────────────────────── */
/* UI kit — Truncate. One line, cut with an ellipsis at its box's edge. `display: block` so it takes
   the width of its cell or column rather than the width of its text; `min-width: 0` lets it shrink
   inside a flex row instead of pushing the row wider. */
.citrine-truncate {
  display: block;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.citrine-truncate:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-2);
}

/* `lines` — several lines, wrapping at spaces, the last one cut with an ellipsis. The line count is
   a class per step rather than a custom property: it is a count, not a design value. Words still
   break only at spaces; a word wider than the box is clipped at its edge, never split. */
.citrine-truncate-lines {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  white-space: normal;
}

.citrine-truncate-l2 {
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.citrine-truncate-l3 {
  -webkit-line-clamp: 3;
  line-clamp: 3;
}
.citrine-truncate-l4 {
  -webkit-line-clamp: 4;
  line-clamp: 4;
}
.citrine-truncate-l5 {
  -webkit-line-clamp: 5;
  line-clamp: 5;
}
.citrine-truncate-l6 {
  -webkit-line-clamp: 6;
  line-clamp: 6;
}

/* ── WorkflowCanvas ────────────────────────────────────────────────────────────────────── */
/* UI kit — WorkflowCanvas. Source: artifact `.wfcanvas`, `.wfnode`, `.wfnode.is-selected`,
   `.wfnode-name`, `.wfhandle`, `.wfhandle.l`, `.wfhandle.r` (citrine-reference.css).

   Group C (2.0): structure unchanged — there is no antd graph surface. Colours and metrics now come
   from antd's live theme via `antdTheme.useToken()` in `WorkflowCanvas.tsx`, handed down as
   `--citrine-wfcanvas-*` / `--citrine-wfnode-*` / `--citrine-wfedge-*` custom properties. Every
   fallback is the Citrine token the rule used to read.

   STRUCTURAL and therefore literal: the 420px minimum height, the 22px dot-grid pitch and its
   1.2px dots, the 158px minimum node width, the 1.5px edge stroke, and the 10px connection handles
   at -6px. Those are the diagram's geometry — change them and the graph stops reading as a graph. */

.citrine-wfcanvas {
  position: relative;
  min-height: 420px;
  background: var(--citrine-wfcanvas-bg, var(--citrine-surface));
  border: var(--citrine-wfcanvas-line-width, 1px) solid var(--citrine-wfcanvas-line, var(--citrine-line));
  border-radius: var(--citrine-wfcanvas-radius, var(--citrine-r));
  overflow: auto;
  background-image: radial-gradient(
    var(--citrine-wfcanvas-grid-ink, var(--citrine-line-2)) 1.2px,
    transparent 1.2px
  );
  background-size: 22px 22px;
}

.citrine-wfcanvas:focus-visible {
  outline: var(--citrine-wfcanvas-focus-width, 2px) solid
    var(--citrine-wfcanvas-focus-ink, var(--citrine-data));
  outline-offset: -2px;
}

.citrine-wfcanvas-svg {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* `color`, not `stroke` — the connector path and its arrow marker both draw with `currentColor`,
     so one declaration colours the whole edge. */
  color: var(--citrine-wfedge-ink, var(--citrine-line-2));
  overflow: visible;
}

.citrine-wfedge-path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

.citrine-wfedge-label {
  font-size: var(--citrine-wfedge-label-size, var(--citrine-font-size-10p5));
  fill: var(--citrine-wfedge-label-ink, var(--citrine-ink-3));
}

.citrine-wfnode {
  position: absolute;
  min-width: 158px;
  padding: var(--citrine-wfnode-pad-y, var(--citrine-sp-3)) var(--citrine-wfnode-pad-x, var(--citrine-sp-4));
  background: var(--citrine-wfnode-bg, var(--citrine-surface));
  border: var(--citrine-wfnode-line-width, 1px) solid var(--citrine-wfnode-line, var(--citrine-line-2));
  border-radius: var(--citrine-wfnode-radius, var(--citrine-r-sm));
  text-align: center;
  box-shadow: var(--citrine-wfnode-shadow, var(--citrine-sh));
}

.citrine-wfnode[role='button'] {
  cursor: pointer;
}

.citrine-wfnode:focus-visible {
  outline: var(--citrine-wfnode-focus-width, 2px) solid
    var(--citrine-wfnode-selected-line, var(--citrine-data));
  outline-offset: 2px;
}

.citrine-wfnode.citrine-is-selected {
  border-color: var(--citrine-wfnode-selected-line, var(--citrine-data));
  box-shadow:
    0 0 0 3px var(--citrine-wfnode-selected-ring, var(--citrine-data-wash)),
    var(--citrine-wfnode-shadow, var(--citrine-sh));
}

.citrine-wfnode.citrine-is-dragging {
  opacity: 0.7;
  cursor: grabbing;
}

.citrine-wfnode-name {
  font-weight: var(--citrine-wfnode-name-weight, var(--citrine-font-weight-700));
  font-size: var(--citrine-wfnode-name-size, var(--citrine-font-size-13p5));
}

.citrine-wfnode-meta {
  margin-top: var(--citrine-wfnode-meta-top, var(--citrine-space-6));
  font-size: var(--citrine-wfnode-meta-size, var(--citrine-font-size-11p5));
  color: var(--citrine-wfnode-meta-ink, var(--citrine-ink-3));
}

/* A connection point. 10px at -6px is what puts it half-outside the node edge — geometry, not
   brand — so only the ring colour tracks the theme. */
.citrine-wfhandle {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--citrine-wfnode-bg, var(--citrine-surface));
  border: 2px solid var(--citrine-wfnode-selected-line, var(--citrine-data));
  transform: translateY(-50%);
}

.citrine-wfhandle-l {
  left: -6px;
}

.citrine-wfhandle-r {
  right: -6px;
}

.citrine-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── charts/Funnel3D ───────────────────────────────────────────────────────────────────── */
/* Charts — Funnel3D. Tokens only; a stage's own colour is data, set inline as `--citrine-f3d-c`
   on its band group and on the detail strip.

   Hover effects sit behind `@media (hover: hover)`: on a touch screen a tap leaves `:hover` stuck on
   the band, and the selection alone must say which band is chosen. */

.citrine-f3d {
  font-family: var(--citrine-sans);
}

.citrine-f3d-stage {
  cursor: pointer;
  transition: opacity var(--citrine-duration-150);
}

.citrine-f3d-stage:focus {
  outline: none;
}

.citrine-f3d-band {
  transition:
    transform var(--citrine-duration-150),
    filter var(--citrine-duration-150);
  transform-box: fill-box;
  transform-origin: center;
}

/* Focus: an ink outline on the band itself — an SVG shape has no box for `outline`. */
.citrine-f3d-stage:focus-visible .citrine-f3d-band {
  stroke: var(--citrine-ink);
  stroke-width: var(--citrine-space-2);
}

/* The count and its unit, on the band: white by default, a dark shade of the band's own colour
   on a light band (`citrine-f3d-on-light`, decided from the colour's luminance). */
.citrine-f3d-count,
.citrine-f3d-unit,
.citrine-f3d-bandname {
  text-anchor: middle;
  fill: var(--citrine-ink-on-solid);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

.citrine-f3d-on-light .citrine-f3d-count,
.citrine-f3d-on-light .citrine-f3d-unit,
.citrine-f3d-on-light .citrine-f3d-bandname {
  fill: color-mix(in srgb, var(--citrine-f3d-c) 22%, black);
}

/* Type matches the dashboard's stage cards (`Launcher`): the figure at 700, the label at 13/600.
   Never above 700 — the kit's sans has no heavier weight, and a browser fakes one by smearing. */
.citrine-f3d-count {
  font-size: var(--citrine-font-size-22);
  font-weight: var(--citrine-font-weight-700);
}

.citrine-f3d-unit {
  font-size: var(--citrine-font-size-11);
  font-weight: var(--citrine-font-weight-600);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.92;
}

/* Compact (a phone): the stage's name on its own band, under the count. */
.citrine-f3d-bandname {
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-600);
}

.citrine-f3d-leader {
  stroke: var(--citrine-line-2);
  stroke-width: var(--citrine-space-1);
  stroke-dasharray: 2 4;
}

.citrine-f3d-dot {
  fill: var(--citrine-f3d-c);
  stroke: var(--citrine-surface);
  stroke-width: 3;
  transition: r var(--citrine-duration-150);
}

.citrine-f3d-name {
  font-size: var(--citrine-font-size-13);
  font-weight: var(--citrine-font-weight-600);
  fill: var(--citrine-ink);
}

.citrine-f3d-info {
  font-size: var(--citrine-font-size-12);
  fill: var(--citrine-ink-3);
  font-variant-numeric: tabular-nums;
}

/* The chosen band: lifted, its dot grown. */
.citrine-f3d-stage.citrine-is-on .citrine-f3d-band {
  transform: scaleX(1.06);
  filter: brightness(1.07) drop-shadow(0 6px 10px color-mix(in srgb, var(--citrine-ink) 22%, transparent));
}

.citrine-f3d-stage.citrine-is-on .citrine-f3d-dot {
  r: 11;
}

/* A desk pointer: the band under it lifts and the rest dim. While one is selected and the pointer
   is elsewhere, the selection does the same. */
@media (hover: hover) {
  .citrine-f3d-stage:hover .citrine-f3d-band {
    transform: scaleX(1.06);
    filter: brightness(1.07) drop-shadow(0 6px 10px color-mix(in srgb, var(--citrine-ink) 22%, transparent));
  }

  .citrine-f3d-stage:hover .citrine-f3d-dot {
    r: 11;
  }

  .citrine-f3d:has(.citrine-f3d-stage:hover) .citrine-f3d-stage:not(:hover),
  .citrine-f3d-is-selected:not(:has(.citrine-f3d-stage:hover)) .citrine-f3d-stage:not(.citrine-is-on) {
    opacity: 0.32;
  }
}

/* A touch screen: only the selection dims. */
@media (hover: none) {
  .citrine-f3d-is-selected .citrine-f3d-stage:not(.citrine-is-on) {
    opacity: 0.32;
  }
}

@media (prefers-reduced-motion: reduce) {
  .citrine-f3d-stage,
  .citrine-f3d-band,
  .citrine-f3d-dot {
    transition: none;
  }
}

/* ── The detail strip under the funnel ────────────────────────────────────────────────────────── */

.citrine-f3d-detail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--citrine-sp-4) var(--citrine-space-28);
  margin-block: var(--citrine-sp-2) var(--citrine-sp-1);
  padding: var(--citrine-sp-3) var(--citrine-sp-4);
  border: 1px solid var(--citrine-line);
  border-inline-start: 4px solid var(--citrine-f3d-c);
  border-radius: var(--citrine-r);
  background: color-mix(in srgb, var(--citrine-f3d-c) 7%, var(--citrine-surface));
}

.citrine-f3d-detail-head {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  min-inline-size: 180px;
}

.citrine-f3d-detail-dot {
  flex: none;
  inline-size: 14px;
  block-size: 14px;
  border-radius: 50%;
  background: var(--citrine-f3d-c);
}

.citrine-f3d-cell-label {
  font-size: var(--citrine-font-size-12);
  color: var(--citrine-ink-3);
}

.citrine-f3d-cell-value {
  font-size: var(--citrine-font-size-16);
  font-weight: var(--citrine-font-weight-600);
  color: var(--citrine-ink);
  font-variant-numeric: tabular-nums;
}

.citrine-f3d-detail-actions {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-2);
  margin-inline-start: auto;
}

/* ── charts/shared ─────────────────────────────────────────────────────────────────────── */
/* Charts — the frame, axes, marks, legend and tooltip every chart in `./charts` draws with.
   Series colours are not here: each mark carries its own `var(--citrine-*)` fill inline (see
   `palette.ts`), so the token resolves where the mark sits. */

.citrine-chart {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--citrine-sp-1);
  min-width: 0;
  font-family: var(--citrine-sans);
  color: var(--citrine-ink-2);
}

/* Legend beside the plot (`legend={{ position: 'left' | 'right' }}`). */
.citrine-chart-side {
  flex-direction: row;
  align-items: center;
}

.citrine-chart-plot {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.citrine-chart-svg {
  display: block;
  overflow: visible;
}

/* ── Axes ───────────────────────────────────────────────────────────────────────────────────── */

.citrine-chart-tick {
  font-size: var(--citrine-chart-tick-size);
  fill: var(--citrine-ink-3);
  font-variant-numeric: tabular-nums;
}

.citrine-chart-axis-title {
  font-size: var(--citrine-font-size-12);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink-2);
}

.citrine-chart-axis-line {
  stroke: var(--citrine-line);
  stroke-width: var(--citrine-space-1);
  shape-rendering: crispEdges;
}

.citrine-chart-grid {
  stroke: var(--citrine-line-2);
  stroke-width: var(--citrine-space-1);
  shape-rendering: crispEdges;
}

/* ── Marks ──────────────────────────────────────────────────────────────────────────────────── */

.citrine-chart-mark-clickable {
  cursor: pointer;
}

/* Focus is drawn as an ink outline on the mark itself: an SVG shape has no box for `outline`. */
.citrine-chart-mark:focus {
  outline: none;
}

.citrine-chart-mark:focus-visible {
  stroke: var(--citrine-ink);
  stroke-width: var(--citrine-space-2);
}

.citrine-chart-slice,
.citrine-chart-point {
  stroke: var(--citrine-surface);
  stroke-width: var(--citrine-space-1);
}

.citrine-chart-line {
  fill: none;
  stroke-width: var(--citrine-chart-line-width);
  stroke-linejoin: round;
  stroke-linecap: round;
}

.citrine-chart-area {
  fill-opacity: var(--citrine-chart-area-opacity);
  stroke: none;
}

.citrine-chart-hit {
  fill: transparent;
}

.citrine-chart-guide {
  stroke: var(--citrine-line);
  stroke-width: var(--citrine-space-1);
}

.citrine-chart-value {
  font-size: var(--citrine-chart-tick-size);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink-2);
  font-variant-numeric: tabular-nums;
}

/* ── Legend ─────────────────────────────────────────────────────────────────────────────────── */

.citrine-chart-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: var(--citrine-sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.citrine-chart-legend-side {
  flex: none;
  flex-direction: column;
  flex-wrap: nowrap;
  max-width: 40%;
}

.citrine-chart-legend-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--citrine-sp-1);
  min-block-size: var(--citrine-target-min);
  max-width: 100%;
  margin: 0;
  padding: 0 var(--citrine-sp-1);
  border: 0;
  border-radius: var(--citrine-r-xs);
  background: none;
  font: inherit;
  font-size: var(--citrine-font-size-12);
  color: var(--citrine-ink-2);
  text-align: start;
  cursor: pointer;
}

.citrine-chart-legend-btn:focus-visible {
  outline: var(--citrine-space-2) solid var(--citrine-focus-ring);
  outline-offset: var(--citrine-space-1);
}

/* A hidden series reads as switched off by more than its colour: its name is struck through. */
.citrine-chart-legend-btn[aria-pressed='false'] .citrine-chart-legend-name {
  color: var(--citrine-ink-3);
  text-decoration: line-through;
}

.citrine-chart-legend-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.citrine-chart-swatch {
  flex: none;
  inline-size: var(--citrine-space-10);
  block-size: var(--citrine-space-10);
  border-radius: var(--citrine-radius-2);
}

/* ── Tooltip ────────────────────────────────────────────────────────────────────────────────── */

.citrine-chart-tip {
  position: absolute;
  z-index: var(--citrine-z-raised);
  padding: var(--citrine-sp-1) var(--citrine-sp-2);
  border: var(--citrine-space-1) solid var(--citrine-line);
  border-radius: var(--citrine-r-xs);
  background: var(--citrine-surface);
  box-shadow: var(--citrine-sh);
  color: var(--citrine-ink);
  font-size: var(--citrine-font-size-12);
  white-space: nowrap;
  pointer-events: none;
  transform: translate(-50%, calc(-100% - var(--citrine-sp-2)));
}

.citrine-chart-tip-start {
  transform: translate(0, calc(-100% - var(--citrine-sp-2)));
}

.citrine-chart-tip-end {
  transform: translate(-100%, calc(-100% - var(--citrine-sp-2)));
}

.citrine-chart-tip-title {
  margin-block-end: var(--citrine-space-4);
  font-weight: var(--citrine-font-weight-600);
}

.citrine-chart-tip-rows {
  display: grid;
  gap: var(--citrine-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.citrine-chart-tip-row {
  display: flex;
  align-items: center;
  gap: var(--citrine-sp-1);
}

.citrine-chart-tip-name {
  color: var(--citrine-ink-2);
}

.citrine-chart-tip-value {
  margin-inline-start: auto;
  padding-inline-start: var(--citrine-sp-3);
  font-weight: var(--citrine-font-weight-600);
  font-variant-numeric: tabular-nums;
}

/* ── Annotations (reference lines and bands) ────────────────────────────────────────────────── */

/* They are part of the picture, never a target: the marks under and over them stay reachable. */
.citrine-chart-annotations {
  pointer-events: none;
}

.citrine-chart-annotation-line {
  stroke: var(--citrine-ink-3);
  stroke-width: var(--citrine-space-1);
  stroke-dasharray: var(--citrine-space-4) var(--citrine-space-3);
  shape-rendering: crispEdges;
}

.citrine-chart-annotation-band {
  fill: var(--citrine-data-wash);
}

.citrine-chart-annotation-label {
  font-size: var(--citrine-chart-tick-size);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink-2);
}

/* ── Centre content (a donut's total) ───────────────────────────────────────────────────────── */

.citrine-chart-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.citrine-chart-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  overflow-wrap: break-word;
  color: var(--citrine-ink);
}

.citrine-chart-statistic-title {
  font-size: var(--citrine-font-size-12);
  color: var(--citrine-ink-3);
}

.citrine-chart-statistic-value {
  font-size: var(--citrine-font-size-22);
  font-weight: var(--citrine-font-weight-600);
  font-variant-numeric: tabular-nums;
}

/* ── Funnel, Treemap, Heatmap, Sankey, Waterfall ────────────────────────────────────────────── */

.citrine-chart-stage-name {
  font-size: var(--citrine-chart-tick-size);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink-2);
}

.citrine-chart-conversion {
  font-size: var(--citrine-chart-tick-size);
  fill: var(--citrine-ink-3);
  font-variant-numeric: tabular-nums;
}

.citrine-chart-cell,
.citrine-chart-tile {
  stroke: var(--citrine-surface);
  stroke-width: var(--citrine-space-1);
}

/* Text painted on a solid mark colour (a tile, a dark heatmap cell). */
.citrine-chart-on-solid {
  font-size: var(--citrine-chart-tick-size);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink-on-solid);
  pointer-events: none;
}

.citrine-chart-on-wash {
  font-size: var(--citrine-chart-tick-size);
  font-weight: var(--citrine-font-weight-500);
  fill: var(--citrine-ink);
  pointer-events: none;
}

.citrine-chart-node {
  stroke: none;
}

/* A Sankey flow: a translucent ribbon, deepened under the pointer or focus. */
.citrine-chart-link {
  fill-opacity: var(--citrine-chart-area-opacity);
}

.citrine-chart-link:hover,
.citrine-chart-link:focus-visible {
  fill-opacity: calc(var(--citrine-chart-area-opacity) * 3);
}

.citrine-chart-connector {
  stroke: var(--citrine-ink-4);
  stroke-width: var(--citrine-space-1);
  stroke-dasharray: var(--citrine-space-2) var(--citrine-space-2);
  shape-rendering: crispEdges;
}

.citrine-chart-scale-label {
  font-size: var(--citrine-chart-tick-size);
  fill: var(--citrine-ink-3);
  font-variant-numeric: tabular-nums;
}

/* `rounded` on Column / Bar. `rx` / `ry` are SVG geometry properties CSS can set, so the radius
   stays a token. A bar thinner than twice the radius is clamped by the browser, never inverted. */
.citrine-chart-bar-rounded {
  rx: var(--citrine-chart-bar-radius);
  ry: var(--citrine-chart-bar-radius);
}
