@loworbitstudio/visor 1.21.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -249,7 +249,7 @@
249
249
  {
250
250
  "path": "components/ui/checkbox/checkbox.module.css",
251
251
  "type": "registry:ui",
252
- "content": "/* Checkbox root styles\n Default rendering uses canonical values directly. Under data-density=\"editorial\"\n (set on any ancestor), the editorial density axis bakes in the blessed admin\n values: 1.125rem box, 4px radius, var(--surface-subtle) background, and\n var(--hairline-strong) border. */\n.root {\n position: relative;\n display: flex;\n width: 1rem;\n height: 1rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: 1px solid var(--border-default, #e5e7eb);\n background-color: transparent;\n transition: border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), box-shadow var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n cursor: pointer;\n}\n\n:global([data-density=\"editorial\"]) .root {\n width: 1.125rem;\n height: 1.125rem;\n border-radius: 4px;\n border-color: var(--hairline-strong, #e5e7eb);\n background-color: var(--surface-subtle);\n}\n\n@media (hover: hover) {\n .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]):not([data-state=\"checked\"]):not([data-state=\"indeterminate\"]) {\n border-color: var(--border-strong, #d1d5db);\n background-color: transparent;\n }\n}\n\n@media (hover: hover) {\n :global([data-density=\"editorial\"]) .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]):not([data-state=\"checked\"]):not([data-state=\"indeterminate\"]) {\n background-color: var(--surface-subtle);\n }\n}\n\n.root:focus-visible {\n border-color: var(--border-focus, #111827);\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-focus, #111827) 15%, transparent);\n}\n\n.root:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.root[aria-invalid=\"true\"] {\n border-color: var(--border-error, #ef4444);\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-error, #ef4444) 15%, transparent);\n}\n\n.root[data-state=\"checked\"] {\n border-color: var(--checkbox-border-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n background-color: var(--checkbox-bg-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n color: var(--interactive-primary-text, #f9fafb);\n}\n\n.root[data-state=\"indeterminate\"] {\n border-color: var(--checkbox-border-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n background-color: var(--checkbox-bg-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n color: var(--interactive-primary-text, #f9fafb);\n}\n\n/* Indicator */\n.indicator {\n display: grid;\n place-content: center;\n color: currentColor;\n transition: none;\n}\n\n.icon {\n width: 0.875rem;\n height: 0.875rem;\n}\n"
252
+ "content": "/* Checkbox root styles\n Default rendering uses canonical values directly. Under data-density=\"editorial\"\n (set on any ancestor), the editorial density axis bakes in the blessed admin\n values: 1.125rem box, 4px radius, var(--surface-subtle) background, and\n var(--hairline-strong) border. */\n.root {\n position: relative;\n display: flex;\n width: var(--checkbox-size, 1rem);\n height: var(--checkbox-size, 1rem);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--checkbox-radius, var(--radius-sm, 0.25rem));\n border: var(--checkbox-border, 1px solid var(--border-default, #e5e7eb));\n background-color: var(--checkbox-bg, transparent);\n transition: border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), box-shadow var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n cursor: pointer;\n}\n\n:global([data-density=\"editorial\"]) .root {\n width: 1.125rem;\n height: 1.125rem;\n border-radius: 4px;\n border-color: var(--hairline-strong, #e5e7eb);\n background-color: var(--surface-subtle);\n}\n\n@media (hover: hover) {\n .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]):not([data-state=\"checked\"]):not([data-state=\"indeterminate\"]) {\n border-color: var(--border-strong, #d1d5db);\n background-color: transparent;\n }\n}\n\n@media (hover: hover) {\n :global([data-density=\"editorial\"]) .root:hover:not(:focus-visible):not(:disabled):not([aria-invalid=\"true\"]):not([data-state=\"checked\"]):not([data-state=\"indeterminate\"]) {\n background-color: var(--surface-subtle);\n }\n}\n\n.root:focus-visible {\n border-color: var(--border-focus, #111827);\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-focus, #111827) 15%, transparent);\n}\n\n.root:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.root[aria-invalid=\"true\"] {\n border-color: var(--border-error, #ef4444);\n box-shadow: 0 0 0 var(--focus-ring-width, 2px) color-mix(in srgb, var(--border-error, #ef4444) 15%, transparent);\n}\n\n.root[data-state=\"checked\"] {\n border-color: var(--checkbox-border-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n background-color: var(--checkbox-bg-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n color: var(--interactive-primary-text, #f9fafb);\n}\n\n.root[data-state=\"indeterminate\"] {\n border-color: var(--checkbox-border-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n background-color: var(--checkbox-bg-checked, var(--interactive-primary-bg, var(--primary, #111827)));\n color: var(--interactive-primary-text, #f9fafb);\n}\n\n/* Indicator */\n.indicator {\n display: grid;\n place-content: center;\n color: currentColor;\n transition: none;\n}\n\n.icon {\n width: 0.875rem;\n height: 0.875rem;\n}\n"
253
253
  }
254
254
  ]
255
255
  },
@@ -401,7 +401,7 @@
401
401
  {
402
402
  "path": "components/ui/badge/badge.module.css",
403
403
  "type": "registry:ui",
404
- "content": "/* Badge base — sizing (height, padding, font-size, gap) is driven by the\n * size classes below. Height is intrinsic (padding + line-height) so badges\n * stay correct when text wraps or icon slots are introduced. */\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n flex-shrink: 0;\n overflow: hidden;\n border-radius: var(--radius-full, 9999px);\n border: 1px solid transparent;\n line-height: 1;\n /* Typography defaults. Under data-density=\"editorial\" on any ancestor,\n the density rules below override to uppercase/heavier/tracked. */\n font-weight: var(--font-weight-medium, 500);\n text-transform: none;\n letter-spacing: normal;\n white-space: nowrap;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Editorial density: uppercase + heavier + tracked. Any ancestor with\n * data-density=\"editorial\" (for example the app root) activates this. */\n:global([data-density=\"editorial\"]) .base {\n /* Theme-invariant: the editorial treatment runs 600 even when a theme maps\n --font-weight-semibold heavier (entr: 700). density-literal */\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n/* Editorial density opt-out: sizeLg is intrinsically a label (never tag),\n * and caseSentence lets sm/md opt out per-usage. Both must win over the\n * editorial .base rule above (same specificity, placed after). */\n:global([data-density=\"editorial\"]) .sizeLg,\n:global([data-density=\"editorial\"]) .caseSentence {\n text-transform: none;\n letter-spacing: 0;\n}\n\n/* uppercase prop — directly sets text-transform so it composes cleanly with\n * the density axis. Specificity (0,2,0), placed after .base, wins over default. */\n.base.uppercase {\n text-transform: uppercase;\n}\n\n.base:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Sizes — token-driven padding + font-size + gap on the 4px grid.\n * No --spacing-1.5 token exists, so the 6px step uses\n * calc(--spacing-1 + --spacing-1/2) — mirrors the pattern used in kbd. */\n.sizeSm {\n gap: var(--spacing-1, 0.25rem);\n padding: 0 calc(var(--spacing-1, 0.25rem) + var(--spacing-1, 0.25rem) / 2);\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n/* md — matches canonical rendering (1.25rem total height). Under\n data-density=\"editorial\", font-size steps down to 11px. */\n.sizeMd {\n gap: var(--spacing-1, 0.25rem);\n padding: calc(var(--spacing-1, 0.25rem) / 2) var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n:global([data-density=\"editorial\"]) .sizeMd {\n font-size: 11px;\n}\n\n.sizeLg {\n gap: calc(var(--spacing-1, 0.25rem) + var(--spacing-1, 0.25rem) / 2);\n padding: var(--spacing-1, 0.25rem) var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Sentence case — opt OUT of any uppercase/tracking applied by the density\n * axis or the uppercase prop. The large size always opts out (it is\n * intrinsically a label); the .caseSentence class lets sm/md opt out\n * per-usage. The editorial-density opt-out mirror lives above, after the\n * editorial .base rule, ensuring it wins at equal specificity. */\n.sizeLg,\n.caseSentence {\n text-transform: none;\n letter-spacing: 0;\n}\n\n/* Icon scaling — leading glyphs track the per-size font-size step. */\n.sizeSm > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n.sizeMd > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.sizeLg > svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* Variants */\n.variantDefault {\n background-color: var(--interactive-primary-bg, currentColor);\n color: var(--interactive-primary-text, var(--text-inverse, #f9fafb));\n border-color: transparent;\n}\n\n.variantSecondary {\n background-color: var(--interactive-secondary-bg, var(--surface-subtle, transparent));\n color: var(--interactive-secondary-text, var(--text-primary, currentColor));\n border-color: transparent;\n}\n\n/* Editorial density: secondary surfaces sit on the card tier. Direct value —\n * the editorial design language owns control surfaces, so the theme's\n * --interactive-secondary-bg is deliberately bypassed (matches the blessed\n * overlay, which out-cascaded the palette at :root). */\n:global([data-density=\"editorial\"]) .variantSecondary {\n background-color: var(--surface-card, #ffffff);\n}\n\n.variantOutline {\n background-color: transparent;\n color: var(--text-primary, #111827);\n border-color: var(--border-default, #e5e7eb);\n}\n\n/* Tonal variants — canonical surface tokens as plain values. Under\n * data-density=\"editorial\", backgrounds switch to color-mix tints for\n * higher saturation at editorial admin density. Text colors are unchanged. */\n.variantDestructive {\n background-color: var(--surface-error-subtle, transparent);\n color: var(--text-error, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantDestructive {\n background-color: color-mix(in srgb, var(--destructive) 20%, transparent);\n}\n\n.variantSuccess {\n background-color: var(--surface-success-subtle, transparent);\n color: var(--text-success, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantSuccess {\n background-color: color-mix(in srgb, var(--success) 18%, transparent);\n}\n\n.variantWarning {\n background-color: var(--surface-warning-subtle, transparent);\n color: var(--text-warning, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantWarning {\n background-color: color-mix(in srgb, var(--warning) 20%, transparent);\n}\n\n.variantInfo {\n background-color: var(--surface-info-subtle, transparent);\n color: var(--text-info, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantInfo {\n background-color: color-mix(in srgb, var(--info) 18%, transparent);\n}\n\n.variantNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-secondary, var(--text-primary, #374151));\n border-color: transparent;\n}\n\n/* Filled variants — step 9 bg + contrast text */\n.variantFilledDestructive {\n background-color: var(--surface-error-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledSuccess {\n background-color: var(--surface-success-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledWarning {\n background-color: var(--surface-warning-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledInfo {\n background-color: var(--surface-info-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n/* filled-neutral — the saturated counterpart to `neutral`. Neutral has no\n * brand `-default` surface, so it uses a fixed neutral-600 fill. Because that\n * fill is dark in BOTH modes, text must stay white (not `--text-inverse`,\n * which flips to a dark value in dark mode). */\n.variantFilledNeutral {\n background-color: var(--color-neutral-600, #4b5563);\n color: var(--color-white, #ffffff);\n border-color: transparent;\n}\n\n/* Icon-only — a circular, fixed-square chip carrying a single centered glyph\n * (for example the filled-success check used by the Roles matrix active cell +\n * legend). Zero padding and a fixed glyph size keep it crisp at any density.\n * The glyph centers via the base flex; per-size svg rules are overridden here. */\n.iconOnly {\n width: 1.375rem;\n height: 1.375rem;\n min-width: 1.375rem;\n padding: 0;\n gap: 0;\n border-radius: var(--radius-full, 9999px);\n}\n\n.iconOnly > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n"
404
+ "content": "/* Badge base — sizing (height, padding, font-size, gap) is driven by the\n * size classes below. Height is intrinsic (padding + line-height) so badges\n * stay correct when text wraps or icon slots are introduced. */\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n flex-shrink: 0;\n overflow: hidden;\n border-radius: var(--badge-radius, var(--radius-full, 9999px));\n border: var(--badge-border, 1px solid transparent);\n line-height: 1;\n /* Typography defaults. Under data-density=\"editorial\" on any ancestor,\n the density rules below override to uppercase/heavier/tracked.\n VI-625: the D3 Tier-2 badge treatment (--badge-text-transform /\n -letter-spacing / -font-size / -font-weight) is bound here. */\n font-family: var(--badge-font-family, inherit);\n font-weight: var(--badge-font-weight, var(--font-weight-medium, 500));\n text-transform: var(--badge-text-transform, none);\n letter-spacing: var(--badge-letter-spacing, normal);\n white-space: nowrap;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Editorial density: uppercase + heavier + tracked. Any ancestor with\n * data-density=\"editorial\" (for example the app root) activates this. */\n:global([data-density=\"editorial\"]) .base {\n /* Theme-invariant: the editorial treatment runs 600 even when a theme maps\n --font-weight-semibold heavier (entr: 700). density-literal */\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n/* Editorial density opt-out: sizeLg is intrinsically a label (never tag),\n * and caseSentence lets sm/md opt out per-usage. Both must win over the\n * editorial .base rule above (same specificity, placed after). */\n:global([data-density=\"editorial\"]) .sizeLg,\n:global([data-density=\"editorial\"]) .caseSentence {\n text-transform: none;\n letter-spacing: 0;\n}\n\n/* uppercase prop — directly sets text-transform so it composes cleanly with\n * the density axis. Specificity (0,2,0), placed after .base, wins over default. */\n.base.uppercase {\n text-transform: uppercase;\n}\n\n.base:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Sizes — token-driven padding + font-size + gap on the 4px grid.\n * No --spacing-1.5 token exists, so the 6px step uses\n * calc(--spacing-1 + --spacing-1/2) — mirrors the pattern used in kbd. */\n.sizeSm {\n gap: var(--spacing-1, 0.25rem);\n padding: 0 calc(var(--spacing-1, 0.25rem) + var(--spacing-1, 0.25rem) / 2);\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n/* md — matches canonical rendering (1.25rem total height). Under\n data-density=\"editorial\", font-size steps down to 11px. A theme binding\n --badge-font-size retunes BOTH steps, which is the point: the badge type\n size becomes one dial instead of two. */\n.sizeMd {\n gap: var(--badge-md-gap, var(--spacing-1, 0.25rem));\n padding: var(--badge-md-padding, calc(var(--spacing-1, 0.25rem) / 2) var(--spacing-2, 0.5rem));\n font-size: var(--badge-font-size, var(--font-size-xs, 0.75rem));\n}\n\n:global([data-density=\"editorial\"]) .sizeMd {\n font-size: var(--badge-font-size, 11px);\n}\n\n.sizeLg {\n gap: calc(var(--spacing-1, 0.25rem) + var(--spacing-1, 0.25rem) / 2);\n padding: var(--spacing-1, 0.25rem) var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Sentence case — opt OUT of any uppercase/tracking applied by the density\n * axis or the uppercase prop. The large size always opts out (it is\n * intrinsically a label); the .caseSentence class lets sm/md opt out\n * per-usage. The editorial-density opt-out mirror lives above, after the\n * editorial .base rule, ensuring it wins at equal specificity. */\n.sizeLg,\n.caseSentence {\n text-transform: none;\n letter-spacing: 0;\n}\n\n/* Icon scaling — leading glyphs track the per-size font-size step. */\n.sizeSm > svg {\n width: 0.75rem;\n height: 0.75rem;\n}\n\n.sizeMd > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n\n.sizeLg > svg {\n width: 1rem;\n height: 1rem;\n}\n\n/* Variants */\n.variantDefault {\n background-color: var(--interactive-primary-bg, currentColor);\n color: var(--interactive-primary-text, var(--text-inverse, #f9fafb));\n border-color: transparent;\n}\n\n.variantSecondary {\n background-color: var(--interactive-secondary-bg, var(--surface-subtle, transparent));\n color: var(--interactive-secondary-text, var(--text-primary, currentColor));\n border-color: transparent;\n}\n\n/* Editorial density: secondary surfaces sit on the card tier. Direct value —\n * the editorial design language owns control surfaces, so the theme's\n * --interactive-secondary-bg is deliberately bypassed (matches the blessed\n * overlay, which out-cascaded the palette at :root). */\n:global([data-density=\"editorial\"]) .variantSecondary {\n background-color: var(--surface-card, #ffffff);\n}\n\n.variantOutline {\n background-color: transparent;\n color: var(--text-primary, #111827);\n border-color: var(--border-default, #e5e7eb);\n}\n\n/* Tonal variants — canonical surface tokens as plain values. Under\n * data-density=\"editorial\", backgrounds switch to color-mix tints for\n * higher saturation at editorial admin density. Text colors are unchanged. */\n.variantDestructive {\n background-color: var(--surface-error-subtle, transparent);\n color: var(--text-error, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantDestructive {\n background-color: color-mix(in srgb, var(--destructive) 20%, transparent);\n}\n\n.variantSuccess {\n background-color: var(--surface-success-subtle, transparent);\n color: var(--text-success, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantSuccess {\n background-color: color-mix(in srgb, var(--success) 18%, transparent);\n}\n\n.variantWarning {\n background-color: var(--surface-warning-subtle, transparent);\n color: var(--text-warning, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantWarning {\n background-color: color-mix(in srgb, var(--warning) 20%, transparent);\n}\n\n.variantInfo {\n background-color: var(--surface-info-subtle, transparent);\n color: var(--text-info, currentColor);\n border-color: transparent;\n}\n\n:global([data-density=\"editorial\"]) .variantInfo {\n background-color: color-mix(in srgb, var(--info) 18%, transparent);\n}\n\n.variantNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-secondary, var(--text-primary, #374151));\n border-color: transparent;\n}\n\n/* Filled variants — step 9 bg + contrast text */\n.variantFilledDestructive {\n background-color: var(--surface-error-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledSuccess {\n background-color: var(--surface-success-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledWarning {\n background-color: var(--surface-warning-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n.variantFilledInfo {\n background-color: var(--surface-info-default, transparent);\n color: var(--text-inverse, currentColor);\n border-color: transparent;\n}\n\n/* filled-neutral — the saturated counterpart to `neutral`. Neutral has no\n * brand `-default` surface, so it uses a fixed neutral-600 fill. Because that\n * fill is dark in BOTH modes, text must stay white (not `--text-inverse`,\n * which flips to a dark value in dark mode). */\n.variantFilledNeutral {\n background-color: var(--color-neutral-600, #4b5563);\n color: var(--color-white, #ffffff);\n border-color: transparent;\n}\n\n/* Icon-only — a circular, fixed-square chip carrying a single centered glyph\n * (for example the filled-success check used by the Roles matrix active cell +\n * legend). Zero padding and a fixed glyph size keep it crisp at any density.\n * The glyph centers via the base flex; per-size svg rules are overridden here. */\n.iconOnly {\n width: 1.375rem;\n height: 1.375rem;\n min-width: 1.375rem;\n padding: 0;\n gap: 0;\n border-radius: var(--radius-full, 9999px);\n}\n\n.iconOnly > svg {\n width: 0.875rem;\n height: 0.875rem;\n}\n"
405
405
  }
406
406
  ]
407
407
  },
@@ -448,7 +448,7 @@
448
448
  {
449
449
  "path": "components/ui/chip/chip.module.css",
450
450
  "type": "registry:ui",
451
- "content": "/* Chip base */\n.base {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-1, 0.25rem);\n width: fit-content;\n flex-shrink: 0;\n border-radius: var(--radius-full, 9999px);\n border: var(--stroke-width-thin, 1px) solid var(--border-default, #e5e7eb);\n background-color: var(--surface-card, #ffffff);\n color: var(--text-primary, #111827);\n font-family: var(--font-body, inherit);\n font-weight: var(--font-weight-medium, 500);\n white-space: nowrap;\n user-select: none;\n transition:\n background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Variant: default (filled/tonal surface) */\n.variantDefault {\n background-color: var(--surface-muted, #f3f4f6);\n border-color: transparent;\n color: var(--text-primary, #111827);\n}\n\n/* Variant: outlined */\n.variantOutlined {\n background-color: transparent;\n border-color: var(--border-default, #e5e7eb);\n color: var(--text-primary, #111827);\n}\n\n/* Variant: filled-primary — saturated brand-essence chip treatment.\n * Primary background + primary-text foreground + subtle shadow.\n * 700 weight + slight negative tracking assert \"this IS a brand value\".\n * Use when the chip must carry primary brand prominence — essence chips\n * on the Brand Workbench Elicit canvas, strategy surfaces, brand decks. */\n.variantFilledPrimary {\n background-color: var(--primary, #111827);\n border-color: transparent;\n color: var(--primary-text, #f9fafb);\n font-weight: 700;\n letter-spacing: -0.01em;\n box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.05));\n}\n\n/* Sizes */\n.sizeSm {\n height: 1.5rem;\n padding: 0 var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeMd {\n height: 2rem;\n padding: 0 var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n gap: var(--spacing-1, 0.25rem);\n}\n\n/* Editorial density: tighter font + gap for md chips. */\n:global([data-density=\"editorial\"]) .sizeMd {\n font-size: var(--font-size-xs);\n gap: var(--spacing-2);\n}\n\n.sizeLg {\n height: 2.5rem;\n padding: 0 var(--spacing-4, 1rem);\n font-size: var(--font-size-base, 1rem);\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Sub-parts */\n.avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.leadingIcon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.deleteButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n color: var(--text-secondary, #6b7280);\n border-radius: var(--radius-full, 9999px);\n transition: color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n width: 1rem;\n height: 1rem;\n}\n\n@media (hover: hover) {\n .deleteButton:hover {\n color: var(--text-primary, #111827);\n background-color: color-mix(in srgb, var(--surface-interactive-hover, #f3f4f6) 60%, transparent);\n }\n}\n\n.deleteButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #6b7280);\n outline-offset: var(--focus-ring-offset, 1px);\n}\n\n.deleteIcon {\n display: block;\n}\n\n/* Count pill (FilterChip only). Under data-density=\"editorial\", it renders\n as a fixed circular semibold pill. */\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-width: auto;\n height: auto;\n border-radius: var(--radius-full, 9999px);\n padding: 0 var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n align-self: baseline;\n}\n\n:global([data-density=\"editorial\"]) .count {\n min-width: 1.25rem;\n height: 1.25rem;\n padding-inline: var(--spacing-1);\n font-weight: var(--font-weight-semibold);\n align-self: center;\n}\n\n.countNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-primary, #111827);\n}\n\n.countPrimary {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Accent selected state overrides count pill (default behaviour). */\n.selected .count {\n background-color: var(--surface-card, #ffffff);\n color: var(--text-link, #2563eb);\n}\n\n/* Editorial density: selected count uses solid primary pill. */\n:global([data-density=\"editorial\"]) .selected .count {\n background-color: var(--primary);\n color: var(--primary-text);\n}\n\n/* Solid mint count pill — editorial/neutral selected treatment */\n.countSolid {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n/* Trailing icon (FilterChip dropdown caret) — muted, after the count. */\n.trailing {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: var(--text-tertiary, #9ca3af);\n font-size: 0.875rem;\n line-height: 1;\n}\n\n/* Interactive chips (ChoiceChip, FilterChip) */\n.interactive {\n cursor: pointer;\n border: var(--stroke-width-thin, 1px) solid transparent;\n}\n\nbutton.base,\n.interactive {\n /* Inherit base layout; add button-specific resets */\n text-align: left;\n appearance: none;\n -webkit-appearance: none;\n}\n\n.interactive:disabled {\n cursor: not-allowed;\n opacity: var(--opacity-40, 0.4);\n pointer-events: none;\n}\n\n.interactive:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #6b7280);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n@media (hover: hover) {\n .interactive:hover:not(:disabled):not([data-selected=\"true\"]) {\n background-color: var(--surface-interactive-hover, #f3f4f6);\n border-color: var(--border-default, #e5e7eb);\n }\n}\n\n.interactive:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n/* Selected state. Under data-density=\"editorial\", switches to neutral-elevated\n treatment (surface-elev bg + transparent border + primary text). */\n.selected {\n background-color: var(--surface-accent-subtle, #eff6ff);\n border-color: var(--surface-accent-default, #3b82f6);\n color: var(--text-link, #2563eb);\n}\n\n/* Editorial density: neutral-elevated selected treatment.\n --surface-elev is a theme role; the color-mix fallback matches how the\n retired overlay derived it. */\n:global([data-density=\"editorial\"]) .selected {\n background: var(--surface-elev, color-mix(in srgb, var(--surface-card), var(--surface-muted, var(--surface-card))));\n border-color: transparent;\n color: var(--text-primary);\n}\n\n@media (hover: hover) {\n .interactive.selected:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--surface-accent-subtle, #eff6ff) 80%, var(--surface-interactive-hover, #f3f4f6) 20%);\n }\n}\n\n/* Editorial / neutral-elevated selected treatment\n * Uses --surface-card bg + neutral border instead of accent tint.\n * Compose with .selected to override the accent colours. */\n.selectedNeutral {\n background-color: var(--surface-card, #ffffff);\n border-color: var(--border-strong, #d1d5db);\n color: var(--text-primary, #111827);\n}\n\n/* Re-assert selectedNeutral under editorial density: the density .selected rule\n * is (0,2,0) and would otherwise override .selectedNeutral at (0,1,0). */\n:global([data-density=\"editorial\"]) .selectedNeutral {\n background-color: var(--surface-card, #ffffff);\n border-color: var(--border-strong, #d1d5db);\n color: var(--text-primary, #111827);\n}\n\n.selectedNeutral .count {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n/* Re-assert selectedNeutral count under editorial density: the density\n * .selected .count rule at (0,3,0) would otherwise override at (0,2,0). */\n:global([data-density=\"editorial\"]) .selectedNeutral .count {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n@media (hover: hover) {\n .interactive.selectedNeutral:hover:not(:disabled) {\n background-color: var(--surface-interactive-hover, #f3f4f6);\n }\n}\n\n/* FilterChip resting (unselected) surface. Under editorial density, uses\n --surface-elev so the filter row reads as one consistent elevated set. */\n.filterChip[data-selected=\"false\"] {\n background-color: var(--surface-muted, #f3f4f6);\n}\n\n:global([data-density=\"editorial\"]) .filterChip[data-selected=\"false\"] {\n background-color: var(--surface-elev, color-mix(in srgb, var(--surface-card), var(--surface-muted, var(--surface-card))));\n}\n"
451
+ "content": "/* Chip base */\n.base {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-1, 0.25rem);\n width: fit-content;\n flex-shrink: 0;\n border-radius: var(--chip-radius, var(--radius-full, 9999px));\n border: var(--chip-border, var(--stroke-width-thin, 1px) solid var(--border-default, #e5e7eb));\n background-color: var(--chip-bg, var(--surface-card, #ffffff));\n color: var(--chip-text-color, var(--text-primary, #111827));\n font-family: var(--chip-font-family, var(--font-body, inherit));\n font-weight: var(--chip-font-weight, var(--font-weight-medium, 500));\n letter-spacing: var(--chip-letter-spacing, inherit);\n text-transform: var(--chip-text-transform, inherit);\n white-space: nowrap;\n user-select: none;\n transition:\n background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Variant: default (filled/tonal surface) */\n.variantDefault {\n background-color: var(--surface-muted, #f3f4f6);\n border-color: transparent;\n color: var(--text-primary, #111827);\n}\n\n/* Variant: outlined */\n.variantOutlined {\n background-color: transparent;\n border-color: var(--border-default, #e5e7eb);\n color: var(--text-primary, #111827);\n}\n\n/* Variant: filled-primary — saturated brand-essence chip treatment.\n * Primary background + primary-text foreground + subtle shadow.\n * 700 weight + slight negative tracking assert \"this IS a brand value\".\n * Use when the chip must carry primary brand prominence — essence chips\n * on the Brand Workbench Elicit canvas, strategy surfaces, brand decks. */\n.variantFilledPrimary {\n background-color: var(--primary, #111827);\n border-color: transparent;\n color: var(--primary-text, #f9fafb);\n font-weight: 700;\n letter-spacing: -0.01em;\n box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.05));\n}\n\n/* Sizes */\n.sizeSm {\n height: var(--chip-sm-height, 1.5rem);\n padding: 0 var(--chip-sm-padding-x, var(--spacing-2, 0.5rem));\n font-size: var(--chip-sm-font-size, var(--font-size-xs, 0.75rem));\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeMd {\n height: var(--chip-md-height, 2rem);\n padding: 0 var(--chip-md-padding-x, var(--spacing-3, 0.75rem));\n font-size: var(--chip-md-font-size, var(--font-size-sm, 0.875rem));\n gap: var(--spacing-1, 0.25rem);\n}\n\n/* Editorial density: tighter font + gap for md chips. */\n:global([data-density=\"editorial\"]) .sizeMd {\n font-size: var(--font-size-xs);\n gap: var(--spacing-2);\n}\n\n.sizeLg {\n height: var(--chip-lg-height, 2.5rem);\n padding: 0 var(--chip-lg-padding-x, var(--spacing-4, 1rem));\n font-size: var(--chip-lg-font-size, var(--font-size-base, 1rem));\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Sub-parts */\n.avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.leadingIcon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.deleteButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n color: var(--text-secondary, #6b7280);\n border-radius: var(--radius-full, 9999px);\n transition: color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out),\n background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n width: 1rem;\n height: 1rem;\n}\n\n@media (hover: hover) {\n .deleteButton:hover {\n color: var(--text-primary, #111827);\n background-color: color-mix(in srgb, var(--surface-interactive-hover, #f3f4f6) 60%, transparent);\n }\n}\n\n.deleteButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #6b7280);\n outline-offset: var(--focus-ring-offset, 1px);\n}\n\n.deleteIcon {\n display: block;\n}\n\n/* Count pill (FilterChip only). Under data-density=\"editorial\", it renders\n as a fixed circular semibold pill. */\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-width: auto;\n height: auto;\n border-radius: var(--radius-full, 9999px);\n padding: 0 var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n align-self: baseline;\n}\n\n:global([data-density=\"editorial\"]) .count {\n min-width: 1.25rem;\n height: 1.25rem;\n padding-inline: var(--spacing-1);\n font-weight: var(--font-weight-semibold);\n align-self: center;\n}\n\n.countNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-primary, #111827);\n}\n\n.countPrimary {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Accent selected state overrides count pill (default behaviour). */\n.selected .count {\n background-color: var(--surface-card, #ffffff);\n color: var(--text-link, #2563eb);\n}\n\n/* Editorial density: selected count uses solid primary pill. */\n:global([data-density=\"editorial\"]) .selected .count {\n background-color: var(--primary);\n color: var(--primary-text);\n}\n\n/* Solid mint count pill — editorial/neutral selected treatment */\n.countSolid {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n/* Trailing icon (FilterChip dropdown caret) — muted, after the count. */\n.trailing {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: var(--text-tertiary, #9ca3af);\n font-size: 0.875rem;\n line-height: 1;\n}\n\n/* Interactive chips (ChoiceChip, FilterChip) */\n.interactive {\n cursor: pointer;\n border: var(--stroke-width-thin, 1px) solid transparent;\n}\n\nbutton.base,\n.interactive {\n /* Inherit base layout; add button-specific resets */\n text-align: left;\n appearance: none;\n -webkit-appearance: none;\n}\n\n.interactive:disabled {\n cursor: not-allowed;\n opacity: var(--opacity-40, 0.4);\n pointer-events: none;\n}\n\n.interactive:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #6b7280);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n@media (hover: hover) {\n .interactive:hover:not(:disabled):not([data-selected=\"true\"]) {\n background-color: var(--surface-interactive-hover, #f3f4f6);\n border-color: var(--border-default, #e5e7eb);\n }\n}\n\n.interactive:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n/* Selected state. Under data-density=\"editorial\", switches to neutral-elevated\n treatment (surface-elev bg + transparent border + primary text). */\n.selected {\n background-color: var(--chip-selected-bg, var(--surface-accent-subtle, #eff6ff));\n border-color: var(--chip-selected-border-color, var(--surface-accent-default, #3b82f6));\n color: var(--chip-selected-text-color, var(--text-link, #2563eb));\n}\n\n/* Editorial density: neutral-elevated selected treatment.\n --surface-elev is a theme role; the color-mix fallback matches how the\n retired overlay derived it. */\n:global([data-density=\"editorial\"]) .selected {\n background: var(--surface-elev, color-mix(in srgb, var(--surface-card), var(--surface-muted, var(--surface-card))));\n border-color: transparent;\n color: var(--text-primary);\n}\n\n@media (hover: hover) {\n .interactive.selected:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--surface-accent-subtle, #eff6ff) 80%, var(--surface-interactive-hover, #f3f4f6) 20%);\n }\n}\n\n/* Editorial / neutral-elevated selected treatment\n * Uses --surface-card bg + neutral border instead of accent tint.\n * Compose with .selected to override the accent colours. */\n.selectedNeutral {\n background-color: var(--surface-card, #ffffff);\n border-color: var(--border-strong, #d1d5db);\n color: var(--text-primary, #111827);\n}\n\n/* Re-assert selectedNeutral under editorial density: the density .selected rule\n * is (0,2,0) and would otherwise override .selectedNeutral at (0,1,0). */\n:global([data-density=\"editorial\"]) .selectedNeutral {\n background-color: var(--surface-card, #ffffff);\n border-color: var(--border-strong, #d1d5db);\n color: var(--text-primary, #111827);\n}\n\n.selectedNeutral .count {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n/* Re-assert selectedNeutral count under editorial density: the density\n * .selected .count rule at (0,3,0) would otherwise override at (0,2,0). */\n:global([data-density=\"editorial\"]) .selectedNeutral .count {\n background-color: var(--surface-success-default, #22c55e);\n color: var(--text-inverse, #ffffff);\n}\n\n@media (hover: hover) {\n .interactive.selectedNeutral:hover:not(:disabled) {\n background-color: var(--surface-interactive-hover, #f3f4f6);\n }\n}\n\n/* FilterChip resting (unselected) surface. Under editorial density, uses\n --surface-elev so the filter row reads as one consistent elevated set. */\n.filterChip[data-selected=\"false\"] {\n background-color: var(--surface-muted, #f3f4f6);\n}\n\n:global([data-density=\"editorial\"]) .filterChip[data-selected=\"false\"] {\n background-color: var(--surface-elev, color-mix(in srgb, var(--surface-card), var(--surface-muted, var(--surface-card))));\n}\n"
452
452
  }
453
453
  ]
454
454
  },
@@ -527,7 +527,7 @@
527
527
  {
528
528
  "path": "components/ui/skeleton/skeleton.module.css",
529
529
  "type": "registry:ui",
530
- "content": "/* Skeleton */\n@keyframes shimmer {\n from {\n background-position: -200% 0;\n }\n to {\n background-position: 200% 0;\n }\n}\n\n.skeleton {\n --_shimmer-duration: 1.5s;\n border-radius: var(--radius-md, 0.375rem);\n /* Default (light) gradient — theme-tunable, falls back to the muted\n surface ramp (Tailwind Gray) */\n background: linear-gradient(\n 90deg,\n var(--skeleton-from, #f3f4f6),\n var(--skeleton-to, #e5e7eb),\n var(--skeleton-from, #f3f4f6)\n );\n background-size: 200% 100%;\n animation: shimmer var(--_shimmer-duration) var(--motion-easing-linear, linear) infinite;\n}\n\n/* Density axis — editorial: dim, text-tinted gradient for dark admin surfaces */\n:global([data-density=\"editorial\"]) .skeleton {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--text-primary) 6%, transparent),\n color-mix(in srgb, var(--text-primary) 10%, transparent),\n color-mix(in srgb, var(--text-primary) 6%, transparent)\n );\n}\n\n/* ── Shape variants (VI-516, opt-in) ─────────────────────────────────────\n Additive modifier classes layered on top of `.skeleton` via `className`.\n They set only a default geometry (width / height / radius) so callers can\n still override via `style`/`className`. The fill + animation are inherited\n from `.skeleton`, so a shape never changes the base look — it only changes\n the silhouette. Each dimension is a token-backed custom property so a theme\n can retune the editorial admin's placeholder geometry without forking the\n component. Defaults match the approved org-management edge capture. */\n\n/* Logo plate — ~104×24, radius-sm. The org-name cell's brand-mark placeholder. */\n.shapeLogo {\n width: var(--skeleton-logo-width, 104px);\n height: var(--skeleton-logo-height, 24px);\n border-radius: var(--radius-sm, 0.25rem);\n}\n\n/* Badge pill — ~64×18, radius-full. The plan/status badge placeholder. */\n.shapePill {\n width: var(--skeleton-pill-width, 64px);\n height: var(--skeleton-pill-height, 18px);\n border-radius: var(--radius-full, 999px);\n}\n\n/* Circle — square footprint, fully rounded. Avatar / icon placeholder. */\n.shapeCircle {\n border-radius: var(--radius-full, 999px);\n}\n\n/* ── Content-shape-aware primitives (VI-584) ──────────────────────────────\n Height / radius / width helpers used internally by SkeletonList,\n SkeletonTable, and SkeletonDetail. All dimensions trace to tokens or are\n documented as intentional pixel values that mirror spec anatomy. */\n\n/* Bar heights */\n.skeletonLineH1 { height: 24px; border-radius: var(--radius-md, 0.375rem); }\n.skeletonLineHeading { height: 18px; border-radius: var(--radius-md, 0.375rem); }\n.skeletonLineBody { height: 14px; border-radius: var(--radius-sm, 0.25rem); }\n\n/* Avatar / thumbnail */\n.skeletonAvatar {\n width: 40px;\n height: 40px;\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Larger avatar for detail views */\n.skeletonAvatarLg {\n width: 64px;\n height: 64px;\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Badge pill used in list rows */\n.skeletonBadge {\n width: 72px;\n height: 22px;\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Width utilities — fractional widths for organic line-length variety */\n.skeletonW40 { width: 40%; }\n.skeletonW60 { width: 60%; }\n.skeletonW70 { width: 70%; }\n.skeletonW80 { width: 80%; }\n.skeletonWFull { width: 100%; }\n\n/* ── SkeletonList layout ─── */\n.skeletonList {\n display: flex;\n flex-direction: column;\n}\n\n.skeletonListRow {\n display: flex;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n padding: var(--spacing-3, 0.75rem) 0;\n border-bottom: 1px solid var(--border-default, #e5e7eb);\n}\n\n/* Remove bottom border on last row */\n.skeletonListRow:last-child {\n border-bottom: none;\n}\n\n.skeletonListText {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n flex: 1;\n}\n\n/* ── SkeletonTable layout ─── */\n.skeletonTable {\n display: flex;\n flex-direction: column;\n gap: 0;\n}\n\n.skeletonTableRow {\n display: flex;\n align-items: center;\n gap: var(--spacing-4, 1rem);\n padding: var(--spacing-3, 0.75rem) 0;\n border-bottom: 1px solid var(--border-default, #e5e7eb);\n}\n\n.skeletonTableRow:last-child {\n border-bottom: none;\n}\n\n.skeletonTableCell {\n flex: 1;\n}\n\n/* ── SkeletonDetail layout ─── */\n.skeletonDetail {\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n}\n\n.skeletonDetailContent {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n flex: 1;\n}\n"
530
+ "content": "/* Skeleton */\n@keyframes shimmer {\n from {\n background-position: -200% 0;\n }\n to {\n background-position: 200% 0;\n }\n}\n\n.skeleton {\n --_shimmer-duration: var(--skeleton-duration, 1.5s);\n border-radius: var(--skeleton-radius, var(--radius-md, 0.375rem));\n /* Default (light) gradient — theme-tunable, falls back to the muted\n surface ramp (Tailwind Gray) */\n background: linear-gradient(\n 90deg,\n var(--skeleton-from, #f3f4f6),\n var(--skeleton-to, #e5e7eb),\n var(--skeleton-from, #f3f4f6)\n );\n background-size: 200% 100%;\n animation: shimmer var(--_shimmer-duration) var(--motion-easing-linear, linear) infinite;\n}\n\n/* Density axis — editorial: dim, text-tinted gradient for dark admin surfaces */\n:global([data-density=\"editorial\"]) .skeleton {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--text-primary) 6%, transparent),\n color-mix(in srgb, var(--text-primary) 10%, transparent),\n color-mix(in srgb, var(--text-primary) 6%, transparent)\n );\n}\n\n/* ── Shape variants (VI-516, opt-in) ─────────────────────────────────────\n Additive modifier classes layered on top of `.skeleton` via `className`.\n They set only a default geometry (width / height / radius) so callers can\n still override via `style`/`className`. The fill + animation are inherited\n from `.skeleton`, so a shape never changes the base look — it only changes\n the silhouette. Each dimension is a token-backed custom property so a theme\n can retune the editorial admin's placeholder geometry without forking the\n component. Defaults match the approved org-management edge capture. */\n\n/* Logo plate — ~104×24, radius-sm. The org-name cell's brand-mark placeholder. */\n.shapeLogo {\n width: var(--skeleton-logo-width, 104px);\n height: var(--skeleton-logo-height, 24px);\n border-radius: var(--radius-sm, 0.25rem);\n}\n\n/* Badge pill — ~64×18, radius-full. The plan/status badge placeholder. */\n.shapePill {\n width: var(--skeleton-pill-width, 64px);\n height: var(--skeleton-pill-height, 18px);\n border-radius: var(--radius-full, 999px);\n}\n\n/* Circle — square footprint, fully rounded. Avatar / icon placeholder. */\n.shapeCircle {\n border-radius: var(--radius-full, 999px);\n}\n\n/* ── Content-shape-aware primitives (VI-584) ──────────────────────────────\n Height / radius / width helpers used internally by SkeletonList,\n SkeletonTable, and SkeletonDetail. All dimensions trace to tokens or are\n documented as intentional pixel values that mirror spec anatomy. */\n\n/* Bar heights */\n.skeletonLineH1 { height: var(--skeleton-line-h1-height, 24px); border-radius: var(--radius-md, 0.375rem); }\n.skeletonLineHeading { height: var(--skeleton-line-heading-height, 18px); border-radius: var(--radius-md, 0.375rem); }\n.skeletonLineBody { height: var(--skeleton-line-body-height, 14px); border-radius: var(--radius-sm, 0.25rem); }\n\n/* Avatar / thumbnail */\n.skeletonAvatar {\n width: var(--skeleton-avatar-size, 40px);\n height: var(--skeleton-avatar-size, 40px);\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Larger avatar for detail views */\n.skeletonAvatarLg {\n width: var(--skeleton-avatar-lg-size, 64px);\n height: var(--skeleton-avatar-lg-size, 64px);\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Badge pill used in list rows */\n.skeletonBadge {\n width: var(--skeleton-badge-width, 72px);\n height: var(--skeleton-badge-height, 22px);\n border-radius: var(--radius-full, 9999px);\n flex-shrink: 0;\n}\n\n/* Width utilities — fractional widths for organic line-length variety */\n.skeletonW40 { width: 40%; }\n.skeletonW60 { width: 60%; }\n.skeletonW70 { width: 70%; }\n.skeletonW80 { width: 80%; }\n.skeletonWFull { width: 100%; }\n\n/* ── SkeletonList layout ─── */\n.skeletonList {\n display: flex;\n flex-direction: column;\n}\n\n.skeletonListRow {\n display: flex;\n align-items: center;\n gap: var(--skeleton-row-gap, var(--spacing-3, 0.75rem));\n padding: var(--skeleton-row-padding, var(--spacing-3, 0.75rem) 0);\n border-bottom: var(--skeleton-row-border-bottom, 1px solid var(--border-default, #e5e7eb));\n}\n\n/* Remove bottom border on last row */\n.skeletonListRow:last-child {\n border-bottom: none;\n}\n\n.skeletonListText {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n flex: 1;\n}\n\n/* ── SkeletonTable layout ─── */\n.skeletonTable {\n display: flex;\n flex-direction: column;\n gap: 0;\n}\n\n.skeletonTableRow {\n display: flex;\n align-items: center;\n gap: var(--spacing-4, 1rem);\n padding: var(--skeleton-row-padding, var(--spacing-3, 0.75rem) 0);\n border-bottom: var(--skeleton-row-border-bottom, 1px solid var(--border-default, #e5e7eb));\n}\n\n.skeletonTableRow:last-child {\n border-bottom: none;\n}\n\n.skeletonTableCell {\n flex: 1;\n}\n\n/* ── SkeletonDetail layout ─── */\n.skeletonDetail {\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n}\n\n.skeletonDetailContent {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n flex: 1;\n}\n"
531
531
  }
532
532
  ]
533
533
  },
@@ -552,7 +552,7 @@
552
552
  {
553
553
  "path": "components/ui/spinner/spinner.module.css",
554
554
  "type": "registry:ui",
555
- "content": "/* Spinner\n *\n * Inline loading spinner — a border-based rotating ring with a subtle track\n * and a tone-colored leading edge. Sizes: xs (12px), sm (16px), md (24px).\n *\n * All colors flow through CSS custom properties so themes can override the\n * track and edge colors without forking the component. Stroke widths are\n * sourced from the --stroke-width-* token set (thin/regular/medium/thick).\n */\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n.root {\n --spinner-track-color: var(--border-default, #e5e7eb);\n --spinner-edge-color: var(--text-tertiary, #6b7280);\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: var(--radius-full, 9999px);\n border-style: solid;\n border-color: var(--spinner-track-color);\n border-top-color: var(--spinner-edge-color);\n animation: spin var(--motion-duration-1500, 1500ms) var(--motion-easing-linear, linear) infinite;\n box-sizing: border-box;\n}\n\n/* Visually-hidden label text — accessible to screen readers, not visible */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Size xs — 12px ring, stroke-width-thin (1px) */\n.root.sizeXs {\n width: 12px;\n height: 12px;\n border-width: var(--stroke-width-thin, 1px);\n}\n\n/* Size sm — 16px ring, stroke-width-regular (1.5px) */\n.root.sizeSm {\n width: 16px;\n height: 16px;\n border-width: var(--stroke-width-regular, 1.5px);\n}\n\n/* Size md — 24px ring, stroke-width-medium (2px) */\n.root.sizeMd {\n width: 24px;\n height: 24px;\n border-width: var(--stroke-width-medium, 2px);\n}\n\n/* Tone: default — tertiary text for leading edge */\n.root.toneDefault {\n --spinner-edge-color: var(--text-tertiary, #6b7280);\n}\n\n/* Tone: primary — primary brand color for leading edge */\n.root.tonePrimary {\n --spinner-edge-color: var(--primary, #111827);\n}\n\n/* Respect reduced motion — stop rotation */\n@media (prefers-reduced-motion: reduce) {\n .root {\n animation-play-state: paused;\n }\n}\n"
555
+ "content": "/* Spinner\n *\n * Inline loading spinner — a border-based rotating ring with a subtle track\n * and a tone-colored leading edge. Sizes: xs (12px), sm (16px), md (24px).\n *\n * All colors flow through CSS custom properties so themes can override the\n * track and edge colors without forking the component. Stroke widths are\n * sourced from the --stroke-width-* token set (thin/regular/medium/thick).\n */\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* VI-625: the hooks used to be *declared* on .root, which meant an element-level\n declaration shadowed anything a theme emitted on :root — the \"themes can\n override the track and edge colors\" promise in the header never actually\n held. Reading them with the same expression as the inline fallback is\n value-preserving and makes them genuinely theme-bindable. */\n.root {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: var(--spinner-radius, var(--radius-full, 9999px));\n border-style: solid;\n border-color: var(--spinner-track-color, var(--border-default, #e5e7eb));\n border-top-color: var(--spinner-edge-color, var(--text-tertiary, #6b7280));\n animation: spin var(--spinner-duration, var(--motion-duration-1500, 1500ms)) var(--motion-easing-linear, linear) infinite;\n box-sizing: border-box;\n}\n\n/* Visually-hidden label text — accessible to screen readers, not visible */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Size xs — 12px ring, stroke-width-thin (1px) */\n.root.sizeXs {\n width: var(--spinner-xs-size, 12px);\n height: var(--spinner-xs-size, 12px);\n border-width: var(--spinner-xs-border-width, var(--stroke-width-thin, 1px));\n}\n\n/* Size sm — 16px ring, stroke-width-regular (1.5px) */\n.root.sizeSm {\n width: var(--spinner-sm-size, 16px);\n height: var(--spinner-sm-size, 16px);\n border-width: var(--spinner-sm-border-width, var(--stroke-width-regular, 1.5px));\n}\n\n/* Size md — 24px ring, stroke-width-medium (2px) */\n.root.sizeMd {\n width: var(--spinner-md-size, 24px);\n height: var(--spinner-md-size, 24px);\n border-width: var(--spinner-md-border-width, var(--stroke-width-medium, 2px));\n}\n\n/* Tone: default — tertiary text for leading edge */\n.root.toneDefault {\n border-top-color: var(--spinner-edge-color, var(--text-tertiary, #6b7280));\n}\n\n/* Tone: primary — primary brand color for leading edge */\n.root.tonePrimary {\n border-top-color: var(--spinner-primary-edge-color, var(--primary, #111827));\n}\n\n/* Respect reduced motion — stop rotation */\n@media (prefers-reduced-motion: reduce) {\n .root {\n animation-play-state: paused;\n }\n}\n"
556
556
  }
557
557
  ]
558
558
  },
@@ -753,7 +753,7 @@
753
753
  {
754
754
  "path": "components/ui/dialog/dialog.module.css",
755
755
  "type": "registry:ui",
756
- "content": "/* Dialog overlay\n * Default: 4px backdrop blur. Density axis: editorial uses 2px. */\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 50;\n background-color: var(--overlay-bg, rgba(0, 0, 0, 0.8));\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n\n:global([data-density=\"editorial\"]) .overlay {\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n}\n\n.overlay[data-state=\"open\"] {\n animation: fadeIn var(--motion-duration-150, 150ms) var(--motion-easing-enter, ease-out);\n}\n\n.overlay[data-state=\"closed\"] {\n animation: fadeOut var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Dialog content\n * Default: 32rem max-width, radius-lg, surface-page, spacing-6, shadow-xl.\n * Density axis: editorial uses 480px, radius-xl, surface-card, spacing-8, shadow-lg. */\n.content {\n position: fixed;\n top: 50%;\n left: 50%;\n z-index: 50;\n width: 100%;\n max-width: 32rem;\n transform: translate(-50%, -50%);\n border-radius: var(--radius-lg, 0.5rem);\n background-color: var(--surface-page, #ffffff);\n padding: var(--spacing-6, 1.5rem);\n box-shadow: var(--shadow-xl);\n transition: opacity var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), transform var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n:global([data-density=\"editorial\"]) .content {\n background-color: var(--surface-card);\n max-width: 480px;\n border-radius: var(--radius-xl);\n padding: var(--spacing-8);\n box-shadow: var(--shadow-lg);\n}\n\n.content[data-state=\"open\"] {\n animation: contentShow var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n.content[data-state=\"closed\"] {\n animation: contentHide var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Close button */\n.closeButton {\n position: absolute;\n top: 1rem;\n right: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n opacity: 0.7;\n transition: opacity var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n padding: var(--spacing-1, 0.25rem);\n color: var(--text-primary, #111827);\n}\n\n.closeButton:hover {\n opacity: 1;\n}\n\n.closeButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.closeIcon {\n width: 1rem;\n height: 1rem;\n}\n\n/* Header */\n.header {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Title\n * Default: font-size-lg / semibold. Density axis: editorial uses font-size-2xl / 700. */\n.title {\n font-size: var(--font-size-lg, 1.125rem);\n font-weight: var(--font-weight-semibold, 600);\n color: var(--text-primary, #111827);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .title {\n font-size: var(--font-size-2xl);\n font-weight: var(--font-weight-bold, 700);\n}\n\n/* Description\n * Default: text-secondary. Density axis: editorial uses text-tertiary. */\n.description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .description {\n color: var(--text-tertiary);\n}\n\n/* Footer */\n.footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n padding-top: var(--spacing-3, 0.75rem);\n margin-top: var(--spacing-2, 0.5rem);\n border-top: var(--hairline-width, 1px) solid var(--hairline, var(--border-subtle, rgba(0, 0, 0, 0.06)));\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes fadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n@keyframes contentShow {\n from {\n opacity: 0;\n transform: translate(-50%, calc(-50% + 8px));\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@keyframes contentHide {\n from {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n to {\n opacity: 0;\n transform: translate(-50%, calc(-50% + 8px));\n }\n}\n"
756
+ "content": "/* Dialog overlay\n * Default: 4px backdrop blur. Density axis: editorial uses 2px. */\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 50;\n background-color: var(--overlay-bg, rgba(0, 0, 0, 0.8));\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n\n:global([data-density=\"editorial\"]) .overlay {\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n}\n\n.overlay[data-state=\"open\"] {\n animation: fadeIn var(--motion-duration-150, 150ms) var(--motion-easing-enter, ease-out);\n}\n\n.overlay[data-state=\"closed\"] {\n animation: fadeOut var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Dialog content\n * Default: 32rem max-width, radius-lg, surface-page, spacing-6, shadow-xl.\n * Density axis: editorial uses 480px, radius-xl, surface-card, spacing-8, shadow-lg. */\n.content {\n position: fixed;\n top: 50%;\n left: 50%;\n z-index: 50;\n width: 100%;\n max-width: 32rem;\n transform: translate(-50%, -50%);\n border-radius: var(--radius-lg, 0.5rem);\n background-color: var(--surface-page, #ffffff);\n padding: var(--spacing-6, 1.5rem);\n box-shadow: var(--shadow-xl);\n transition: opacity var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), transform var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n:global([data-density=\"editorial\"]) .content {\n /* Floating modal — opaque. --surface-card may be translucent glass per theme\n * (blackout: rgba(255,255,255,0.04)); over the backdrop it reads see-through.\n * --surface-popover is the opaque floating token (== --surface-card on solid\n * themes, so no visual change there). VI-623. */\n background-color: var(--surface-popover, var(--surface-page, #ffffff));\n max-width: 480px;\n border-radius: var(--radius-xl);\n padding: var(--spacing-8);\n box-shadow: var(--shadow-lg);\n}\n\n.content[data-state=\"open\"] {\n animation: contentShow var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n.content[data-state=\"closed\"] {\n animation: contentHide var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Close button */\n.closeButton {\n position: absolute;\n top: 1rem;\n right: 1rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n opacity: 0.7;\n transition: opacity var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n padding: var(--spacing-1, 0.25rem);\n color: var(--text-primary, #111827);\n}\n\n.closeButton:hover {\n opacity: 1;\n}\n\n.closeButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.closeIcon {\n width: 1rem;\n height: 1rem;\n}\n\n/* Header */\n.header {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Title\n * Default: font-size-lg / semibold. Density axis: editorial uses font-size-2xl / 700. */\n.title {\n font-size: var(--font-size-lg, 1.125rem);\n font-weight: var(--font-weight-semibold, 600);\n color: var(--text-primary, #111827);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .title {\n font-size: var(--font-size-2xl);\n font-weight: var(--font-weight-bold, 700);\n}\n\n/* Description\n * Default: text-secondary. Density axis: editorial uses text-tertiary. */\n.description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .description {\n color: var(--text-tertiary);\n}\n\n/* Footer */\n.footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n padding-top: var(--spacing-3, 0.75rem);\n margin-top: var(--spacing-2, 0.5rem);\n border-top: var(--hairline-width, 1px) solid var(--hairline, var(--border-subtle, rgba(0, 0, 0, 0.06)));\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes fadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n@keyframes contentShow {\n from {\n opacity: 0;\n transform: translate(-50%, calc(-50% + 8px));\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@keyframes contentHide {\n from {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n to {\n opacity: 0;\n transform: translate(-50%, calc(-50% + 8px));\n }\n}\n"
757
757
  }
758
758
  ]
759
759
  },
@@ -832,7 +832,7 @@
832
832
  {
833
833
  "path": "components/ui/tabs/tabs.module.css",
834
834
  "type": "registry:ui",
835
- "content": "/* Tabs root */\n.root {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Tabs list */\n.list {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-full, 9999px);\n padding: calc(var(--spacing-1, 0.25rem) * 0.75);\n color: var(--text-secondary, #6b7280);\n height: 2.25rem;\n width: fit-content;\n}\n\n.listVariantDefault {\n background-color: var(--surface-muted, #f3f4f6);\n}\n\n.listVariantLine {\n position: relative;\n gap: var(--spacing-2, 0.5rem);\n background-color: transparent;\n border-radius: 0;\n border-bottom: 1px solid var(--hairline, var(--border-default, #e5e7eb));\n height: auto;\n padding-bottom: 0;\n width: 100%;\n justify-content: flex-start;\n}\n\n/* Line variant: content-width triggers with comfortable padding — the strip\n reads as an underlined rail, not a segmented control. */\n.listVariantLine .trigger {\n flex: 0 0 auto;\n padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);\n}\n\n/* Tabs trigger */\n.trigger {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n border-radius: var(--radius-md, 0.375rem);\n border: 1px solid transparent;\n padding: var(--spacing-1, 0.25rem) var(--spacing-2, 0.5rem);\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-medium, 500);\n white-space: nowrap;\n color: var(--text-secondary, #6b7280);\n background: transparent;\n cursor: pointer;\n transition: color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), transform var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n flex: 1;\n height: calc(100% - 2px);\n}\n\n.trigger:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n color: var(--text-primary, #111827);\n }\n}\n\n.trigger:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.trigger:disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.trigger[data-state=\"active\"] {\n background-color: var(--surface-page, #ffffff);\n color: var(--text-primary, #111827);\n box-shadow: var(--shadow-sm);\n}\n\n/* Trigger label — wraps children to allow count pill alongside */\n.label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Count pill */\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: var(--radius-full, 9999px);\n padding: 0 var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n font-variant-numeric: tabular-nums;\n line-height: 1.5;\n vertical-align: baseline;\n}\n\n.countNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-secondary, #6b7280);\n}\n\n.countPrimary {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Active-state re-tone: regardless of countTone, re-tone to primary on active */\n.trigger[data-state=\"active\"] .count {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Tabs content */\n.content {\n flex: 1;\n font-size: var(--font-size-sm, 0.875rem);\n outline: none;\n}\n\n.content:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Line variant: active trigger loses shadow/bg, just text color */\n.listVariantLine .trigger[data-state=\"active\"] {\n background-color: transparent;\n box-shadow: none;\n}\n\n/* Animated sliding indicator for line variant */\n.indicator {\n position: absolute;\n bottom: -1px;\n height: 2px;\n /* Default: text-primary */\n background-color: var(--text-primary, #111827);\n border-radius: var(--radius-full, 9999px);\n transition: left var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out),\n width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Density axis — editorial: brand primary underline for dark admin surfaces */\n:global([data-density=\"editorial\"]) .indicator {\n background-color: var(--primary);\n}\n"
835
+ "content": "/* Tabs root */\n.root {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Tabs list */\n.list {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--tabs-list-radius, var(--radius-full, 9999px));\n padding: var(--tabs-list-padding, calc(var(--spacing-1, 0.25rem) * 0.75));\n color: var(--tabs-list-color, var(--text-secondary, #6b7280));\n height: var(--tabs-list-height, 2.25rem);\n width: fit-content;\n}\n\n.listVariantDefault {\n background-color: var(--tabs-list-bg, var(--surface-muted, #f3f4f6));\n}\n\n.listVariantLine {\n position: relative;\n gap: var(--spacing-2, 0.5rem);\n background-color: transparent;\n border-radius: 0;\n border-bottom: var(--tabs-line-border-bottom, 1px solid var(--hairline, var(--border-default, #e5e7eb)));\n height: auto;\n padding-bottom: 0;\n width: 100%;\n justify-content: flex-start;\n}\n\n/* Line variant: content-width triggers with comfortable padding — the strip\n reads as an underlined rail, not a segmented control. */\n.listVariantLine .trigger {\n flex: 0 0 auto;\n padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);\n}\n\n/* Tabs trigger */\n.trigger {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n border-radius: var(--tabs-trigger-radius, var(--radius-md, 0.375rem));\n border: 1px solid transparent;\n padding: var(--tabs-trigger-padding, var(--spacing-1, 0.25rem) var(--spacing-2, 0.5rem));\n font-size: var(--tabs-trigger-font-size, var(--font-size-sm, 0.875rem));\n font-weight: var(--tabs-trigger-font-weight, var(--font-weight-medium, 500));\n letter-spacing: var(--tabs-trigger-letter-spacing, inherit);\n text-transform: var(--tabs-trigger-text-transform, inherit);\n white-space: nowrap;\n color: var(--tabs-trigger-color, var(--text-secondary, #6b7280));\n background: transparent;\n cursor: pointer;\n transition: color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), transform var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n flex: 1;\n height: calc(100% - 2px);\n}\n\n.trigger:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n color: var(--text-primary, #111827);\n }\n}\n\n.trigger:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.trigger:disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.trigger[data-state=\"active\"] {\n background-color: var(--tabs-trigger-active-bg, var(--surface-page, #ffffff));\n color: var(--tabs-trigger-active-color, var(--text-primary, #111827));\n box-shadow: var(--tabs-trigger-active-shadow, var(--shadow-sm));\n}\n\n/* Trigger label — wraps children to allow count pill alongside */\n.label {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Count pill */\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n border-radius: var(--radius-full, 9999px);\n padding: 0 var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n font-variant-numeric: tabular-nums;\n line-height: 1.5;\n vertical-align: baseline;\n}\n\n.countNeutral {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-secondary, #6b7280);\n}\n\n.countPrimary {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Active-state re-tone: regardless of countTone, re-tone to primary on active */\n.trigger[data-state=\"active\"] .count {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--text-link, #2563eb);\n}\n\n/* Tabs content */\n.content {\n flex: 1;\n font-size: var(--font-size-sm, 0.875rem);\n outline: none;\n}\n\n.content:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Line variant: active trigger loses shadow/bg, just text color */\n.listVariantLine .trigger[data-state=\"active\"] {\n background-color: transparent;\n box-shadow: none;\n}\n\n/* Animated sliding indicator for line variant */\n.indicator {\n position: absolute;\n bottom: -1px;\n height: var(--tabs-indicator-height, 2px);\n /* Default: text-primary */\n background-color: var(--tabs-indicator-color, var(--text-primary, #111827));\n border-radius: var(--radius-full, 9999px);\n transition: left var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out),\n width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Density axis — editorial: brand primary underline for dark admin surfaces */\n:global([data-density=\"editorial\"]) .indicator {\n background-color: var(--primary);\n}\n"
836
836
  }
837
837
  ]
838
838
  },
@@ -908,7 +908,7 @@
908
908
  {
909
909
  "path": "components/ui/sidebar/sidebar.module.css",
910
910
  "type": "registry:ui",
911
- "content": "/* Sidebar wrapper */\n.wrapper {\n display: flex;\n min-height: 100svh;\n width: 100%;\n background-color: var(--sidebar-bg);\n}\n\n/* Sidebar static (collapsible=none) */\n.sidebarStatic {\n display: flex;\n height: 100%;\n width: var(--sidebar-width, 16rem);\n flex-direction: column;\n background-color: var(--sidebar-bg);\n color: var(--sidebar-text);\n}\n\n/* Sidebar container (collapsible versions) */\n.sidebarContainer {\n position: relative;\n color: var(--sidebar-text);\n}\n\n/* Sidebar gap (reserves layout space) */\n.sidebarGap {\n width: var(--sidebar-width, 16rem);\n background: transparent;\n transition: width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n flex-shrink: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarGap {\n width: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"icon\"] .sidebarGap {\n width: var(--sidebar-width-icon, 3rem);\n}\n\n/* Sidebar inner */\n.sidebarInner {\n position: fixed;\n inset-block: 0;\n z-index: 10;\n display: flex;\n height: 100svh;\n width: var(--sidebar-width, 16rem);\n transition: left var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), right var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n.sidebarInner[data-side=\"left\"] {\n left: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarInner[data-side=\"left\"] {\n left: calc(var(--sidebar-width, 16rem) * -1);\n}\n\n.sidebarInner[data-side=\"right\"] {\n right: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarInner[data-side=\"right\"] {\n right: calc(var(--sidebar-width, 16rem) * -1);\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"icon\"] .sidebarInner {\n width: var(--sidebar-width-icon, 3rem);\n}\n\n/* Sidebar content container */\n.sidebarContent {\n display: flex;\n width: 100%;\n height: 100%;\n flex-direction: column;\n background-color: var(--sidebar-bg);\n overflow: hidden;\n}\n\n.sidebarContainer[data-variant=\"floating\"] .sidebarContent,\n.sidebarContainer[data-variant=\"inset\"] .sidebarContent {\n border-radius: var(--radius-lg, 0.5rem);\n box-shadow: var(--shadow-sm);\n}\n\n/* Trigger button */\n.trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n cursor: pointer;\n border-radius: var(--radius-sm, 0.25rem);\n padding: calc(var(--spacing-1, 0.25rem) * 1.5);\n color: var(--text-primary, #111827);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.trigger:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n background-color: var(--surface-muted, #f3f4f6);\n }\n}\n\n.trigger:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Rail */\n.rail {\n position: absolute;\n inset-block: 0;\n z-index: 20;\n width: 1rem;\n border: none;\n background: transparent;\n cursor: col-resize;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n@media (hover: hover) {\n .rail:hover {\n background-color: var(--sidebar-border);\n }\n}\n\n/* Inset main content area */\n.inset {\n position: relative;\n display: flex;\n width: 100%;\n flex: 1;\n flex-direction: column;\n background-color: var(--surface-page, #ffffff);\n}\n\n/* Header */\n.header {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n padding: var(--spacing-2, 0.5rem);\n}\n\n/* Footer */\n.footer {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n padding: var(--spacing-2, 0.5rem);\n}\n\n/* Separator */\n.separator {\n margin-inline: var(--spacing-2, 0.5rem);\n border: none;\n height: 1px;\n background-color: var(--sidebar-border);\n}\n\n/* Content area */\n.contentArea {\n display: flex;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n overflow: auto;\n padding: var(--spacing-1, 0.25rem);\n}\n\n/* Group */\n.group {\n position: relative;\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n padding: var(--spacing-2, 0.5rem);\n}\n\n/* Group label */\n.groupLabel {\n display: flex;\n height: 2rem;\n flex-shrink: 0;\n align-items: center;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-2, 0.5rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--sidebar-text-muted);\n transition: margin var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), opacity var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Group action */\n.groupAction {\n position: absolute;\n top: 0.875rem;\n right: 0.75rem;\n display: flex;\n aspect-ratio: 1;\n width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 0;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.groupAction:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .groupAction:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n/* Group content */\n.groupContent {\n width: 100%;\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Menu */\n.menu {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/* Menu item */\n.menuItem {\n position: relative;\n}\n\n/* Menu button base */\n.menuButton {\n display: flex;\n width: 100%;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n overflow: hidden;\n border-radius: var(--radius-md, 0.375rem);\n border: none;\n text-align: left;\n font-size: var(--font-size-sm, 0.875rem);\n cursor: pointer;\n background: transparent;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n}\n\n.menuButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.menuButton:disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.menuButton[data-active] {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n font-weight: var(--font-weight-medium, 500);\n}\n\n/* Menu button variants */\n.menuButton:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuButtonDefault:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuButtonOutline {\n background-color: var(--surface-page, #ffffff);\n box-shadow: inset 0 0 0 1px var(--sidebar-border);\n}\n\n@media (hover: hover) {\n .menuButtonOutline:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n/* Menu button sizes */\n.menuButtonSizeDefault {\n height: 2.25rem;\n padding: 0 var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n.menuButtonSizeSm {\n height: 2rem;\n padding: 0 calc(var(--spacing-2, 0.5rem) + var(--spacing-1, 0.25rem) / 2);\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n.menuButtonSizeLg {\n height: 3.5rem;\n padding: 0 var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Menu action */\n.menuAction {\n position: absolute;\n top: 0.375rem;\n right: 0.25rem;\n display: flex;\n aspect-ratio: 1;\n width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 0;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.menuAction:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuAction:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuActionHover {\n opacity: 0;\n}\n\n@media (hover: hover) {\n .menuItem:hover .menuActionHover {\n opacity: 1;\n }\n}\n\n.menuItem:focus-within .menuActionHover {\n opacity: 1;\n}\n\n/* Menu badge */\n.menuBadge {\n pointer-events: none;\n position: absolute;\n right: 0.25rem;\n display: flex;\n height: 1.25rem;\n min-width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-1, 0.25rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--sidebar-text);\n font-variant-numeric: tabular-nums;\n user-select: none;\n}\n\n/* Menu sub */\n.menuSub {\n margin-inline: calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2);\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n border-left: 1px solid var(--sidebar-border);\n padding-inline: calc(var(--spacing-2, 0.5rem) + var(--spacing-1, 0.25rem) / 2);\n padding-block: calc(var(--spacing-1, 0.25rem) / 2);\n list-style: none;\n margin: 0;\n margin-inline: calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2);\n}\n\n/* Menu sub item */\n.menuSubItem {\n position: relative;\n}\n\n/* Menu sub button */\n.menuSubButton {\n display: flex;\n height: 1.75rem;\n min-width: 0;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n overflow: hidden;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-2, 0.5rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--sidebar-text);\n text-decoration: none;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n}\n\n.menuSubButton:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuSubButton:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuSubButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.menuSubButton[data-active] {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n}\n\n.menuSubButtonSm {\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n"
911
+ "content": "/* Sidebar wrapper */\n.wrapper {\n display: flex;\n min-height: 100svh;\n width: 100%;\n background-color: var(--sidebar-bg);\n}\n\n/* Sidebar static (collapsible=none) */\n.sidebarStatic {\n display: flex;\n height: 100%;\n width: var(--sidebar-width, 16rem);\n flex-direction: column;\n background-color: var(--sidebar-bg);\n color: var(--sidebar-text);\n}\n\n/* Sidebar container (collapsible versions) */\n.sidebarContainer {\n position: relative;\n color: var(--sidebar-text);\n}\n\n/* Sidebar gap (reserves layout space) */\n.sidebarGap {\n width: var(--sidebar-width, 16rem);\n background: transparent;\n transition: width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n flex-shrink: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarGap {\n width: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"icon\"] .sidebarGap {\n width: var(--sidebar-width-icon, 3rem);\n}\n\n/* Sidebar inner */\n.sidebarInner {\n position: fixed;\n inset-block: 0;\n z-index: 10;\n display: flex;\n height: 100svh;\n width: var(--sidebar-width, 16rem);\n transition: left var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), right var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), width var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n.sidebarInner[data-side=\"left\"] {\n left: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarInner[data-side=\"left\"] {\n left: calc(var(--sidebar-width, 16rem) * -1);\n}\n\n.sidebarInner[data-side=\"right\"] {\n right: 0;\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"offcanvas\"] .sidebarInner[data-side=\"right\"] {\n right: calc(var(--sidebar-width, 16rem) * -1);\n}\n\n.sidebarContainer[data-state=\"collapsed\"][data-collapsible=\"icon\"] .sidebarInner {\n width: var(--sidebar-width-icon, 3rem);\n}\n\n/* Sidebar content container */\n.sidebarContent {\n display: flex;\n width: 100%;\n height: 100%;\n flex-direction: column;\n background-color: var(--sidebar-bg);\n overflow: hidden;\n}\n\n.sidebarContainer[data-variant=\"floating\"] .sidebarContent,\n.sidebarContainer[data-variant=\"inset\"] .sidebarContent {\n border-radius: var(--radius-lg, 0.5rem);\n box-shadow: var(--shadow-sm);\n}\n\n/* Trigger button */\n.trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n cursor: pointer;\n border-radius: var(--radius-sm, 0.25rem);\n padding: calc(var(--spacing-1, 0.25rem) * 1.5);\n color: var(--text-primary, #111827);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.trigger:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n background-color: var(--surface-muted, #f3f4f6);\n }\n}\n\n.trigger:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n/* Rail */\n.rail {\n position: absolute;\n inset-block: 0;\n z-index: 20;\n width: 1rem;\n border: none;\n background: transparent;\n cursor: col-resize;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n@media (hover: hover) {\n .rail:hover {\n background-color: var(--sidebar-border);\n }\n}\n\n/* Inset main content area */\n.inset {\n position: relative;\n display: flex;\n width: 100%;\n flex: 1;\n flex-direction: column;\n background-color: var(--surface-page, #ffffff);\n}\n\n/* Header */\n.header {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n padding: var(--sidebar-header-padding, var(--spacing-2, 0.5rem));\n}\n\n/* Footer */\n.footer {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n padding: var(--sidebar-footer-padding, var(--spacing-2, 0.5rem));\n}\n\n/* Separator */\n.separator {\n margin-inline: var(--spacing-2, 0.5rem);\n border: none;\n height: 1px;\n background-color: var(--sidebar-border);\n}\n\n/* Content area */\n.contentArea {\n display: flex;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n overflow: auto;\n padding: var(--sidebar-content-padding, var(--spacing-1, 0.25rem));\n}\n\n/* Group */\n.group {\n position: relative;\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n padding: var(--spacing-2, 0.5rem);\n}\n\n/* Group label */\n.groupLabel {\n display: flex;\n height: var(--sidebar-group-label-height, 2rem);\n flex-shrink: 0;\n align-items: center;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-2, 0.5rem);\n font-size: var(--sidebar-group-label-font-size, var(--font-size-xs, 0.75rem));\n font-weight: var(--sidebar-group-label-font-weight, var(--font-weight-medium, 500));\n letter-spacing: var(--sidebar-group-label-letter-spacing, inherit);\n text-transform: var(--sidebar-group-label-text-transform, inherit);\n color: var(--sidebar-text-muted);\n transition: margin var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out), opacity var(--motion-duration-normal, 200ms) var(--motion-easing-default, ease-in-out);\n}\n\n/* Group action */\n.groupAction {\n position: absolute;\n top: 0.875rem;\n right: 0.75rem;\n display: flex;\n aspect-ratio: 1;\n width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 0;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.groupAction:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .groupAction:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n/* Group content */\n.groupContent {\n width: 100%;\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Menu */\n.menu {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n gap: var(--sidebar-menu-gap, var(--spacing-1, 0.25rem));\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/* Menu item */\n.menuItem {\n position: relative;\n}\n\n/* Menu button base — the nav-item treatment. VI-625 hooks let a theme pin the\n item shape (radius, height, inset, type) once instead of every admin shell\n re-deriving it. */\n.menuButton {\n display: flex;\n width: 100%;\n align-items: center;\n gap: var(--sidebar-item-gap, var(--spacing-2, 0.5rem));\n overflow: hidden;\n border-radius: var(--sidebar-item-radius, var(--radius-md, 0.375rem));\n border: none;\n text-align: left;\n font-size: var(--sidebar-item-font-size, var(--font-size-sm, 0.875rem));\n font-weight: var(--sidebar-item-font-weight, inherit);\n letter-spacing: var(--sidebar-item-letter-spacing, inherit);\n text-transform: var(--sidebar-item-text-transform, inherit);\n cursor: pointer;\n background: transparent;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n}\n\n.menuButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.menuButton:disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.menuButton[data-active] {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n font-weight: var(--sidebar-item-active-font-weight, var(--font-weight-medium, 500));\n}\n\n/* Menu button variants */\n.menuButton:active:not(:disabled) {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuButtonDefault:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuButtonOutline {\n background-color: var(--surface-page, #ffffff);\n box-shadow: inset 0 0 0 1px var(--sidebar-border);\n}\n\n@media (hover: hover) {\n .menuButtonOutline:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n/* Menu button sizes */\n.menuButtonSizeDefault {\n height: var(--sidebar-item-height, 2.25rem);\n padding: var(--sidebar-item-padding, 0 var(--spacing-3, 0.75rem));\n font-size: var(--sidebar-item-font-size, var(--font-size-sm, 0.875rem));\n}\n\n.menuButtonSizeSm {\n height: 2rem;\n padding: 0 calc(var(--spacing-2, 0.5rem) + var(--spacing-1, 0.25rem) / 2);\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n.menuButtonSizeLg {\n height: 3.5rem;\n padding: 0 var(--spacing-3, 0.75rem);\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n/* Menu action */\n.menuAction {\n position: absolute;\n top: 0.375rem;\n right: 0.25rem;\n display: flex;\n aspect-ratio: 1;\n width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n border: none;\n background: transparent;\n cursor: pointer;\n padding: 0;\n color: var(--sidebar-text);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.menuAction:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuAction:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuActionHover {\n opacity: 0;\n}\n\n@media (hover: hover) {\n .menuItem:hover .menuActionHover {\n opacity: 1;\n }\n}\n\n.menuItem:focus-within .menuActionHover {\n opacity: 1;\n}\n\n/* Menu badge */\n.menuBadge {\n pointer-events: none;\n position: absolute;\n right: 0.25rem;\n display: flex;\n height: 1.25rem;\n min-width: 1.25rem;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-1, 0.25rem);\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--sidebar-text);\n font-variant-numeric: tabular-nums;\n user-select: none;\n}\n\n/* Menu sub */\n.menuSub {\n margin-inline: calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2);\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n border-left: 1px solid var(--sidebar-border);\n padding-inline: calc(var(--spacing-2, 0.5rem) + var(--spacing-1, 0.25rem) / 2);\n padding-block: calc(var(--spacing-1, 0.25rem) / 2);\n list-style: none;\n margin: 0;\n margin-inline: calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2);\n}\n\n/* Menu sub item */\n.menuSubItem {\n position: relative;\n}\n\n/* Menu sub button */\n.menuSubButton {\n display: flex;\n height: 1.75rem;\n min-width: 0;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n overflow: hidden;\n border-radius: var(--radius-sm, 0.25rem);\n padding-inline: var(--spacing-2, 0.5rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--sidebar-text);\n text-decoration: none;\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out), color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n outline: none;\n}\n\n.menuSubButton:active {\n transform: translateY(1px);\n transition: none;\n}\n\n@media (hover: hover) {\n .menuSubButton:hover {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n }\n}\n\n.menuSubButton:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.menuSubButton[data-active] {\n background-color: var(--sidebar-accent-bg);\n color: var(--sidebar-accent-text);\n}\n\n.menuSubButtonSm {\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n"
912
912
  }
913
913
  ]
914
914
  },
@@ -1045,7 +1045,7 @@
1045
1045
  {
1046
1046
  "path": "components/ui/banner/banner.module.css",
1047
1047
  "type": "registry:ui",
1048
- "content": "/* Banner base */\n.base {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);\n font-size: var(--font-size-sm, 0.875rem);\n border-bottom: 1px solid;\n box-shadow: var(--shadow-sm);\n}\n\n/* Intent variants */\n.intentInfo {\n background-color: var(--surface-info-subtle, transparent);\n color: var(--text-info, currentColor);\n border-color: var(--border-info, currentColor);\n}\n\n.intentWarning {\n background-color: var(--surface-warning-subtle, transparent);\n color: var(--text-warning, currentColor);\n border-color: var(--border-warning, currentColor);\n}\n\n.intentError {\n background-color: var(--surface-error-subtle, transparent);\n color: var(--text-error, currentColor);\n border-color: var(--border-error, currentColor);\n}\n\n.intentSuccess {\n background-color: var(--surface-success-subtle, transparent);\n color: var(--text-success, currentColor);\n border-color: var(--border-success, currentColor);\n}\n\n/* Position variants */\n.positionInline {\n /* Default — no extra styles */\n}\n\n.positionSticky {\n position: sticky;\n top: 0;\n z-index: 40;\n}\n\n/* Banner content wrapper */\n.content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) / 2);\n}\n\n/* BannerTitle */\n.title {\n font-weight: var(--font-weight-semibold, 600);\n line-height: 1.5;\n color: inherit;\n}\n\n/* BannerDescription */\n.description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, currentColor);\n line-height: 1.5;\n}\n\n.intentInfo .description {\n color: color-mix(in srgb, var(--text-info, currentColor) 90%, transparent);\n}\n\n.intentWarning .description {\n color: color-mix(in srgb, var(--text-warning, currentColor) 90%, transparent);\n}\n\n.intentError .description {\n color: color-mix(in srgb, var(--text-error, currentColor) 90%, transparent);\n}\n\n.intentSuccess .description {\n color: color-mix(in srgb, var(--text-success, currentColor) 90%, transparent);\n}\n\n/* BannerAction */\n.action {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n}\n"
1048
+ "content": "/* Banner base.\n\n VI-625: the AN-366 retro found six separate banner systems across ~180\n instances in one admin app. Binding the `banner` family collapses them to\n one — `--banner-radius` defaults to `0` (the initial value) so the\n full-bleed bar is unchanged until a theme asks for a rounded card. */\n.base {\n width: 100%;\n display: flex;\n align-items: center;\n gap: var(--banner-gap, var(--spacing-3, 0.75rem));\n padding: var(--banner-padding, var(--spacing-3, 0.75rem) var(--spacing-4, 1rem));\n font-size: var(--banner-font-size, var(--font-size-sm, 0.875rem));\n border-bottom: var(--banner-border-width, 1px) solid;\n border-radius: var(--banner-radius, 0);\n box-shadow: var(--banner-shadow, var(--shadow-sm));\n}\n\n/* Intent variants */\n.intentInfo {\n background-color: var(--banner-info-bg, var(--surface-info-subtle, transparent));\n color: var(--banner-info-text-color, var(--text-info, currentColor));\n border-color: var(--banner-info-border-color, var(--border-info, currentColor));\n}\n\n.intentWarning {\n background-color: var(--banner-warning-bg, var(--surface-warning-subtle, transparent));\n color: var(--banner-warning-text-color, var(--text-warning, currentColor));\n border-color: var(--banner-warning-border-color, var(--border-warning, currentColor));\n}\n\n.intentError {\n background-color: var(--banner-error-bg, var(--surface-error-subtle, transparent));\n color: var(--banner-error-text-color, var(--text-error, currentColor));\n border-color: var(--banner-error-border-color, var(--border-error, currentColor));\n}\n\n.intentSuccess {\n background-color: var(--banner-success-bg, var(--surface-success-subtle, transparent));\n color: var(--banner-success-text-color, var(--text-success, currentColor));\n border-color: var(--banner-success-border-color, var(--border-success, currentColor));\n}\n\n/* Position variants */\n.positionInline {\n /* Default — no extra styles */\n}\n\n.positionSticky {\n position: sticky;\n top: 0;\n z-index: 40;\n}\n\n/* Banner content wrapper */\n.content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) / 2);\n}\n\n/* BannerTitle */\n.title {\n font-weight: var(--banner-title-font-weight, var(--font-weight-semibold, 600));\n line-height: 1.5;\n color: inherit;\n}\n\n/* BannerDescription */\n.description {\n font-size: var(--banner-description-font-size, var(--font-size-sm, 0.875rem));\n color: var(--text-secondary, currentColor);\n line-height: 1.5;\n}\n\n.intentInfo .description {\n color: color-mix(in srgb, var(--text-info, currentColor) 90%, transparent);\n}\n\n.intentWarning .description {\n color: color-mix(in srgb, var(--text-warning, currentColor) 90%, transparent);\n}\n\n.intentError .description {\n color: color-mix(in srgb, var(--text-error, currentColor) 90%, transparent);\n}\n\n.intentSuccess .description {\n color: color-mix(in srgb, var(--text-success, currentColor) 90%, transparent);\n}\n\n/* BannerAction */\n.action {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n}\n"
1049
1049
  }
1050
1050
  ]
1051
1051
  },
@@ -1120,7 +1120,7 @@
1120
1120
  {
1121
1121
  "path": "components/ui/table/table.module.css",
1122
1122
  "type": "registry:ui",
1123
- "content": "/* Table container — radius/shadow are theme-driven so a consumer that wraps the\n table in its own rounded shell (for example with a bulk bar on top) can flatten them.\n Under the editorial density axis these default to 0/none (flush admin shell). */\n.container {\n position: relative;\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n border-radius: var(--dt-container-radius, var(--radius-lg, 0.5rem));\n box-shadow: var(--dt-container-shadow, var(--shadow-sm));\n}\n\n/* Editorial density — bakes the editorial-admin surface values into the density\n axis. Ancestor [data-density=\"editorial\"] (set by DataTable's density prop or\n an app-root attribute) triggers these rules. The var() reads keep --dt-* hooks\n working for per-theme overrides; the fallbacks resolve to the editorial values\n when no hook is set.\n Turbopack pure-selector rule: never bare :global — always paired with a local class. */\n:global([data-density=\"editorial\"]) .container {\n border-radius: var(--dt-container-radius, 0);\n box-shadow: var(--dt-container-shadow, none);\n}\n\n:global([data-density=\"editorial\"]) .head {\n background-color: var(--dt-header-bg, var(--surface-card));\n}\n\n:global([data-density=\"editorial\"]) .cell {\n background-color: var(--dt-row-bg, color-mix(in srgb, var(--surface-page) 75%, var(--surface-card) 25%));\n}\n\n/* Table */\n.table {\n width: 100%;\n caption-side: bottom;\n border-collapse: collapse;\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-primary, #111827);\n}\n\n/* TableHeader */\n.header tr {\n border-bottom: 1px solid var(--border-default, #e5e7eb);\n}\n\n/* TableBody */\n.body tr:last-child {\n border-bottom: none;\n}\n\n/* TableFooter */\n.footer {\n border-top: 1px solid var(--border-default, #e5e7eb);\n background-color: var(--surface-muted, #f3f4f6);\n font-weight: var(--font-weight-medium, 500);\n}\n\n.footer tr:last-child {\n border-bottom: none;\n}\n\n/* TableRow */\n.row {\n border-bottom: 1px solid var(--border-default, #e5e7eb);\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.row:not([data-slot=\"data-table-group-row\"]):hover {\n background-color: var(--surface-muted, #f3f4f6);\n}\n\n.row[data-state=\"selected\"] {\n background-color: var(--surface-muted, #f3f4f6);\n}\n\n/* TableHead — bg via --dt-header-bg (theme-driven; transparent default). */\n.head {\n height: 3rem;\n padding-left: var(--spacing-3, 0.75rem);\n padding-right: var(--spacing-3, 0.75rem);\n text-align: left;\n vertical-align: middle;\n font-weight: var(--font-weight-medium, 500);\n white-space: nowrap;\n color: var(--text-primary, #111827);\n background-color: var(--dt-header-bg, transparent);\n}\n\n/* TableCell — bg via --dt-row-bg (theme-driven; transparent default). Painting\n on the cell gives a uniform row surface regardless of tr selected/hover state. */\n.cell {\n padding: var(--spacing-3, 0.75rem);\n vertical-align: middle;\n color: var(--text-primary, #111827);\n background-color: var(--dt-row-bg, transparent);\n border-top: 1px solid var(--hairline, transparent);\n}\n\n/* TableCaption */\n.caption {\n margin-top: var(--spacing-4, 1rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n}\n"
1123
+ "content": "/* Table container — radius/shadow are theme-driven so a consumer that wraps the\n table in its own rounded shell (for example with a bulk bar on top) can flatten them.\n Under the editorial density axis these default to 0/none (flush admin shell).\n\n VI-625: every declaration below reads a component-scoped `--table-*` hook with\n its previous expression as the fallback, so a theme that binds nothing renders\n identically and a theme that binds the `table` family retunes every admin list\n page at once. Contract: the component-tokens module in packages/theme-engine/src */\n.container {\n position: relative;\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n border-radius: var(--dt-container-radius, var(--radius-lg, 0.5rem));\n box-shadow: var(--dt-container-shadow, var(--shadow-sm));\n}\n\n/* Editorial density — bakes the editorial-admin surface values into the density\n axis. Ancestor [data-density=\"editorial\"] (set by DataTable's density prop or\n an app-root attribute) triggers these rules. The var() reads keep --dt-* hooks\n working for per-theme overrides; the fallbacks resolve to the editorial values\n when no hook is set.\n Turbopack pure-selector rule: never bare :global — always paired with a local class. */\n:global([data-density=\"editorial\"]) .container {\n border-radius: var(--dt-container-radius, 0);\n box-shadow: var(--dt-container-shadow, none);\n}\n\n:global([data-density=\"editorial\"]) .head {\n background-color: var(--table-head-bg, var(--dt-header-bg, var(--surface-card)));\n}\n\n:global([data-density=\"editorial\"]) .cell {\n background-color: var(--table-cell-bg, var(--dt-row-bg, color-mix(in srgb, var(--surface-page) 75%, var(--surface-card) 25%)));\n}\n\n/* Table */\n.table {\n width: 100%;\n caption-side: bottom;\n border-collapse: collapse;\n font-size: var(--table-font-size, var(--font-size-sm, 0.875rem));\n color: var(--table-text-color, var(--text-primary, #111827));\n}\n\n/* TableHeader */\n.header tr {\n border-bottom: 1px solid var(--border-default, #e5e7eb);\n}\n\n/* TableBody */\n.body tr:last-child {\n border-bottom: none;\n}\n\n/* TableFooter */\n.footer {\n border-top: 1px solid var(--border-default, #e5e7eb);\n background-color: var(--table-footer-bg, var(--surface-muted, #f3f4f6));\n font-weight: var(--font-weight-medium, 500);\n}\n\n.footer tr:last-child {\n border-bottom: none;\n}\n\n/* TableRow */\n.row {\n border-bottom: var(--table-row-border, 1px solid var(--border-default, #e5e7eb));\n transition: background-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.row:not([data-slot=\"data-table-group-row\"]):hover {\n background-color: var(--table-row-hover-bg, var(--surface-muted, #f3f4f6));\n}\n\n.row[data-state=\"selected\"] {\n background-color: var(--table-row-selected-bg, var(--surface-muted, #f3f4f6));\n}\n\n/* TableHead — bg via --table-head-bg → --dt-header-bg (theme-driven; transparent\n default). The type hooks (family/size/letter-spacing/text-transform) fall back\n to `inherit`, which is the initial computed value for each of those inherited\n properties — so an unbound theme renders exactly as before they existed. */\n.head {\n height: var(--table-head-height, 3rem);\n padding-left: var(--table-head-padding-x, var(--spacing-3, 0.75rem));\n padding-right: var(--table-head-padding-x, var(--spacing-3, 0.75rem));\n text-align: left;\n vertical-align: middle;\n font-family: var(--table-head-font-family, inherit);\n font-size: var(--table-head-font-size, inherit);\n font-weight: var(--table-head-font-weight, var(--font-weight-medium, 500));\n letter-spacing: var(--table-head-letter-spacing, inherit);\n text-transform: var(--table-head-text-transform, inherit);\n white-space: nowrap;\n color: var(--table-head-color, var(--text-primary, #111827));\n background-color: var(--table-head-bg, var(--dt-header-bg, transparent));\n}\n\n/* TableCell — bg via --table-cell-bg → --dt-row-bg (theme-driven; transparent\n default). Painting on the cell gives a uniform row surface regardless of tr\n selected/hover state. */\n.cell {\n padding: var(--table-cell-padding, var(--spacing-3, 0.75rem));\n vertical-align: middle;\n font-size: var(--table-cell-font-size, inherit);\n color: var(--table-cell-color, var(--text-primary, #111827));\n background-color: var(--table-cell-bg, var(--dt-row-bg, transparent));\n border-top: var(--table-cell-border-top, 1px solid var(--hairline, transparent));\n}\n\n/* TableCaption */\n.caption {\n margin-top: var(--spacing-4, 1rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--table-caption-color, var(--text-secondary, #6b7280));\n}\n"
1124
1124
  }
1125
1125
  ]
1126
1126
  },
@@ -1589,7 +1589,7 @@
1589
1589
  {
1590
1590
  "path": "components/ui/session-timeout/session-timeout.module.css",
1591
1591
  "type": "registry:ui",
1592
- "content": "/* Session Timeout Overlay */\n\n/* Scrim — semi-transparent backdrop with blur, covers the full viewport */\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 50;\n background-color: var(--overlay-bg, rgba(0, 0, 0, var(--opacity-80, 0.8)));\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n}\n\n.overlay[data-state=\"open\"] {\n animation: overlayIn var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n.overlay[data-state=\"closed\"] {\n animation: overlayFadeOut var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Content shell — centers the card in the viewport */\n.content {\n position: fixed;\n inset: 0;\n z-index: 51;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--spacing-6, 1.5rem);\n outline: none;\n /* No background on content shell — scrim is on .overlay */\n}\n\n/* Card surface */\n.card {\n background-color: var(--surface-card, #ffffff);\n border-radius: var(--radius-xl, 1rem);\n padding: var(--spacing-10, 2.5rem) var(--spacing-9, 2.25rem);\n text-align: center;\n max-width: 340px;\n width: 100%;\n box-shadow: var(--shadow-lg);\n border: var(--stroke-width-thin, 1px) solid var(--border-default, #e5e7eb);\n}\n\n.card[data-state=\"open\"],\n.content[data-state=\"open\"] .card {\n animation: cardIn var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n/* Lock icon wrap — circular tinted plate */\n.iconWrap {\n width: 4rem;\n height: 4rem;\n border-radius: var(--radius-full, 9999px);\n background-color: color-mix(in srgb, var(--primary, #2563eb) var(--opacity-10, 10%), transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto var(--spacing-6, 1.5rem);\n}\n\n/* Lock icon — uses primary color */\n.icon {\n color: var(--primary, #2563eb);\n}\n\n/* Headline */\n.headline {\n font-size: var(--font-size-xl, 1.25rem);\n font-weight: var(--font-weight-bold, 700);\n color: var(--text-primary, #111827);\n line-height: 1.25;\n margin-bottom: var(--spacing-2, 0.5rem);\n margin-top: 0;\n}\n\n/* Body copy */\n.copy {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #4b5563);\n line-height: 1.6;\n margin-bottom: var(--spacing-7, 1.75rem);\n margin-top: 0;\n}\n\n/* Primary CTA — Sign in */\n.signinBtn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-2, 0.5rem);\n width: 100%;\n padding: var(--spacing-3, 0.75rem) var(--spacing-7, 1.75rem);\n border-radius: var(--radius-md, 0.375rem);\n border: none;\n background-color: var(--interactive-primary-bg, var(--primary, #2563eb));\n color: var(--interactive-primary-text, #ffffff);\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-semibold, 600);\n cursor: pointer;\n transition:\n background-color var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease),\n opacity var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease);\n}\n\n@media (hover: hover) {\n .signinBtn:hover:not(:disabled) {\n background-color: var(--interactive-primary-bg-hover, var(--primary, #1d4ed8));\n }\n}\n\n.signinBtn:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #3b82f6);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.signinBtn:disabled {\n opacity: var(--opacity-60, 0.6);\n cursor: not-allowed;\n}\n\n/* Spinner — inline ring spinner for the primary CTA's redirecting state */\n.signinSpinner {\n display: inline-block;\n flex-shrink: 0;\n width: 0.875rem;\n height: 0.875rem;\n border-radius: var(--radius-full, 9999px);\n border-style: solid;\n border-width: var(--stroke-width-regular, 1.5px);\n border-color: color-mix(in srgb, var(--text-inverse, #ffffff) var(--opacity-40, 40%), transparent);\n border-top-color: var(--text-inverse, #ffffff);\n animation: spin var(--motion-duration-1500, 1500ms) var(--motion-easing-linear, linear) infinite;\n}\n\n/* Return home ghost link */\n.returnHomeBtn {\n display: block;\n width: 100%;\n margin-top: var(--spacing-3, 0.75rem);\n padding: var(--spacing-1, 0.25rem);\n background: none;\n border: none;\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--text-tertiary, #9ca3af);\n text-align: center;\n cursor: pointer;\n transition: color var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease);\n}\n\n@media (hover: hover) {\n .returnHomeBtn:hover:not(:disabled) {\n color: var(--text-secondary, #4b5563);\n }\n}\n\n.returnHomeBtn:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #3b82f6);\n outline-offset: var(--focus-ring-offset, 2px);\n border-radius: var(--radius-sm, 0.125rem);\n}\n\n.returnHomeBtn:disabled {\n opacity: var(--opacity-40, 0.4);\n cursor: not-allowed;\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Keyframes */\n@keyframes overlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes overlayFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n@keyframes cardIn {\n from {\n opacity: 0;\n transform: translateY(var(--spacing-4, 1rem)) scale(0.97);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n"
1592
+ "content": "/* Session Timeout Overlay */\n\n/* Scrim — semi-transparent backdrop with blur, covers the full viewport */\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 50;\n background-color: var(--overlay-bg, rgba(0, 0, 0, var(--opacity-80, 0.8)));\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n}\n\n.overlay[data-state=\"open\"] {\n animation: overlayIn var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n.overlay[data-state=\"closed\"] {\n animation: overlayFadeOut var(--motion-duration-150, 150ms) var(--motion-easing-exit, ease-in);\n}\n\n/* Content shell — centers the card in the viewport */\n.content {\n position: fixed;\n inset: 0;\n z-index: 51;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--spacing-6, 1.5rem);\n outline: none;\n /* No background on content shell — scrim is on .overlay */\n}\n\n/* Card surface */\n.card {\n /* Floating card over the scrim — opaque. --surface-card may be translucent\n * glass per theme (blackout); use the opaque floating token so the card is\n * never see-through (== --surface-card on solid themes). VI-623. */\n background-color: var(--surface-popover, var(--surface-page, #ffffff));\n border-radius: var(--radius-xl, 1rem);\n padding: var(--spacing-10, 2.5rem) var(--spacing-9, 2.25rem);\n text-align: center;\n max-width: 340px;\n width: 100%;\n box-shadow: var(--shadow-lg);\n border: var(--stroke-width-thin, 1px) solid var(--border-default, #e5e7eb);\n}\n\n.card[data-state=\"open\"],\n.content[data-state=\"open\"] .card {\n animation: cardIn var(--motion-duration-normal, 200ms) var(--motion-easing-enter, ease-out);\n}\n\n/* Lock icon wrap — circular tinted plate */\n.iconWrap {\n width: 4rem;\n height: 4rem;\n border-radius: var(--radius-full, 9999px);\n background-color: color-mix(in srgb, var(--primary, #2563eb) var(--opacity-10, 10%), transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto var(--spacing-6, 1.5rem);\n}\n\n/* Lock icon — uses primary color */\n.icon {\n color: var(--primary, #2563eb);\n}\n\n/* Headline */\n.headline {\n font-size: var(--font-size-xl, 1.25rem);\n font-weight: var(--font-weight-bold, 700);\n color: var(--text-primary, #111827);\n line-height: 1.25;\n margin-bottom: var(--spacing-2, 0.5rem);\n margin-top: 0;\n}\n\n/* Body copy */\n.copy {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #4b5563);\n line-height: 1.6;\n margin-bottom: var(--spacing-7, 1.75rem);\n margin-top: 0;\n}\n\n/* Primary CTA — Sign in */\n.signinBtn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-2, 0.5rem);\n width: 100%;\n padding: var(--spacing-3, 0.75rem) var(--spacing-7, 1.75rem);\n border-radius: var(--radius-md, 0.375rem);\n border: none;\n background-color: var(--interactive-primary-bg, var(--primary, #2563eb));\n color: var(--interactive-primary-text, #ffffff);\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-semibold, 600);\n cursor: pointer;\n transition:\n background-color var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease),\n opacity var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease);\n}\n\n@media (hover: hover) {\n .signinBtn:hover:not(:disabled) {\n background-color: var(--interactive-primary-bg-hover, var(--primary, #1d4ed8));\n }\n}\n\n.signinBtn:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #3b82f6);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.signinBtn:disabled {\n opacity: var(--opacity-60, 0.6);\n cursor: not-allowed;\n}\n\n/* Spinner — inline ring spinner for the primary CTA's redirecting state */\n.signinSpinner {\n display: inline-block;\n flex-shrink: 0;\n width: 0.875rem;\n height: 0.875rem;\n border-radius: var(--radius-full, 9999px);\n border-style: solid;\n border-width: var(--stroke-width-regular, 1.5px);\n border-color: color-mix(in srgb, var(--text-inverse, #ffffff) var(--opacity-40, 40%), transparent);\n border-top-color: var(--text-inverse, #ffffff);\n animation: spin var(--motion-duration-1500, 1500ms) var(--motion-easing-linear, linear) infinite;\n}\n\n/* Return home ghost link */\n.returnHomeBtn {\n display: block;\n width: 100%;\n margin-top: var(--spacing-3, 0.75rem);\n padding: var(--spacing-1, 0.25rem);\n background: none;\n border: none;\n font-size: var(--font-size-sm, 0.875rem);\n font-weight: var(--font-weight-medium, 500);\n color: var(--text-tertiary, #9ca3af);\n text-align: center;\n cursor: pointer;\n transition: color var(--motion-duration-fast, 100ms) var(--motion-easing-default, ease);\n}\n\n@media (hover: hover) {\n .returnHomeBtn:hover:not(:disabled) {\n color: var(--text-secondary, #4b5563);\n }\n}\n\n.returnHomeBtn:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, #3b82f6);\n outline-offset: var(--focus-ring-offset, 2px);\n border-radius: var(--radius-sm, 0.125rem);\n}\n\n.returnHomeBtn:disabled {\n opacity: var(--opacity-40, 0.4);\n cursor: not-allowed;\n}\n\n/* Screen reader only */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* Keyframes */\n@keyframes overlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes overlayFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n}\n\n@keyframes cardIn {\n from {\n opacity: 0;\n transform: translateY(var(--spacing-4, 1rem)) scale(0.97);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n"
1593
1593
  }
1594
1594
  ]
1595
1595
  },
@@ -2506,7 +2506,7 @@
2506
2506
  {
2507
2507
  "path": "components/ui/confirm-dialog/confirm-dialog.module.css",
2508
2508
  "type": "registry:ui",
2509
- "content": "/* ConfirmDialog — destructive-action confirmation compound wrapping Dialog. */\n\n.root {\n width: 100%;\n min-width: 0;\n container-type: inline-size;\n container-name: confirm-dialog;\n}\n\n/* ── Canonical default treatment (iconTreatment unset) ─────────────────────\n * A tinted circular plate (~2.5rem, surface-*-subtle wash) stacked above the\n * title/description. This is the unchanged default rendering. */\n\n/* Header layout — plate stacked above title/description */\n.headerStack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Tinted circular icon plate (~2.5rem) stacked above the dialog title */\n.iconPlate {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-10, 2.5rem);\n height: var(--spacing-10, 2.5rem);\n border-radius: 9999px;\n}\n\n.iconPlate svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.plateInfo {\n background: var(--surface-info-subtle, #f0f9ff);\n color: var(--text-info, #0369a1);\n}\n\n.plateWarning {\n background: var(--surface-warning-subtle, #fffbeb);\n color: var(--text-warning, #b45309);\n}\n\n.plateDestructive {\n background: var(--surface-error-subtle, #fef2f2);\n color: var(--text-error, #b91c1c);\n}\n\n/* ── Blessed editorial treatments (iconTreatment=\"inline\" | \"plated\") ───────\n * Brought in from the blessed admin build's confirm-dialog. Opt-in via the\n * iconTreatment prop; the canonical default (above) is unchanged. */\n\n/* Title row with leading severity icon — blessed inline treatment. */\n.titleRow {\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Plated layout — opt-in via iconTreatment=\"plated\". Stacks a tinted circular\n * icon container ABOVE the title (golden organization-management Feedback\n * screen). */\n.titleRowPlated {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Plated severity icon container — ~40px tinted circle. */\n.iconPlated {\n width: var(--confirm-dialog-plate-size, 40px);\n height: var(--confirm-dialog-plate-size, 40px);\n border-radius: var(--radius-full, 999px);\n display: grid;\n place-items: center;\n flex-shrink: 0;\n}\n\n.iconPlated svg {\n width: var(--confirm-dialog-plate-icon-size, 22px);\n height: var(--confirm-dialog-plate-icon-size, 22px);\n}\n\n/* Plated severity tints — bg uses a tinted wash of the severity token;\n * foreground uses the solid severity token. Matches prototype-overlay\n * the prototype's warning / destructive icon plates exactly. */\n.iconPlatedInfo {\n background: color-mix(in srgb, var(--accent, #0369a1) 18%, transparent);\n color: var(--accent, #0369a1);\n}\n\n.iconPlatedWarning {\n background: color-mix(in srgb, var(--warning, #b45309) 22%, transparent);\n color: var(--warning, #b45309);\n}\n\n.iconPlatedDanger {\n background: color-mix(in srgb, var(--destructive, #b91c1c) 18%, transparent);\n color: var(--destructive, #b91c1c);\n}\n\n/* Inline severity icon — sized via svg to respect Phosphor sizing contract.\n * Also serves as the legacy `.iconInfo/.iconWarning/.iconDanger` color hook for\n * theme overrides targeting the old class names. */\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n line-height: 1;\n}\n\n.icon svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.iconInfo {\n color: var(--text-info, #0369a1);\n}\n\n.iconWarning {\n color: var(--text-warning, #b45309);\n}\n\n.iconDanger {\n color: var(--text-error, #b91c1c);\n}\n\n/* Body slot — description paragraph or richer children */\n.body {\n margin-top: var(--spacing-2, 0.5rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n line-height: var(--line-height-relaxed, 1.6);\n}\n\n.body > :first-child {\n margin-top: 0;\n}\n\n.body > :last-child {\n margin-bottom: 0;\n}\n\n/* Confirm-text gate — labeled text input between body and actions */\n.confirmGate {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n margin-top: var(--spacing-4, 1rem);\n}\n\n.confirmGateLabel {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-primary, #111827);\n font-weight: var(--font-weight-medium, 500);\n}\n\n.confirmGateInput {\n width: 100%;\n padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);\n border-radius: var(--radius-md, 0.5rem);\n border: 1px solid var(--border-default, #e5e7eb);\n background-color: var(--surface-page, #ffffff);\n color: var(--text-primary, #111827);\n font-size: var(--font-size-sm, 0.875rem);\n line-height: var(--line-height-normal, 1.5);\n transition: border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.confirmGateInput:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n border-color: var(--border-focus, currentColor);\n}\n\n.confirmGateInput:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Action row — cancel + confirm, right-aligned on wide, stacked on narrow */\n.actions {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n margin-top: var(--spacing-6, 1.5rem);\n}\n\n.action {\n display: inline-flex;\n}\n\n/* Inline (non-portal) surface — opt-in via mode=\"inline\". Renders the dialog\n * content card in normal flow WITHOUT Radix DialogPortal/DialogOverlay/auto\n * X-close, so consumers can stack multiple dialogs inside one shared scrim.\n * Mirrors the dialog content card visuals (surface/radius/padding/shadow) but\n * uses static positioning instead of fixed centering.\n * Default: 32rem max-width, radius-lg, surface-card, spacing-6, shadow-xl.\n * Density axis: editorial uses 480px, radius-xl, surface-card, spacing-8, shadow-lg. */\n.inlineSurface {\n position: relative;\n width: 100%;\n max-width: 32rem;\n border-radius: var(--radius-lg, 0.5rem);\n background-color: var(--surface-card, var(--surface-page, #ffffff));\n padding: var(--spacing-6, 1.5rem);\n box-shadow: var(--shadow-xl);\n}\n\n/* Editorial density: the inline confirm card carries the editorial modal\n metrics (card surface, 480px, radius-xl, spacing-8, shadow-lg) — verified\n against the blessed user-states render. */\n:global([data-density=\"editorial\"]) .inlineSurface {\n background-color: var(--surface-card);\n max-width: 480px;\n border-radius: var(--radius-xl);\n padding: var(--spacing-8);\n box-shadow: var(--shadow-lg);\n}\n\n/* Inline header — replaces DialogHeader (which depends on the dialog stylesheet). */\n.inlineHeader {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Inline title — replaces Radix DialogTitle (no Dialog context in inline mode).\n * Default: font-size-lg / semibold. Density axis: editorial uses font-size-2xl / 700. */\n.inlineTitle {\n font-size: var(--font-size-lg, 1.125rem);\n font-weight: var(--font-weight-semibold, 600);\n color: var(--text-primary, #111827);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .inlineTitle {\n font-size: var(--font-size-2xl);\n font-weight: 700;\n}\n\n/* Inline description — replaces Radix DialogDescription.\n * Default: text-secondary. Density axis: editorial uses text-tertiary. */\n.inlineDescription {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .inlineDescription {\n color: var(--text-tertiary);\n}\n\n/* Container query: stack actions vertically on narrow dialog widths */\n@container confirm-dialog (max-width: 360px) {\n .actions {\n flex-direction: column-reverse;\n align-items: stretch;\n width: 100%;\n }\n\n .actions .action {\n width: 100%;\n }\n\n .actions .action > * {\n width: 100%;\n justify-content: center;\n }\n}\n"
2509
+ "content": "/* ConfirmDialog — destructive-action confirmation compound wrapping Dialog. */\n\n.root {\n width: 100%;\n min-width: 0;\n container-type: inline-size;\n container-name: confirm-dialog;\n}\n\n/* ── Canonical default treatment (iconTreatment unset) ─────────────────────\n * A tinted circular plate (~2.5rem, surface-*-subtle wash) stacked above the\n * title/description. This is the unchanged default rendering. */\n\n/* Header layout — plate stacked above title/description */\n.headerStack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Tinted circular icon plate (~2.5rem) stacked above the dialog title */\n.iconPlate {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-10, 2.5rem);\n height: var(--spacing-10, 2.5rem);\n border-radius: 9999px;\n}\n\n.iconPlate svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.plateInfo {\n background: var(--surface-info-subtle, #f0f9ff);\n color: var(--text-info, #0369a1);\n}\n\n.plateWarning {\n background: var(--surface-warning-subtle, #fffbeb);\n color: var(--text-warning, #b45309);\n}\n\n.plateDestructive {\n background: var(--surface-error-subtle, #fef2f2);\n color: var(--text-error, #b91c1c);\n}\n\n/* ── Blessed editorial treatments (iconTreatment=\"inline\" | \"plated\") ───────\n * Brought in from the blessed admin build's confirm-dialog. Opt-in via the\n * iconTreatment prop; the canonical default (above) is unchanged. */\n\n/* Title row with leading severity icon — blessed inline treatment. */\n.titleRow {\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Plated layout — opt-in via iconTreatment=\"plated\". Stacks a tinted circular\n * icon container ABOVE the title (golden organization-management Feedback\n * screen). */\n.titleRowPlated {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Plated severity icon container — ~40px tinted circle. */\n.iconPlated {\n width: var(--confirm-dialog-plate-size, 40px);\n height: var(--confirm-dialog-plate-size, 40px);\n border-radius: var(--radius-full, 999px);\n display: grid;\n place-items: center;\n flex-shrink: 0;\n}\n\n.iconPlated svg {\n width: var(--confirm-dialog-plate-icon-size, 22px);\n height: var(--confirm-dialog-plate-icon-size, 22px);\n}\n\n/* Plated severity tints — bg uses a tinted wash of the severity token;\n * foreground uses the solid severity token. Matches prototype-overlay\n * the prototype's warning / destructive icon plates exactly. */\n.iconPlatedInfo {\n background: color-mix(in srgb, var(--accent, #0369a1) 18%, transparent);\n color: var(--accent, #0369a1);\n}\n\n.iconPlatedWarning {\n background: color-mix(in srgb, var(--warning, #b45309) 22%, transparent);\n color: var(--warning, #b45309);\n}\n\n.iconPlatedDanger {\n background: color-mix(in srgb, var(--destructive, #b91c1c) 18%, transparent);\n color: var(--destructive, #b91c1c);\n}\n\n/* Inline severity icon — sized via svg to respect Phosphor sizing contract.\n * Also serves as the legacy `.iconInfo/.iconWarning/.iconDanger` color hook for\n * theme overrides targeting the old class names. */\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n line-height: 1;\n}\n\n.icon svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.iconInfo {\n color: var(--text-info, #0369a1);\n}\n\n.iconWarning {\n color: var(--text-warning, #b45309);\n}\n\n.iconDanger {\n color: var(--text-error, #b91c1c);\n}\n\n/* Body slot — description paragraph or richer children */\n.body {\n margin-top: var(--spacing-2, 0.5rem);\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n line-height: var(--line-height-relaxed, 1.6);\n}\n\n.body > :first-child {\n margin-top: 0;\n}\n\n.body > :last-child {\n margin-bottom: 0;\n}\n\n/* Confirm-text gate — labeled text input between body and actions */\n.confirmGate {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n margin-top: var(--spacing-4, 1rem);\n}\n\n.confirmGateLabel {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-primary, #111827);\n font-weight: var(--font-weight-medium, 500);\n}\n\n.confirmGateInput {\n width: 100%;\n padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);\n border-radius: var(--radius-md, 0.5rem);\n border: 1px solid var(--border-default, #e5e7eb);\n background-color: var(--surface-page, #ffffff);\n color: var(--text-primary, #111827);\n font-size: var(--font-size-sm, 0.875rem);\n line-height: var(--line-height-normal, 1.5);\n transition: border-color var(--motion-duration-150, 150ms) var(--motion-easing-default, ease-in-out);\n}\n\n.confirmGateInput:focus-visible {\n outline: var(--focus-ring-width, 2px) solid var(--border-focus, currentColor);\n outline-offset: var(--focus-ring-offset, 2px);\n border-color: var(--border-focus, currentColor);\n}\n\n.confirmGateInput:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Action row — cancel + confirm, right-aligned on wide, stacked on narrow */\n.actions {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n margin-top: var(--spacing-6, 1.5rem);\n}\n\n.action {\n display: inline-flex;\n}\n\n/* Inline (non-portal) surface — opt-in via mode=\"inline\". Renders the dialog\n * content card in normal flow WITHOUT Radix DialogPortal/DialogOverlay/auto\n * X-close, so consumers can stack multiple dialogs inside one shared scrim.\n * Mirrors the dialog content card visuals (surface/radius/padding/shadow) but\n * uses static positioning instead of fixed centering.\n * Default: 32rem max-width, radius-lg, surface-card, spacing-6, shadow-xl.\n * Density axis: editorial uses 480px, radius-xl, surface-card, spacing-8, shadow-lg. */\n.inlineSurface {\n position: relative;\n width: 100%;\n max-width: 32rem;\n border-radius: var(--radius-lg, 0.5rem);\n /* In-flow static-positioned surface — sits on the page, not over a backdrop,\n * so a glass --surface-card reads as a faint card lift, not see-through. The\n * portaled confirm uses the dialog atom (surface-page). */\n /* opaque-bg-exempt: in-flow static card */\n background-color: var(--surface-card, var(--surface-page, #ffffff));\n padding: var(--spacing-6, 1.5rem);\n box-shadow: var(--shadow-xl);\n}\n\n/* Editorial density: the inline confirm card carries the editorial modal\n metrics (card surface, 480px, radius-xl, spacing-8, shadow-lg) — verified\n against the blessed user-states render. */\n:global([data-density=\"editorial\"]) .inlineSurface {\n /* opaque-bg-exempt: in-flow static-positioned surface (see note above). */\n background-color: var(--surface-card);\n max-width: 480px;\n border-radius: var(--radius-xl);\n padding: var(--spacing-8);\n box-shadow: var(--shadow-lg);\n}\n\n/* Inline header — replaces DialogHeader (which depends on the dialog stylesheet). */\n.inlineHeader {\n display: flex;\n flex-direction: column;\n gap: calc(var(--spacing-1, 0.25rem) * 1.5);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Inline title — replaces Radix DialogTitle (no Dialog context in inline mode).\n * Default: font-size-lg / semibold. Density axis: editorial uses font-size-2xl / 700. */\n.inlineTitle {\n font-size: var(--font-size-lg, 1.125rem);\n font-weight: var(--font-weight-semibold, 600);\n color: var(--text-primary, #111827);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .inlineTitle {\n font-size: var(--font-size-2xl);\n font-weight: 700;\n}\n\n/* Inline description — replaces Radix DialogDescription.\n * Default: text-secondary. Density axis: editorial uses text-tertiary. */\n.inlineDescription {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n margin: 0;\n}\n\n:global([data-density=\"editorial\"]) .inlineDescription {\n color: var(--text-tertiary);\n}\n\n/* Container query: stack actions vertically on narrow dialog widths */\n@container confirm-dialog (max-width: 360px) {\n .actions {\n flex-direction: column-reverse;\n align-items: stretch;\n width: 100%;\n }\n\n .actions .action {\n width: 100%;\n }\n\n .actions .action > * {\n width: 100%;\n justify-content: center;\n }\n}\n"
2510
2510
  }
2511
2511
  ]
2512
2512
  },
@@ -2564,7 +2564,7 @@
2564
2564
  {
2565
2565
  "path": "components/ui/empty-state/empty-state.module.css",
2566
2566
  "type": "registry:ui",
2567
- "content": "/* Empty State base: centered placeholder for empty lists, tables, regions */\n.base {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n width: 100%;\n min-width: 0;\n gap: var(--spacing-2, 0.5rem);\n color: var(--text-secondary, #6b7280);\n border-radius: var(--radius-lg, 0.75rem);\n container-type: inline-size;\n container-name: empty-state;\n}\n\n/* Size variants — padding, gap, icon/heading scale */\n.sizeSm {\n padding: var(--spacing-4, 1rem) var(--spacing-4, 1rem);\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeMd {\n padding: var(--spacing-8, 2rem) var(--spacing-5, 1.25rem);\n gap: var(--spacing-2, 0.5rem);\n}\n\n.sizeLg {\n padding: var(--spacing-12, 3rem) var(--spacing-6, 1.5rem);\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Tone: default — dashed border card on muted surface */\n.toneDefault {\n background-color: var(--surface-muted, #f9fafb);\n border: 1px dashed var(--border-default, #e5e7eb);\n}\n\n/* Tone: subtle — borderless, transparent */\n.toneSubtle {\n background-color: transparent;\n border: 0;\n}\n\n/* Icon slot — leading visual above the heading */\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--text-tertiary, #6b7280);\n line-height: 1;\n margin-bottom: var(--spacing-1, 0.25rem);\n}\n\n/* Icon wrap — circular chip behind the icon; activated by intent variants */\n.iconWrap {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-full, 9999px);\n width: 72px;\n height: 72px;\n margin-bottom: var(--spacing-3, 0.75rem);\n background-color: var(--surface-subtle, #f5f5f6);\n color: var(--text-tertiary, #6b7280);\n}\n\n.iconWrap svg {\n width: 32px;\n height: 32px;\n}\n\n/* Intent: first-use — accent-tinted chip, primary action CTA encouraged */\n.intentFirstUse .iconWrap {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--surface-accent-default, #3b82f6);\n}\n\n/* Intent: zero-results — accent-tinted chip, clear-filter secondary action */\n.intentZeroResults .iconWrap {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--surface-accent-default, #3b82f6);\n}\n\n/* Intent: no-access — neutral chip, lock icon */\n.intentNoAccess .iconWrap {\n background-color: var(--surface-subtle, #f5f5f6);\n color: var(--text-secondary, #6b7280);\n}\n\n.sizeSm .icon {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeSm .icon svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.sizeMd .icon {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n.sizeMd .icon svg {\n width: var(--spacing-8, 2rem);\n height: var(--spacing-8, 2rem);\n}\n\n.sizeLg .icon {\n font-size: var(--font-size-4xl, 2.25rem);\n}\n\n.sizeLg .icon svg {\n width: var(--spacing-12, 3rem);\n height: var(--spacing-12, 3rem);\n}\n\n/* Heading — short direct statement */\n.heading {\n margin: 0;\n font-family: var(--font-family-heading, inherit);\n font-weight: var(--font-weight-semibold, 600);\n line-height: var(--line-height-tight, 1.25);\n color: var(--text-primary, #111827);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n.sizeSm .heading {\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n.sizeMd .heading {\n font-size: var(--font-size-base, 1rem);\n}\n\n.sizeLg .heading {\n font-size: var(--font-size-lg, 1.125rem);\n}\n\n/* Description — supporting guidance */\n.description {\n margin: 0;\n font-size: var(--font-size-sm, 0.875rem);\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--text-secondary, #6b7280);\n max-width: 48ch;\n}\n\n.sizeSm .description {\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Action cluster — wraps primary + secondary actions */\n.actions {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-2, 0.5rem);\n flex-wrap: wrap;\n margin-top: var(--spacing-3, 0.75rem);\n}\n\n.sizeSm .actions {\n margin-top: var(--spacing-2, 0.5rem);\n}\n\n.sizeLg .actions {\n margin-top: var(--spacing-4, 1rem);\n}\n\n.action {\n display: inline-flex;\n}\n\n.secondaryAction {\n display: inline-flex;\n}\n\n/* Container query: stack actions vertically on very narrow regions */\n@container empty-state (max-width: 320px) {\n .actions {\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n }\n\n .action,\n .secondaryAction {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* ─────────────────────────────────────────────────────────────\n * Variant: editorial — filled card with a circular icon chip.\n * Opt-in admin-editorial composition. Self-contained: overrides the\n * `tone` surface and the `size` icon/heading scale. Declared after the\n * size rules so its equal-specificity child overrides win by source order.\n * ───────────────────────────────────────────────────────────── */\n.editorial {\n background-color: var(--surface-card, #ffffff);\n border: 0;\n border-radius: var(--radius-lg, 0.75rem);\n padding: var(--spacing-16, 4rem) var(--spacing-8, 2rem);\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* 64px circular icon chip */\n.editorial .icon {\n width: 64px;\n height: 64px;\n border-radius: var(--radius-full, 999px);\n background-color: var(--surface-subtle, #f5f5f6);\n color: var(--text-tertiary, #6b7280);\n font-size: 28px;\n margin-bottom: var(--spacing-3, 0.75rem);\n}\n\n.editorial .icon svg {\n width: 28px;\n height: 28px;\n}\n\n.editorial .heading {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.editorial .description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-tertiary, #6b7280);\n max-width: 360px;\n}\n\n.editorial .actions {\n margin-top: var(--spacing-3, 0.75rem);\n}\n"
2567
+ "content": "/* Empty State base: centered placeholder for empty lists, tables, regions */\n.base {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n width: 100%;\n min-width: 0;\n gap: var(--empty-state-gap, var(--spacing-2, 0.5rem));\n color: var(--empty-state-text-color, var(--text-secondary, #6b7280));\n border-radius: var(--empty-state-radius, var(--radius-lg, 0.75rem));\n container-type: inline-size;\n container-name: empty-state;\n}\n\n/* Size variants — padding, gap, icon/heading scale */\n.sizeSm {\n padding: var(--spacing-4, 1rem) var(--spacing-4, 1rem);\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeMd {\n padding: var(--empty-state-padding, var(--spacing-8, 2rem) var(--spacing-5, 1.25rem));\n gap: var(--empty-state-gap, var(--spacing-2, 0.5rem));\n}\n\n.sizeLg {\n padding: var(--spacing-12, 3rem) var(--spacing-6, 1.5rem);\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Tone: default — dashed border card on muted surface */\n.toneDefault {\n background-color: var(--empty-state-bg, var(--surface-muted, #f9fafb));\n border: var(--empty-state-border, 1px dashed var(--border-default, #e5e7eb));\n}\n\n/* Tone: subtle — borderless, transparent */\n.toneSubtle {\n background-color: transparent;\n border: 0;\n}\n\n/* Icon slot — leading visual above the heading */\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--text-tertiary, #6b7280);\n line-height: 1;\n margin-bottom: var(--spacing-1, 0.25rem);\n}\n\n/* Icon wrap — circular chip behind the icon; activated by intent variants */\n.iconWrap {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--empty-state-icon-radius, var(--radius-full, 9999px));\n width: var(--empty-state-icon-size, 72px);\n height: var(--empty-state-icon-size, 72px);\n margin-bottom: var(--spacing-3, 0.75rem);\n background-color: var(--empty-state-icon-bg, var(--surface-subtle, #f5f5f6));\n color: var(--empty-state-icon-color, var(--text-tertiary, #6b7280));\n}\n\n.iconWrap svg {\n width: 32px;\n height: 32px;\n}\n\n/* Intent: first-use — accent-tinted chip, primary action CTA encouraged */\n.intentFirstUse .iconWrap {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--surface-accent-default, #3b82f6);\n}\n\n/* Intent: zero-results — accent-tinted chip, clear-filter secondary action */\n.intentZeroResults .iconWrap {\n background-color: var(--surface-accent-subtle, #eff6ff);\n color: var(--surface-accent-default, #3b82f6);\n}\n\n/* Intent: no-access — neutral chip, lock icon */\n.intentNoAccess .iconWrap {\n background-color: var(--surface-subtle, #f5f5f6);\n color: var(--text-secondary, #6b7280);\n}\n\n.sizeSm .icon {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeSm .icon svg {\n width: var(--spacing-5, 1.25rem);\n height: var(--spacing-5, 1.25rem);\n}\n\n.sizeMd .icon {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n.sizeMd .icon svg {\n width: var(--spacing-8, 2rem);\n height: var(--spacing-8, 2rem);\n}\n\n.sizeLg .icon {\n font-size: var(--font-size-4xl, 2.25rem);\n}\n\n.sizeLg .icon svg {\n width: var(--spacing-12, 3rem);\n height: var(--spacing-12, 3rem);\n}\n\n/* Heading — short direct statement */\n.heading {\n margin: 0;\n font-family: var(--empty-state-heading-font-family, var(--font-family-heading, inherit));\n font-weight: var(--empty-state-heading-font-weight, var(--font-weight-semibold, 600));\n line-height: var(--line-height-tight, 1.25);\n color: var(--empty-state-heading-color, var(--text-primary, #111827));\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n.sizeSm .heading {\n font-size: var(--font-size-sm, 0.875rem);\n}\n\n.sizeMd .heading {\n font-size: var(--empty-state-heading-font-size, var(--font-size-base, 1rem));\n}\n\n.sizeLg .heading {\n font-size: var(--font-size-lg, 1.125rem);\n}\n\n/* Description — supporting guidance */\n.description {\n margin: 0;\n font-size: var(--empty-state-description-font-size, var(--font-size-sm, 0.875rem));\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--empty-state-description-color, var(--text-secondary, #6b7280));\n max-width: 48ch;\n}\n\n.sizeSm .description {\n font-size: var(--font-size-xs, 0.75rem);\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Action cluster — wraps primary + secondary actions */\n.actions {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--empty-state-actions-gap, var(--spacing-2, 0.5rem));\n flex-wrap: wrap;\n margin-top: var(--spacing-3, 0.75rem);\n}\n\n.sizeSm .actions {\n margin-top: var(--spacing-2, 0.5rem);\n}\n\n.sizeLg .actions {\n margin-top: var(--spacing-4, 1rem);\n}\n\n.action {\n display: inline-flex;\n}\n\n.secondaryAction {\n display: inline-flex;\n}\n\n/* Container query: stack actions vertically on very narrow regions */\n@container empty-state (max-width: 320px) {\n .actions {\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n }\n\n .action,\n .secondaryAction {\n width: 100%;\n justify-content: center;\n }\n}\n\n/* ─────────────────────────────────────────────────────────────\n * Variant: editorial — filled card with a circular icon chip.\n * Opt-in admin-editorial composition. Self-contained: overrides the\n * `tone` surface and the `size` icon/heading scale. Declared after the\n * size rules so its equal-specificity child overrides win by source order.\n * ───────────────────────────────────────────────────────────── */\n.editorial {\n background-color: var(--surface-card, #ffffff);\n border: 0;\n border-radius: var(--radius-lg, 0.75rem);\n padding: var(--spacing-16, 4rem) var(--spacing-8, 2rem);\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* 64px circular icon chip */\n.editorial .icon {\n width: 64px;\n height: 64px;\n border-radius: var(--radius-full, 999px);\n background-color: var(--surface-subtle, #f5f5f6);\n color: var(--text-tertiary, #6b7280);\n font-size: 28px;\n margin-bottom: var(--spacing-3, 0.75rem);\n}\n\n.editorial .icon svg {\n width: 28px;\n height: 28px;\n}\n\n.editorial .heading {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.editorial .description {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-tertiary, #6b7280);\n max-width: 360px;\n}\n\n.editorial .actions {\n margin-top: var(--spacing-3, 0.75rem);\n}\n"
2568
2568
  }
2569
2569
  ]
2570
2570
  },
@@ -2642,7 +2642,7 @@
2642
2642
  {
2643
2643
  "path": "components/ui/filter-bar/filter-bar.module.css",
2644
2644
  "type": "registry:ui",
2645
- "content": "/* Filter Bar: horizontal layout + state container for filter controls above a data-table. */\n.base {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--spacing-3, 0.75rem);\n padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);\n background-color: var(--surface-card, #ffffff);\n border: 1px solid var(--border-default, #e5e7eb);\n border-radius: var(--radius-lg, 0.75rem);\n color: var(--text-primary, #111827);\n container-type: inline-size;\n container-name: filter-bar;\n}\n\n/* Dense variant — tighter padding for packed admin layouts */\n.dense {\n gap: var(--spacing-2, 0.5rem);\n padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem);\n}\n\n/* Primary row: [search] [filters...] [spacer] [meta] */\n.row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n min-width: 0;\n width: 100%;\n}\n\n.dense .row {\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Search slot — bounded width on wide layouts */\n.searchSlot {\n flex: 0 1 auto;\n min-width: 0;\n width: min(100%, 18rem);\n}\n\n/* Radius harmony — children step down one level from the container's --radius-lg */\n.searchSlot input,\n.filters > button[role=\"combobox\"],\n.filters button[data-slot=\"select-trigger\"] {\n border-radius: var(--radius-md, 0.25rem);\n}\n\n/* Filters cluster — horizontal flow, wraps on tight rows */\n.filters {\n display: flex;\n flex-direction: row;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-2, 0.5rem);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Meta cluster — right-aligned results count + clear-all */\n.meta {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n margin-left: auto;\n flex: 0 0 auto;\n}\n\n.results {\n font-size: var(--font-size-sm, 0.875rem);\n color: var(--text-secondary, #6b7280);\n line-height: var(--line-height-tight, 1.25);\n white-space: nowrap;\n}\n\n.clearButton {\n flex: 0 0 auto;\n}\n\n/* Secondary row: active filter chips\n * Separation from the primary row is achieved via whitespace alone\n * (the .base gap token handles the vertical rhythm) rather than a\n * hairline rule — modern admin pattern that avoids harsh contrast\n * bands on dark themes where subtle borders are hard to render.\n */\n.chips {\n display: flex;\n flex-direction: row;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Individual chip — uses Badge variant=\"secondary\" as the surface */\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-1, 0.25rem);\n padding-right: var(--spacing-1, 0.25rem);\n}\n\n.chipLabel {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n}\n\n/* Chip remove button — square X affordance inside the chip */\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-4, 1rem);\n height: var(--spacing-4, 1rem);\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n border-radius: var(--radius-sm, 0.25rem);\n transition:\n background-color var(--motion-duration-fast, 120ms)\n var(--motion-easing-standard, ease),\n color var(--motion-duration-fast, 120ms)\n var(--motion-easing-standard, ease);\n}\n\n.chipRemove:hover {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-primary, #111827);\n}\n\n.chipRemove:focus-visible {\n outline: var(--focus-ring-width, 2px) solid\n var(--focus-ring-color, #2563eb);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.chipRemove svg {\n width: var(--spacing-3, 0.75rem);\n height: var(--spacing-3, 0.75rem);\n}\n\n/* Container query: stack the bar vertically on narrow regions */\n@container filter-bar (max-width: 640px) {\n .row {\n flex-direction: column;\n align-items: stretch;\n }\n\n .searchSlot {\n width: 100%;\n }\n\n .filters {\n width: 100%;\n }\n\n .meta {\n margin-left: 0;\n justify-content: space-between;\n width: 100%;\n }\n}\n"
2645
+ "content": "/* Filter Bar: horizontal layout + state container for filter controls above a data-table. */\n.base {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--filter-bar-gap, var(--spacing-3, 0.75rem));\n padding: var(--filter-bar-padding, var(--spacing-3, 0.75rem) var(--spacing-4, 1rem));\n background-color: var(--filter-bar-bg, var(--surface-card, #ffffff));\n border: var(--filter-bar-border, 1px solid var(--border-default, #e5e7eb));\n border-radius: var(--filter-bar-radius, var(--radius-lg, 0.75rem));\n color: var(--filter-bar-text-color, var(--text-primary, #111827));\n container-type: inline-size;\n container-name: filter-bar;\n}\n\n/* Dense variant — tighter padding for packed admin layouts */\n.dense {\n gap: var(--filter-bar-dense-gap, var(--spacing-2, 0.5rem));\n padding: var(--filter-bar-dense-padding, var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem));\n}\n\n/* Primary row: [search] [filters...] [spacer] [meta] */\n.row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n min-width: 0;\n width: 100%;\n}\n\n.dense .row {\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Search slot — bounded width on wide layouts */\n.searchSlot {\n flex: 0 1 auto;\n min-width: 0;\n width: min(100%, 18rem);\n}\n\n/* Radius harmony — children step down one level from the container's --radius-lg */\n.searchSlot input,\n.filters > button[role=\"combobox\"],\n.filters button[data-slot=\"select-trigger\"] {\n border-radius: var(--filter-bar-control-radius, var(--radius-md, 0.25rem));\n}\n\n/* Filters cluster — horizontal flow, wraps on tight rows */\n.filters {\n display: flex;\n flex-direction: row;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-2, 0.5rem);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Meta cluster — right-aligned results count + clear-all */\n.meta {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-3, 0.75rem);\n margin-left: auto;\n flex: 0 0 auto;\n}\n\n.results {\n font-size: var(--filter-bar-results-font-size, var(--font-size-sm, 0.875rem));\n color: var(--filter-bar-results-color, var(--text-secondary, #6b7280));\n line-height: var(--line-height-tight, 1.25);\n white-space: nowrap;\n}\n\n.clearButton {\n flex: 0 0 auto;\n}\n\n/* Secondary row: active filter chips\n * Separation from the primary row is achieved via whitespace alone\n * (the .base gap token handles the vertical rhythm) rather than a\n * hairline rule — modern admin pattern that avoids harsh contrast\n * bands on dark themes where subtle borders are hard to render.\n */\n.chips {\n display: flex;\n flex-direction: row;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-2, 0.5rem);\n}\n\n/* Individual chip — uses Badge variant=\"secondary\" as the surface */\n.chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-1, 0.25rem);\n padding-right: var(--spacing-1, 0.25rem);\n}\n\n.chipLabel {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n}\n\n/* Chip remove button — square X affordance inside the chip */\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-4, 1rem);\n height: var(--spacing-4, 1rem);\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n border-radius: var(--radius-sm, 0.25rem);\n transition:\n background-color var(--motion-duration-fast, 120ms)\n var(--motion-easing-standard, ease),\n color var(--motion-duration-fast, 120ms)\n var(--motion-easing-standard, ease);\n}\n\n.chipRemove:hover {\n background-color: var(--surface-muted, #f3f4f6);\n color: var(--text-primary, #111827);\n}\n\n.chipRemove:focus-visible {\n outline: var(--focus-ring-width, 2px) solid\n var(--focus-ring-color, #2563eb);\n outline-offset: var(--focus-ring-offset, 2px);\n}\n\n.chipRemove svg {\n width: var(--spacing-3, 0.75rem);\n height: var(--spacing-3, 0.75rem);\n}\n\n/* Container query: stack the bar vertically on narrow regions */\n@container filter-bar (max-width: 640px) {\n .row {\n flex-direction: column;\n align-items: stretch;\n }\n\n .searchSlot {\n width: 100%;\n }\n\n .filters {\n width: 100%;\n }\n\n .meta {\n margin-left: 0;\n justify-content: space-between;\n width: 100%;\n }\n}\n"
2646
2646
  }
2647
2647
  ]
2648
2648
  },
@@ -2692,7 +2692,7 @@
2692
2692
  {
2693
2693
  "path": "components/ui/page-header/page-header.module.css",
2694
2694
  "type": "registry:ui",
2695
- "content": "/* Page Header base: container query wrapper */\n.base {\n /* Override hooks for title typography. Themes (or consumers via a wrapping\n className) can set these to retune the marquee title without forking the\n component. Props are sugar over the same custom properties.\n `--page-header-title-leading` is intentionally left unset here so the\n title falls back to its per-rule default line-height (tight for the\n standard title, `1` for the marquee scale); the `leading` prop sets it. */\n --page-header-title-size: 3.5rem;\n --page-header-title-family: var(--font-display, var(--font-family-heading, inherit));\n\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--spacing-4, 1rem);\n container-type: inline-size;\n container-name: page-header;\n color: var(--text-primary, #111827);\n}\n\n/* Size variants control vertical rhythm */\n.sizeSm {\n gap: var(--spacing-3, 0.75rem);\n}\n\n.sizeMd {\n gap: var(--spacing-4, 1rem);\n}\n\n.sizeLg {\n gap: var(--spacing-5, 1.25rem);\n}\n\n/* Breadcrumb slot sits above the title row */\n.breadcrumb {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n/* Main row: text block on the left, actions on the right */\n.row {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--spacing-6, 1.5rem);\n min-width: 0;\n}\n\n/* When a leading media/identity slot is present, the row forms an identity\n lockup: the media sits before the text stack a tight gap from it. Scoped to\n `data-has-leading` so the default (text|actions) row is untouched for\n consumers that don't pass `leading`. The expanding text block keeps actions\n pinned right via the inherited `justify-content: space-between`. */\n.row[data-has-leading] {\n align-items: center;\n gap: var(--page-header-leading-gap, var(--spacing-3, 0.75rem));\n}\n\n/* Leading media/identity slot: sits before the text block, vertically\n centered against the title/description stack, never shrinks. (Reverts the\n VI-539 top-align, which assumed a different slot — the blessed admin\n identity lockup centers the media. Superseded per the admin-editorial\n reconcile plan.) */\n.leading {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n/* Text block: eyebrow, title, description stacked */\n.text {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.sizeSm .text {\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeLg .text {\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Eyebrow: small uppercase label above the title */\n.eyebrow {\n font-size: var(--font-size-xs, 0.75rem);\n font-weight: var(--font-weight-semibold, 600);\n letter-spacing: var(--letter-spacing-wide, 0.05em);\n text-transform: uppercase;\n color: var(--text-tertiary, #6b7280);\n line-height: var(--line-height-tight, 1.25);\n}\n\n/* Title */\n.title {\n margin: 0;\n font-family: var(--font-family-heading, inherit);\n font-size: var(--font-size-2xl, 1.5rem);\n font-weight: var(--font-weight-semibold, 600);\n line-height: var(--page-header-title-leading, var(--line-height-tight, 1.2));\n color: var(--text-primary, #111827);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n.sizeSm .title {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeLg .title {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n/* Title typography overrides (orthogonal to size variants).\n `data-title-size=\"marquee\"` pulls the title up to a hero scale via the\n `--page-header-title-size` custom property. Raw-string consumers pass an\n inline `--page-header-title-size` and rely on the same rule. */\n.title[data-title-size=\"marquee\"] {\n font-size: var(--page-header-title-size, 3.5rem);\n line-height: var(--page-header-title-leading, 1);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n/* `data-title-family=\"display\"` switches to the display family with a\n graceful fallback to the heading family when no display font is themed. */\n.title[data-title-family=\"display\"] {\n font-family: var(--page-header-title-family, var(--font-display, var(--font-family-heading, inherit)));\n}\n\n/* Description */\n.description {\n margin: 0;\n font-size: var(--font-size-sm, 0.875rem);\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--text-secondary, #6b7280);\n max-width: 65ch;\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Actions slot: cluster on the right */\n.actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n flex: 0 0 auto;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n\n/* Container query: stack to column below a small container width */\n@container page-header (max-width: 480px) {\n .row {\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n }\n\n .actions {\n width: 100%;\n justify-content: flex-start;\n }\n}\n"
2695
+ "content": "/* Page Header base: container query wrapper.\n\n VI-625: `--page-header-title-size` / `-title-family` used to be *declared*\n here. An element-level declaration outranks anything a theme emits into a\n cascade layer on :root, so the documented \"themes can set these\" was never\n actually true — the component shadowed the theme. Both use sites already\n carry the identical fallback inline, so dropping the declarations is\n value-preserving (`var(--x, 3.5rem)` with no --x resolves to 3.5rem, exactly\n as it did when --x was declared as 3.5rem) and makes the hooks genuinely\n theme-bindable. `--page-header-title-leading` was already unset for the same\n reason: the title falls back to its per-rule default line-height (tight for\n the standard title, `1` for the marquee scale); the `leading` prop sets it. */\n.base {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n gap: var(--page-header-gap, var(--spacing-4, 1rem));\n container-type: inline-size;\n container-name: page-header;\n color: var(--page-header-text-color, var(--text-primary, #111827));\n}\n\n/* Size variants control vertical rhythm */\n.sizeSm {\n gap: var(--spacing-3, 0.75rem);\n}\n\n.sizeMd {\n gap: var(--spacing-4, 1rem);\n}\n\n.sizeLg {\n gap: var(--spacing-5, 1.25rem);\n}\n\n/* Breadcrumb slot sits above the title row */\n.breadcrumb {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n\n/* Main row: text block on the left, actions on the right */\n.row {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--spacing-6, 1.5rem);\n min-width: 0;\n}\n\n/* When a leading media/identity slot is present, the row forms an identity\n lockup: the media sits before the text stack a tight gap from it. Scoped to\n `data-has-leading` so the default (text|actions) row is untouched for\n consumers that don't pass `leading`. The expanding text block keeps actions\n pinned right via the inherited `justify-content: space-between`. */\n.row[data-has-leading] {\n align-items: center;\n gap: var(--page-header-leading-gap, var(--spacing-3, 0.75rem));\n}\n\n/* Leading media/identity slot: sits before the text block, vertically\n centered against the title/description stack, never shrinks. (Reverts the\n VI-539 top-align, which assumed a different slot — the blessed admin\n identity lockup centers the media. Superseded per the admin-editorial\n reconcile plan.) */\n.leading {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n/* Text block: eyebrow, title, description stacked */\n.text {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2, 0.5rem);\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.sizeSm .text {\n gap: var(--spacing-1, 0.25rem);\n}\n\n.sizeLg .text {\n gap: var(--spacing-3, 0.75rem);\n}\n\n/* Eyebrow: small uppercase label above the title */\n.eyebrow {\n font-size: var(--page-header-eyebrow-font-size, var(--font-size-xs, 0.75rem));\n font-weight: var(--page-header-eyebrow-font-weight, var(--font-weight-semibold, 600));\n letter-spacing: var(--page-header-eyebrow-letter-spacing, var(--letter-spacing-wide, 0.05em));\n text-transform: var(--page-header-eyebrow-text-transform, uppercase);\n color: var(--page-header-eyebrow-color, var(--text-tertiary, #6b7280));\n line-height: var(--line-height-tight, 1.25);\n}\n\n/* Title */\n.title {\n margin: 0;\n font-family: var(--font-family-heading, inherit);\n font-size: var(--page-header-title-font-size, var(--font-size-2xl, 1.5rem));\n font-weight: var(--page-header-title-font-weight, var(--font-weight-semibold, 600));\n line-height: var(--page-header-title-leading, var(--line-height-tight, 1.2));\n color: var(--page-header-title-color, var(--text-primary, #111827));\n letter-spacing: var(--page-header-title-letter-spacing, var(--letter-spacing-tight, -0.01em));\n}\n\n.sizeSm .title {\n font-size: var(--font-size-xl, 1.25rem);\n}\n\n.sizeLg .title {\n font-size: var(--font-size-3xl, 1.875rem);\n}\n\n/* Title typography overrides (orthogonal to size variants).\n `data-title-size=\"marquee\"` pulls the title up to a hero scale via the\n `--page-header-title-size` custom property. Raw-string consumers pass an\n inline `--page-header-title-size` and rely on the same rule. */\n.title[data-title-size=\"marquee\"] {\n font-size: var(--page-header-title-size, 3.5rem);\n line-height: var(--page-header-title-leading, 1);\n letter-spacing: var(--letter-spacing-tight, -0.01em);\n}\n\n/* `data-title-family=\"display\"` switches to the display family with a\n graceful fallback to the heading family when no display font is themed.\n VI-625: `--admin-ui-marquee-family` sits in the chain so the admin-ui\n pattern's marquee role drives the hero title when a theme binds it. */\n.title[data-title-family=\"display\"] {\n font-family: var(--page-header-title-family, var(--admin-ui-marquee-family, var(--font-display, var(--font-family-heading, inherit))));\n}\n\n/* Description */\n.description {\n margin: 0;\n font-size: var(--page-header-description-font-size, var(--font-size-sm, 0.875rem));\n line-height: var(--line-height-relaxed, 1.6);\n color: var(--page-header-description-color, var(--text-secondary, #6b7280));\n max-width: 65ch;\n}\n\n.sizeLg .description {\n font-size: var(--font-size-base, 1rem);\n}\n\n/* Actions slot: cluster on the right */\n.actions {\n display: flex;\n align-items: center;\n gap: var(--page-header-actions-gap, var(--spacing-2, 0.5rem));\n flex: 0 0 auto;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n\n/* Container query: stack to column below a small container width */\n@container page-header (max-width: 480px) {\n .row {\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4, 1rem);\n }\n\n .actions {\n width: 100%;\n justify-content: flex-start;\n }\n}\n"
2696
2696
  }
2697
2697
  ]
2698
2698
  },
@@ -2811,7 +2811,7 @@
2811
2811
  {
2812
2812
  "path": "components/ui/doc-nav/doc-nav.tsx",
2813
2813
  "type": "registry:ui",
2814
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n CaretDownIcon,\n CaretRightIcon,\n ArrowSquareOutIcon,\n} from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./doc-nav.module.css\"\n\n/**\n * Order threshold at or above which a doc with no group and no scope is\n * treated as an ad-hoc \"random\" doc and tucked into the Appendix bucket.\n * Mirrors the PL-2185 rule: no scope + no group + order >= 10 -> Appendix.\n */\nconst APPENDIX_ORDER_THRESHOLD = 10\n\nconst APPENDIX_ID = \"appendix\"\nconst APPENDIX_LABEL = \"Appendix\"\nconst SHARED_ID = \"shared\"\nconst SHARED_LABEL = \"Shared\"\n\n/**\n * A single documentation entry — the manifest slice DocFrame passes down.\n * Every field but `order`/`label`/`href` is optional; absence of `scope`,\n * `group`, and `tier` reproduces the flat, single-row behaviour.\n */\nexport interface DocEntry {\n /** Sort order within the resolved group. `0` marks the hub/overview entry. */\n order: number\n /** Visible pill label. */\n label: string\n /** Destination URL (route or static `/docs/*.html`). */\n href: string\n /** Manifest kind (`route`, `local-html`, `external`, …). Used to detect external links. */\n kind?: string\n /** PL-2177 product scope — which product group(s) the doc belongs to. */\n scope?: string[]\n /** PL-2185 nav section-label hint. Defaults from `scope`. */\n group?: string\n /** PL-2170 load depth — referenced here, owned there. */\n tier?: number\n /** Force external treatment (new tab + badge). Defaults from `kind`/`href`. */\n external?: boolean\n}\n\nexport interface DocNavProps\n extends Omit<React.ComponentProps<\"nav\">, \"children\"> {\n /** The manifest slice for the active view (Shared + product-scoped), pre-filtered by DocFrame. */\n docs: DocEntry[]\n /** Which product group is open (accordion). Absent → single-product mode (no accordion). */\n activeProduct?: string\n /**\n * Accordion callback — expand a product group (the parent collapses the\n * sibling by swapping `activeProduct`). Absent → groups render as plain,\n * independently expandable links (static-doc mode).\n */\n onProductToggle?: (id: string) => void\n /** Groups that stay open regardless of the accordion. Default `[\"shared\"]`. */\n pinnedGroups?: string[]\n /** Resolves the active pill and auto-expands its group. */\n currentPath: string\n /** Whether non-active, non-pinned groups start collapsed. Default `true` — the anti-wall rule. */\n defaultCollapsed?: boolean\n}\n\ntype GroupRole = \"pinned\" | \"collapsible\" | \"appendix\"\n\ninterface ResolvedGroup {\n id: string\n label: string\n role: GroupRole\n entries: DocEntry[]\n}\n\n// ─── helpers ─────────────────────────────────────────────────────────────────\n\nfunction slug(value: string): string {\n return value.trim().toLowerCase().replace(/\\s+/g, \"-\")\n}\n\nfunction titleCase(value: string): string {\n if (value.length === 0) return value\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n\n/** Strip query + hash so a static `href` compares cleanly against `currentPath`. */\nfunction normalizePath(value: string): string {\n return value.replace(/[?#].*$/, \"\")\n}\n\nfunction hrefMatchesPath(href: string, currentPath: string): boolean {\n if (!href) return false\n return normalizePath(href) === normalizePath(currentPath)\n}\n\nfunction isExternalEntry(entry: DocEntry): boolean {\n if (typeof entry.external === \"boolean\") return entry.external\n if (entry.kind === \"external\") return true\n return /^https?:\\/\\//.test(entry.href)\n}\n\n/** Resolve which group an entry belongs to, following the PL-2185 rules. */\nfunction groupKeyFor(entry: DocEntry): { id: string; label: string } {\n const hasGroup = typeof entry.group === \"string\" && entry.group.length > 0\n const hasScope = Array.isArray(entry.scope) && entry.scope.length > 0\n\n if (!hasGroup && !hasScope && entry.order >= APPENDIX_ORDER_THRESHOLD) {\n return { id: APPENDIX_ID, label: APPENDIX_LABEL }\n }\n if (hasGroup) {\n return { id: slug(entry.group as string), label: entry.group as string }\n }\n if (hasScope) {\n const first = (entry.scope as string[])[0]\n return { id: slug(first), label: titleCase(first) }\n }\n return { id: SHARED_ID, label: SHARED_LABEL }\n}\n\n/** Bucket the docs into ordered, non-empty groups (pinned → collapsible → appendix). */\nfunction resolveGroups(\n docs: DocEntry[],\n pinnedGroups: string[]\n): ResolvedGroup[] {\n const byId = new Map<string, ResolvedGroup>()\n const appearance: string[] = []\n\n for (const entry of docs) {\n const key = groupKeyFor(entry)\n let group = byId.get(key.id)\n if (!group) {\n const role: GroupRole =\n key.id === APPENDIX_ID\n ? \"appendix\"\n : pinnedGroups.includes(key.id)\n ? \"pinned\"\n : \"collapsible\"\n group = { id: key.id, label: key.label, role, entries: [] }\n byId.set(key.id, group)\n appearance.push(key.id)\n }\n group.entries.push(entry)\n }\n\n for (const group of byId.values()) {\n group.entries.sort((a, b) => a.order - b.order)\n }\n\n const rank = (group: ResolvedGroup): number => {\n if (group.role === \"pinned\") {\n const idx = pinnedGroups.indexOf(group.id)\n return idx === -1 ? 0 : idx\n }\n if (group.role === \"appendix\") return Number.MAX_SAFE_INTEGER\n return 1000 + appearance.indexOf(group.id)\n }\n\n return appearance\n .map((id) => byId.get(id) as ResolvedGroup)\n .sort((a, b) => rank(a) - rank(b))\n}\n\n// ─── DocNav ──────────────────────────────────────────────────────────────────\n\nconst DocNav = React.forwardRef<HTMLElement, DocNavProps>(\n (\n {\n docs,\n activeProduct,\n onProductToggle,\n pinnedGroups = [SHARED_ID],\n currentPath,\n defaultCollapsed = true,\n className,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref\n ) => {\n const groups = React.useMemo(\n () => resolveGroups(docs, pinnedGroups),\n [docs, pinnedGroups]\n )\n\n // The group holding the active doc — always expanded on load.\n const activeGroupId = React.useMemo(() => {\n for (const group of groups) {\n if (group.entries.some((e) => hrefMatchesPath(e.href, currentPath))) {\n return group.id\n }\n }\n return null\n }, [groups, currentPath])\n\n const isControlled = onProductToggle != null\n\n // Uncontrolled expand state (static-doc mode + appendix, which is never a product).\n const [openSet, setOpenSet] = React.useState<Set<string>>(() => {\n const initial = new Set<string>()\n if (activeGroupId) initial.add(activeGroupId)\n if (activeProduct) initial.add(activeProduct)\n for (const group of groups) {\n if (group.role === \"pinned\") continue\n if (group.role === \"appendix\") {\n // A lone ad-hoc doc renders inline; two or more collapse.\n if (group.entries.length <= 1) initial.add(group.id)\n continue\n }\n if (!defaultCollapsed) initial.add(group.id)\n }\n return initial\n })\n\n const isOpen = (group: ResolvedGroup): boolean => {\n if (group.role === \"pinned\") return true\n if (group.role === \"appendix\") return openSet.has(group.id)\n // Collapsible product group.\n if (isControlled) {\n return group.id === activeProduct || group.id === activeGroupId\n }\n return openSet.has(group.id) || group.id === activeGroupId\n }\n\n const toggle = (group: ResolvedGroup): void => {\n if (group.role === \"pinned\") return\n if (group.role === \"collapsible\" && isControlled) {\n onProductToggle(group.id)\n return\n }\n setOpenSet((prev) => {\n const next = new Set(prev)\n if (next.has(group.id)) next.delete(group.id)\n else next.add(group.id)\n return next\n })\n }\n\n return (\n <nav\n ref={ref}\n aria-label={ariaLabel ?? \"Documentation\"}\n data-slot=\"doc-nav\"\n className={cn(styles.root, className)}\n {...props}\n >\n {groups.map((group) => (\n <DocNavGroup\n key={group.id}\n group={group}\n open={isOpen(group)}\n currentPath={currentPath}\n onToggle={() => toggle(group)}\n />\n ))}\n </nav>\n )\n }\n)\nDocNav.displayName = \"DocNav\"\n\n// ─── DocNavGroup (internal) ──────────────────────────────────────────────────\n\ninterface DocNavGroupProps {\n group: ResolvedGroup\n open: boolean\n currentPath: string\n onToggle: () => void\n}\n\nfunction DocNavGroup({ group, open, currentPath, onToggle }: DocNavGroupProps) {\n const panelId = React.useId()\n const isPinned = group.role === \"pinned\"\n const Caret = open ? CaretDownIcon : CaretRightIcon\n\n const head = (\n <>\n <Caret className={styles.caret} weight=\"fill\" aria-hidden=\"true\" />\n <span className={styles.scopeDot} aria-hidden=\"true\" />\n <span className={styles.groupLabel}>{group.label}</span>\n {!open && <span className={styles.count}>{group.entries.length}</span>}\n </>\n )\n\n return (\n <div\n data-slot=\"doc-nav-group\"\n data-group={group.id}\n data-role={group.role}\n data-state={open ? \"open\" : \"closed\"}\n className={cn(\n styles.group,\n isPinned && styles.groupPinned,\n !open && styles.groupCollapsed\n )}\n >\n {isPinned ? (\n <div className={styles.head}>{head}</div>\n ) : (\n <button\n type=\"button\"\n data-slot=\"doc-nav-group-trigger\"\n className={cn(styles.head, styles.headButton)}\n aria-expanded={open}\n aria-controls={panelId}\n onClick={onToggle}\n >\n {head}\n </button>\n )}\n\n {open && (\n <div id={panelId} className={styles.pills}>\n {group.entries.map((entry, index) => (\n <DocNavPill\n key={entry.href}\n entry={entry}\n active={hrefMatchesPath(entry.href, currentPath)}\n // Hub = the group's overview entry: an explicit order-0 doc, or the\n // lead pill of the pinned Shared set (which often starts at order 1).\n isHub={entry.order === 0 || (group.role === \"pinned\" && index === 0)}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// ─── DocNavPill (internal) ───────────────────────────────────────────────────\n\ninterface DocNavPillProps {\n entry: DocEntry\n active: boolean\n /** Marks the group's hub/overview entry — a leading accent dot. */\n isHub: boolean\n}\n\nfunction DocNavPill({ entry, active, isHub }: DocNavPillProps) {\n const external = isExternalEntry(entry)\n\n return (\n <a\n href={entry.href}\n data-slot=\"doc-nav-pill\"\n data-active={active || undefined}\n data-hub={isHub || undefined}\n aria-current={active ? \"page\" : undefined}\n className={cn(styles.pill, active && styles.pillActive, isHub && styles.pillHub)}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n >\n <span className={styles.pillNum}>{entry.order}</span>\n <span className={styles.pillLabel}>{entry.label}</span>\n {external && (\n <>\n <ArrowSquareOutIcon className={styles.pillExternal} aria-hidden=\"true\" />\n <span className={styles.srOnly}>(opens in a new tab)</span>\n </>\n )}\n </a>\n )\n}\n\nexport { DocNav }\n"
2814
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n CaretDownIcon,\n CaretRightIcon,\n ArrowSquareOutIcon,\n} from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport styles from \"./doc-nav.module.css\"\n\n/**\n * Order threshold at or above which a doc with no group and no scope is\n * treated as an ad-hoc \"random\" doc and tucked into the Appendix bucket.\n * Mirrors the PL-2185 rule: no scope + no group + order >= 10 -> Appendix.\n */\nconst APPENDIX_ORDER_THRESHOLD = 10\n\nconst APPENDIX_ID = \"appendix\"\nconst APPENDIX_LABEL = \"Appendix\"\nconst SHARED_ID = \"shared\"\nconst SHARED_LABEL = \"Shared\"\n\n/**\n * A single documentation entry — the manifest slice DocFrame passes down.\n * Every field but `order`/`label`/`href` is optional; absence of `scope`,\n * `group`, and `tier` reproduces the flat, single-row behaviour.\n */\nexport interface DocEntry {\n /** Sort order within the resolved group. `0` marks the hub/overview entry. */\n order: number\n /** Visible pill label. */\n label: string\n /** Destination URL (route or static `/docs/*.html`). */\n href: string\n /** Manifest kind (`route`, `local-html`, `external`, …). Used to detect external links. */\n kind?: string\n /** PL-2177 product scope — which product group(s) the doc belongs to. */\n scope?: string[]\n /** PL-2185 nav section-label hint. Defaults from `scope`. */\n group?: string\n /** PL-2170 load depth — referenced here, owned there. */\n tier?: number\n /** Force external treatment (new tab + badge). Defaults from `kind`/`href`. */\n external?: boolean\n}\n\nexport interface DocNavProps\n extends Omit<React.ComponentProps<\"nav\">, \"children\"> {\n /** The manifest slice for the active view (Shared + product-scoped), pre-filtered by DocFrame. */\n docs: DocEntry[]\n /** Which product group is open (accordion). Absent → single-product mode (no accordion). */\n activeProduct?: string\n /**\n * Accordion callback — expand a product group (the parent collapses the\n * sibling by swapping `activeProduct`). Absent → groups render as plain,\n * independently expandable links (static-doc mode).\n */\n onProductToggle?: (id: string) => void\n /** Groups that stay open regardless of the accordion. Default `[\"shared\"]`. */\n pinnedGroups?: string[]\n /** Resolves the active pill and auto-expands its group. */\n currentPath: string\n /** Whether non-active, non-pinned groups start collapsed. Default `true` — the anti-wall rule. */\n defaultCollapsed?: boolean\n}\n\ntype GroupRole = \"pinned\" | \"collapsible\" | \"appendix\"\n\ninterface ResolvedGroup {\n id: string\n label: string\n role: GroupRole\n entries: DocEntry[]\n}\n\n// ─── helpers ─────────────────────────────────────────────────────────────────\n\nfunction slug(value: string): string {\n return value.trim().toLowerCase().replace(/\\s+/g, \"-\")\n}\n\nfunction titleCase(value: string): string {\n if (value.length === 0) return value\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n\n/** Strip query + hash so a static `href` compares cleanly against `currentPath`. */\nfunction normalizePath(value: string): string {\n return value.replace(/[?#].*$/, \"\")\n}\n\nfunction hrefMatchesPath(href: string, currentPath: string): boolean {\n if (!href) return false\n return normalizePath(href) === normalizePath(currentPath)\n}\n\nfunction isExternalEntry(entry: DocEntry): boolean {\n if (typeof entry.external === \"boolean\") return entry.external\n if (entry.kind === \"external\") return true\n return /^https?:\\/\\//.test(entry.href)\n}\n\n/** Resolve which group an entry belongs to, following the PL-2185 rules. */\nfunction groupKeyFor(entry: DocEntry): { id: string; label: string } {\n const hasGroup = typeof entry.group === \"string\" && entry.group.length > 0\n const hasScope = Array.isArray(entry.scope) && entry.scope.length > 0\n\n if (!hasGroup && !hasScope && entry.order >= APPENDIX_ORDER_THRESHOLD) {\n return { id: APPENDIX_ID, label: APPENDIX_LABEL }\n }\n if (hasGroup) {\n return { id: slug(entry.group as string), label: entry.group as string }\n }\n if (hasScope) {\n // Bind-then-guard: index reads are only `string` under a checked-index\n // compiler. `hasScope` already proves a non-empty array, so the guard never\n // fires in practice — a scope that somehow yields nothing falls through to\n // the same Shared bucket an absent scope resolves to.\n const first = (entry.scope as string[])[0]\n if (first !== undefined) {\n return { id: slug(first), label: titleCase(first) }\n }\n }\n return { id: SHARED_ID, label: SHARED_LABEL }\n}\n\n/** Bucket the docs into ordered, non-empty groups (pinned → collapsible → appendix). */\nfunction resolveGroups(\n docs: DocEntry[],\n pinnedGroups: string[]\n): ResolvedGroup[] {\n const byId = new Map<string, ResolvedGroup>()\n const appearance: string[] = []\n\n for (const entry of docs) {\n const key = groupKeyFor(entry)\n let group = byId.get(key.id)\n if (!group) {\n const role: GroupRole =\n key.id === APPENDIX_ID\n ? \"appendix\"\n : pinnedGroups.includes(key.id)\n ? \"pinned\"\n : \"collapsible\"\n group = { id: key.id, label: key.label, role, entries: [] }\n byId.set(key.id, group)\n appearance.push(key.id)\n }\n group.entries.push(entry)\n }\n\n for (const group of byId.values()) {\n group.entries.sort((a, b) => a.order - b.order)\n }\n\n const rank = (group: ResolvedGroup): number => {\n if (group.role === \"pinned\") {\n const idx = pinnedGroups.indexOf(group.id)\n return idx === -1 ? 0 : idx\n }\n if (group.role === \"appendix\") return Number.MAX_SAFE_INTEGER\n return 1000 + appearance.indexOf(group.id)\n }\n\n return appearance\n .map((id) => byId.get(id) as ResolvedGroup)\n .sort((a, b) => rank(a) - rank(b))\n}\n\n// ─── DocNav ──────────────────────────────────────────────────────────────────\n\nconst DocNav = React.forwardRef<HTMLElement, DocNavProps>(\n (\n {\n docs,\n activeProduct,\n onProductToggle,\n pinnedGroups = [SHARED_ID],\n currentPath,\n defaultCollapsed = true,\n className,\n \"aria-label\": ariaLabel,\n ...props\n },\n ref\n ) => {\n const groups = React.useMemo(\n () => resolveGroups(docs, pinnedGroups),\n [docs, pinnedGroups]\n )\n\n // The group holding the active doc — always expanded on load.\n const activeGroupId = React.useMemo(() => {\n for (const group of groups) {\n if (group.entries.some((e) => hrefMatchesPath(e.href, currentPath))) {\n return group.id\n }\n }\n return null\n }, [groups, currentPath])\n\n const isControlled = onProductToggle != null\n\n // Uncontrolled expand state (static-doc mode + appendix, which is never a product).\n const [openSet, setOpenSet] = React.useState<Set<string>>(() => {\n const initial = new Set<string>()\n if (activeGroupId) initial.add(activeGroupId)\n if (activeProduct) initial.add(activeProduct)\n for (const group of groups) {\n if (group.role === \"pinned\") continue\n if (group.role === \"appendix\") {\n // A lone ad-hoc doc renders inline; two or more collapse.\n if (group.entries.length <= 1) initial.add(group.id)\n continue\n }\n if (!defaultCollapsed) initial.add(group.id)\n }\n return initial\n })\n\n const isOpen = (group: ResolvedGroup): boolean => {\n if (group.role === \"pinned\") return true\n if (group.role === \"appendix\") return openSet.has(group.id)\n // Collapsible product group.\n if (isControlled) {\n return group.id === activeProduct || group.id === activeGroupId\n }\n return openSet.has(group.id) || group.id === activeGroupId\n }\n\n const toggle = (group: ResolvedGroup): void => {\n if (group.role === \"pinned\") return\n if (group.role === \"collapsible\" && isControlled) {\n onProductToggle(group.id)\n return\n }\n setOpenSet((prev) => {\n const next = new Set(prev)\n if (next.has(group.id)) next.delete(group.id)\n else next.add(group.id)\n return next\n })\n }\n\n return (\n <nav\n ref={ref}\n aria-label={ariaLabel ?? \"Documentation\"}\n data-slot=\"doc-nav\"\n className={cn(styles.root, className)}\n {...props}\n >\n {groups.map((group) => (\n <DocNavGroup\n key={group.id}\n group={group}\n open={isOpen(group)}\n currentPath={currentPath}\n onToggle={() => toggle(group)}\n />\n ))}\n </nav>\n )\n }\n)\nDocNav.displayName = \"DocNav\"\n\n// ─── DocNavGroup (internal) ──────────────────────────────────────────────────\n\ninterface DocNavGroupProps {\n group: ResolvedGroup\n open: boolean\n currentPath: string\n onToggle: () => void\n}\n\nfunction DocNavGroup({ group, open, currentPath, onToggle }: DocNavGroupProps) {\n const panelId = React.useId()\n const isPinned = group.role === \"pinned\"\n const Caret = open ? CaretDownIcon : CaretRightIcon\n\n const head = (\n <>\n <Caret className={styles.caret} weight=\"fill\" aria-hidden=\"true\" />\n <span className={styles.scopeDot} aria-hidden=\"true\" />\n <span className={styles.groupLabel}>{group.label}</span>\n {!open && <span className={styles.count}>{group.entries.length}</span>}\n </>\n )\n\n return (\n <div\n data-slot=\"doc-nav-group\"\n data-group={group.id}\n data-role={group.role}\n data-state={open ? \"open\" : \"closed\"}\n className={cn(\n styles.group,\n isPinned && styles.groupPinned,\n !open && styles.groupCollapsed\n )}\n >\n {isPinned ? (\n <div className={styles.head}>{head}</div>\n ) : (\n <button\n type=\"button\"\n data-slot=\"doc-nav-group-trigger\"\n className={cn(styles.head, styles.headButton)}\n aria-expanded={open}\n aria-controls={panelId}\n onClick={onToggle}\n >\n {head}\n </button>\n )}\n\n {open && (\n <div id={panelId} className={styles.pills}>\n {group.entries.map((entry, index) => (\n <DocNavPill\n key={entry.href}\n entry={entry}\n active={hrefMatchesPath(entry.href, currentPath)}\n // Hub = the group's overview entry: an explicit order-0 doc, or the\n // lead pill of the pinned Shared set (which often starts at order 1).\n isHub={entry.order === 0 || (group.role === \"pinned\" && index === 0)}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// ─── DocNavPill (internal) ───────────────────────────────────────────────────\n\ninterface DocNavPillProps {\n entry: DocEntry\n active: boolean\n /** Marks the group's hub/overview entry — a leading accent dot. */\n isHub: boolean\n}\n\nfunction DocNavPill({ entry, active, isHub }: DocNavPillProps) {\n const external = isExternalEntry(entry)\n\n return (\n <a\n href={entry.href}\n data-slot=\"doc-nav-pill\"\n data-active={active || undefined}\n data-hub={isHub || undefined}\n aria-current={active ? \"page\" : undefined}\n className={cn(styles.pill, active && styles.pillActive, isHub && styles.pillHub)}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n >\n <span className={styles.pillNum}>{entry.order}</span>\n <span className={styles.pillLabel}>{entry.label}</span>\n {external && (\n <>\n <ArrowSquareOutIcon className={styles.pillExternal} aria-hidden=\"true\" />\n <span className={styles.srOnly}>(opens in a new tab)</span>\n </>\n )}\n </a>\n )\n}\n\nexport { DocNav }\n"
2815
2815
  },
2816
2816
  {
2817
2817
  "path": "components/ui/doc-nav/doc-nav.module.css",
@@ -2837,7 +2837,7 @@
2837
2837
  {
2838
2838
  "path": "components/ui/doc-frame/doc-frame.tsx",
2839
2839
  "type": "registry:ui",
2840
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { CompassIcon } from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport { DocNav, type DocEntry } from \"../doc-nav/doc-nav\"\nimport styles from \"./doc-frame.module.css\"\n\n/**\n * PL-2185 · VI-609 — the themed doc-page shell that wraps <DocNav>.\n *\n * DocFrame owns the page: theme tokens, a sticky header (a flexible brand/logo\n * slot, an OVERVIEW home pill, and a right-aligned meta slot), the <DocNav>\n * slot, and the content wrapper (the doc as children). It reads the single\n * `manifest`, derives the active product, and passes the slice + active-state\n * down to DocNav. Everything is themed by Visor tokens, so the shell adopts the\n * active project theme without modification.\n *\n * Replaces the vanilla-JS doc shell (nav.js + docs.css). `children` accepts any\n * content — a Next route's MDX, or a static doc's HTML body injected server-side\n * by a downstream catch-all route — so one React frame serves both tracks.\n */\n\n// ─── manifest types ───────────────────────────────────────────────────────────\n\n/**\n * A product entry in the PL-2177 roster. `id` matches the values in each\n * `DocEntry.scope[]` and the DocNav group id; `label` is the visible name\n * (defaults to a title-cased id).\n */\nexport interface DocProductEntry {\n /** Product id — matches `DocEntry.scope[]` values and the DocNav group id. */\n id: string\n /** Visible product name. Defaults to a title-cased `id`. */\n label?: string\n}\n\n/**\n * The parsed docs manifest — the single input DocFrame derives everything from.\n * Additive over PL-2177: `docs` is required; `products`, `brand`, and\n * `dispositions` are optional. Absence of `products` (or a single product) =\n * single-product mode: DocNav degrades to one grouped row, no accordion.\n */\nexport interface DocsManifest {\n /** Every documentation entry across the shared set and all products. */\n docs: DocEntry[]\n /**\n * PL-2177 product roster. Absent or length ≤ 1 → single-product mode.\n * When omitted, products are inferred from the distinct `scope` values in\n * `docs`.\n */\n products?: DocProductEntry[]\n /**\n * Text wordmark shown when no `logo` prop is passed and the active theme\n * ships no `brand.logo` SVG (the final fallback in the logo resolution order).\n */\n brand?: string\n /** PL-2170 disposition map — referenced, owned there. Opaque pass-through. */\n dispositions?: Record<string, unknown>\n}\n\n/** The OVERVIEW / home pill in the header — a link back to the docs hub. */\nexport interface DocFrameHome {\n /** Destination for the home pill. */\n href: string\n /** Visible label — rendered mono, UPPERCASE (e.g. `\"Overview\"`). */\n label: string\n}\n\n// ─── props ─────────────────────────────────────────────────────────────────────\n\nexport interface DocFrameProps\n extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The parsed manifest — the single input everything derives from. */\n manifest: DocsManifest\n /**\n * The brand slot. Any node — an `<img>` of an SVG, an inline `<svg>`, or a\n * full component (an animated mark, a Visor `<Brand>`). Resolution order:\n * explicit `logo` → the active theme's `brand.logo` SVG (mode-aware via\n * `--brand-logo`, upgraded only once it loads) → the manifest `brand` text.\n */\n logo?: React.ReactNode\n /**\n * The OVERVIEW / home pill rendered after the brand — a bordered mono chip\n * with a leading compass glyph. Omit to hide it.\n */\n home?: DocFrameHome\n /**\n * A right-aligned breadcrumb / status slot in the header (mono, UPPERCASE,\n * `--text-tertiary`) — e.g. `ARTIST · BUILD-READY`. Any node.\n */\n meta?: React.ReactNode\n /**\n * Which product group is expanded (the accordion). Defaults to the route's\n * product, else the first product in the roster. Absent roster →\n * single-product mode.\n */\n activeProduct?: string\n /**\n * The active route, for active-state resolution. Next consumers pass\n * `usePathname()`; a static page passes `location.pathname`. Defaults to\n * `window.location.pathname` in the browser (framework-agnostic — no hard\n * next/navigation dependency), or `\"/\"` during SSR.\n */\n currentPath?: string\n /**\n * A Visor theme class name applied to the shell root, scoping all doc-shell\n * CSS variables. Defaults to the app's ambient theme (inherited from an\n * ancestor). e.g. `theme=\"strata-theme\"`.\n */\n theme?: string\n /**\n * Per-group nav accent overrides, keyed by DocNav group id or role, driving\n * its `--doc-nav-group-accent` hook. Merged over the sensible default palette\n * (`pro → --warning`, the amber Pro dot); DocNav already defaults\n * shared → --info, other products → --accent, appendix → --text-tertiary.\n * Values are CSS colors or `var(--token)` references.\n */\n groupAccents?: Record<string, string>\n /**\n * Force the borderless treatment — null the pinned Shared group's frame\n * (`--doc-nav-pin-border: transparent`) and settle its fill onto the card\n * surface, for themes that carry structure from surface contrast, not borders.\n * A theme that is borderless by design nulls the token in its own CSS; this\n * prop is the per-instance escape hatch.\n */\n borderless?: boolean\n /** The doc content, rendered in the content wrapper below the nav. */\n children: React.ReactNode\n}\n\n// ─── constants ─────────────────────────────────────────────────────────────────\n\n/**\n * Default per-group nav accents. DocNav already resolves shared → --info,\n * products → --accent, and appendix → --text-tertiary via its own fallbacks;\n * the only design-intent override the frame supplies is the amber Pro dot.\n */\nconst DEFAULT_GROUP_ACCENTS: Record<string, string> = {\n pro: \"var(--warning, #d97706)\",\n}\n\n// ─── helpers ───────────────────────────────────────────────────────────────────\n\nfunction titleCase(value: string): string {\n if (value.length === 0) return value\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n\n/** Strip query + hash so an href compares cleanly against the current path. */\nfunction normalizePath(value: string): string {\n return value.replace(/[?#].*$/, \"\")\n}\n\n/** Extract the URL from a computed `url(\"…\")` custom-property value, if any. */\nfunction cssUrl(value: string): string | null {\n const match = value.match(/url\\(\\s*[\"']?([^\"')]+)[\"']?\\s*\\)/)\n return match ? match[1] : null\n}\n\n/**\n * Resolve the product roster: the explicit `manifest.products`, else inferred\n * from the distinct `scope` values across `docs` (first-appearance order).\n */\nfunction resolveProducts(manifest: DocsManifest): DocProductEntry[] {\n if (manifest.products && manifest.products.length > 0) {\n return manifest.products\n }\n const seen = new Map<string, DocProductEntry>()\n for (const doc of manifest.docs) {\n if (!Array.isArray(doc.scope)) continue\n for (const id of doc.scope) {\n if (!seen.has(id)) seen.set(id, { id, label: titleCase(id) })\n }\n }\n return [...seen.values()]\n}\n\n/** The product owning the doc at `path` (its first `scope`), if any. */\nfunction productForPath(docs: DocEntry[], path: string): string | undefined {\n const target = normalizePath(path)\n for (const doc of docs) {\n if (normalizePath(doc.href) !== target) continue\n if (Array.isArray(doc.scope) && doc.scope.length > 0) return doc.scope[0]\n return undefined\n }\n return undefined\n}\n\n// ─── brand slot ────────────────────────────────────────────────────────────────\n\n/**\n * The default brand slot: the active theme's `brand.logo` SVG when the theme\n * ships one (via the `--brand-logo` custom property, mode-aware light/dark),\n * else the manifest `brand` text wordmark (a leading glyph chip + the name).\n *\n * SSR-safe + hardened by construction: the resting state renders the text\n * wordmark (always visible), and a client effect upgrades to the theme logo\n * only once the image actually loads — a 404 keeps the wordmark, never an empty\n * slot, and there is never a flash of invisibility on reload.\n */\nfunction DocFrameBrand({ brand }: { brand?: string }) {\n const ref = React.useRef<HTMLSpanElement>(null)\n const [hasThemeLogo, setHasThemeLogo] = React.useState(false)\n\n React.useEffect(() => {\n const el = ref.current\n if (!el || typeof window === \"undefined\") return\n const value = window\n .getComputedStyle(el)\n .getPropertyValue(\"--brand-logo\")\n .trim()\n const url = value && value !== \"none\" ? cssUrl(value) : null\n if (!url) {\n setHasThemeLogo(false)\n return\n }\n // Hardening: probe-load the logo and only upgrade once it resolves, so a\n // failed load (a 404 theme-logo URL) leaves the visible wordmark in place.\n let cancelled = false\n const probe = new window.Image()\n probe.onload = () => {\n if (!cancelled) setHasThemeLogo(true)\n }\n probe.onerror = () => {\n if (!cancelled) setHasThemeLogo(false)\n }\n probe.src = url\n return () => {\n cancelled = true\n }\n }, [])\n\n const glyphChar = brand?.trim().charAt(0).toUpperCase()\n\n return (\n <span\n ref={ref}\n className={styles.brand}\n data-slot=\"doc-frame-brand\"\n data-theme-logo={hasThemeLogo || undefined}\n >\n <span\n className={styles.themeLogo}\n role=\"img\"\n aria-label={brand ?? \"Documentation\"}\n />\n {brand ? (\n <span className={styles.wordmark}>\n {glyphChar ? (\n <span className={styles.glyph} aria-hidden=\"true\">\n {glyphChar}\n </span>\n ) : null}\n {brand}\n </span>\n ) : null}\n </span>\n )\n}\n\n// ─── DocFrame ────────────────────────────────────────────────────────────────\n\nconst DocFrame = React.forwardRef<HTMLDivElement, DocFrameProps>(\n (\n {\n manifest,\n logo,\n home,\n meta,\n activeProduct,\n currentPath,\n theme,\n groupAccents,\n borderless,\n children,\n className,\n style,\n ...props\n },\n ref\n ) => {\n const resolvedPath =\n currentPath ??\n (typeof window !== \"undefined\" ? window.location.pathname : \"/\")\n\n const products = React.useMemo(() => resolveProducts(manifest), [manifest])\n const isMultiProduct = products.length > 1\n\n // Seed the open accordion product: explicit prop → the route's product →\n // the first product in the roster.\n const seededProduct =\n activeProduct ??\n productForPath(manifest.docs, resolvedPath) ??\n products[0]?.id\n\n const [openProduct, setOpenProduct] = React.useState<string | undefined>(\n seededProduct\n )\n\n // Keep the open product in sync when a controlling `activeProduct` changes.\n React.useEffect(() => {\n if (activeProduct) setOpenProduct(activeProduct)\n }, [activeProduct])\n\n const resolvedLogo = logo ?? <DocFrameBrand brand={manifest.brand} />\n\n // Borderless drops the pinned-group frame via `--doc-nav-pin-border`. Set it\n // explicitly with the `borderless` prop; a theme that is borderless by design\n // nulls the token in its own CSS rather than the component hardcoding (and\n // leaking) private theme names into the public bundle.\n const isBorderless = borderless ?? false\n\n // Per-group nav accents → DocNav's `--doc-nav-group-accent` hook. Rendered\n // as a frame-scoped <style> keyed to this instance so it never leaks to a\n // sibling DocFrame. DocNav only *reads* the variable (via a fallback), so\n // any value set here wins with no specificity battle.\n const frameId = React.useId()\n const resolvedAccents: Record<string, string> = {\n ...DEFAULT_GROUP_ACCENTS,\n ...groupAccents,\n }\n const accentCss = Object.entries(resolvedAccents)\n .map(\n ([key, value]) =>\n `[data-doc-frame=\"${frameId}\"] [data-group=\"${key}\"],` +\n `[data-doc-frame=\"${frameId}\"] [data-role=\"${key}\"]` +\n `{--doc-nav-group-accent:${value}}`\n )\n .join(\"\")\n\n const frameStyle = {\n ...(isBorderless\n ? {\n \"--doc-nav-pin-border\": \"transparent\",\n \"--doc-nav-pin-bg\": \"var(--surface-card)\",\n }\n : {}),\n ...style,\n } as React.CSSProperties\n\n return (\n <div\n ref={ref}\n data-slot=\"doc-frame\"\n data-doc-frame={frameId}\n data-active-product={isMultiProduct ? openProduct : undefined}\n data-borderless={isBorderless || undefined}\n className={cn(styles.frame, theme, className)}\n style={frameStyle}\n {...props}\n >\n {accentCss ? <style>{accentCss}</style> : null}\n <header data-slot=\"doc-frame-header\" className={styles.chrome}>\n <div className={styles.headerRow}>\n <div className={styles.brandSlot} data-slot=\"doc-frame-brand-slot\">\n {resolvedLogo}\n </div>\n {home ? (\n <a\n href={home.href}\n className={styles.home}\n data-slot=\"doc-frame-home\"\n >\n <CompassIcon\n className={styles.homeIcon}\n weight=\"regular\"\n aria-hidden=\"true\"\n />\n <span>{home.label}</span>\n </a>\n ) : null}\n <div className={styles.spacer} aria-hidden=\"true\" />\n {meta ? (\n <div className={styles.meta} data-slot=\"doc-frame-meta\">\n {meta}\n </div>\n ) : null}\n </div>\n <div className={styles.navRow} data-slot=\"doc-frame-nav\">\n <DocNav\n docs={manifest.docs}\n currentPath={resolvedPath}\n activeProduct={isMultiProduct ? openProduct : undefined}\n onProductToggle={isMultiProduct ? setOpenProduct : undefined}\n />\n </div>\n </header>\n\n <div className={styles.content} data-slot=\"doc-frame-content\">\n {children}\n </div>\n </div>\n )\n }\n)\nDocFrame.displayName = \"DocFrame\"\n\nexport { DocFrame }\n"
2840
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { CompassIcon } from \"@phosphor-icons/react\"\nimport { cn } from \"../../../lib/utils\"\nimport { DocNav, type DocEntry } from \"../doc-nav/doc-nav\"\nimport styles from \"./doc-frame.module.css\"\n\n/**\n * PL-2185 · VI-609 — the themed doc-page shell that wraps <DocNav>.\n *\n * DocFrame owns the page: theme tokens, a sticky header (a flexible brand/logo\n * slot, an OVERVIEW home pill, and a right-aligned meta slot), the <DocNav>\n * slot, and the content wrapper (the doc as children). It reads the single\n * `manifest`, derives the active product, and passes the slice + active-state\n * down to DocNav. Everything is themed by Visor tokens, so the shell adopts the\n * active project theme without modification.\n *\n * Replaces the vanilla-JS doc shell (nav.js + docs.css). `children` accepts any\n * content — a Next route's MDX, or a static doc's HTML body injected server-side\n * by a downstream catch-all route — so one React frame serves both tracks.\n */\n\n// ─── manifest types ───────────────────────────────────────────────────────────\n\n/**\n * A product entry in the PL-2177 roster. `id` matches the values in each\n * `DocEntry.scope[]` and the DocNav group id; `label` is the visible name\n * (defaults to a title-cased id).\n */\nexport interface DocProductEntry {\n /** Product id — matches `DocEntry.scope[]` values and the DocNav group id. */\n id: string\n /** Visible product name. Defaults to a title-cased `id`. */\n label?: string\n}\n\n/**\n * The parsed docs manifest — the single input DocFrame derives everything from.\n * Additive over PL-2177: `docs` is required; `products`, `brand`, and\n * `dispositions` are optional. Absence of `products` (or a single product) =\n * single-product mode: DocNav degrades to one grouped row, no accordion.\n */\nexport interface DocsManifest {\n /** Every documentation entry across the shared set and all products. */\n docs: DocEntry[]\n /**\n * PL-2177 product roster. Absent or length ≤ 1 → single-product mode.\n * When omitted, products are inferred from the distinct `scope` values in\n * `docs`.\n */\n products?: DocProductEntry[]\n /**\n * Text wordmark shown when no `logo` prop is passed and the active theme\n * ships no `brand.logo` SVG (the final fallback in the logo resolution order).\n */\n brand?: string\n /** PL-2170 disposition map — referenced, owned there. Opaque pass-through. */\n dispositions?: Record<string, unknown>\n}\n\n/** The OVERVIEW / home pill in the header — a link back to the docs hub. */\nexport interface DocFrameHome {\n /** Destination for the home pill. */\n href: string\n /** Visible label — rendered mono, UPPERCASE (e.g. `\"Overview\"`). */\n label: string\n}\n\n// ─── props ─────────────────────────────────────────────────────────────────────\n\nexport interface DocFrameProps\n extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The parsed manifest — the single input everything derives from. */\n manifest: DocsManifest\n /**\n * The brand slot. Any node — an `<img>` of an SVG, an inline `<svg>`, or a\n * full component (an animated mark, a Visor `<Brand>`). Resolution order:\n * explicit `logo` → the active theme's `brand.logo` SVG (mode-aware via\n * `--brand-logo`, upgraded only once it loads) → the manifest `brand` text.\n */\n logo?: React.ReactNode\n /**\n * The OVERVIEW / home pill rendered after the brand — a bordered mono chip\n * with a leading compass glyph. Omit to hide it.\n */\n home?: DocFrameHome\n /**\n * A right-aligned breadcrumb / status slot in the header (mono, UPPERCASE,\n * `--text-tertiary`) — e.g. `ARTIST · BUILD-READY`. Any node.\n */\n meta?: React.ReactNode\n /**\n * Which product group is expanded (the accordion). Defaults to the route's\n * product, else the first product in the roster. Absent roster →\n * single-product mode.\n */\n activeProduct?: string\n /**\n * The active route, for active-state resolution. Next consumers pass\n * `usePathname()`; a static page passes `location.pathname`. Defaults to\n * `window.location.pathname` in the browser (framework-agnostic — no hard\n * next/navigation dependency), or `\"/\"` during SSR.\n */\n currentPath?: string\n /**\n * A Visor theme class name applied to the shell root, scoping all doc-shell\n * CSS variables. Defaults to the app's ambient theme (inherited from an\n * ancestor). e.g. `theme=\"strata-theme\"`.\n */\n theme?: string\n /**\n * Per-group nav accent overrides, keyed by DocNav group id or role, driving\n * its `--doc-nav-group-accent` hook. Merged over the sensible default palette\n * (`pro → --warning`, the amber Pro dot); DocNav already defaults\n * shared → --info, other products → --accent, appendix → --text-tertiary.\n * Values are CSS colors or `var(--token)` references.\n */\n groupAccents?: Record<string, string>\n /**\n * Force the borderless treatment — null the pinned Shared group's frame\n * (`--doc-nav-pin-border: transparent`) and settle its fill onto the card\n * surface, for themes that carry structure from surface contrast, not borders.\n * A theme that is borderless by design nulls the token in its own CSS; this\n * prop is the per-instance escape hatch.\n */\n borderless?: boolean\n /** The doc content, rendered in the content wrapper below the nav. */\n children: React.ReactNode\n}\n\n// ─── constants ─────────────────────────────────────────────────────────────────\n\n/**\n * Default per-group nav accents. DocNav already resolves shared → --info,\n * products → --accent, and appendix → --text-tertiary via its own fallbacks;\n * the only design-intent override the frame supplies is the amber Pro dot.\n */\nconst DEFAULT_GROUP_ACCENTS: Record<string, string> = {\n pro: \"var(--warning, #d97706)\",\n}\n\n// ─── helpers ───────────────────────────────────────────────────────────────────\n\nfunction titleCase(value: string): string {\n if (value.length === 0) return value\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n\n/** Strip query + hash so an href compares cleanly against the current path. */\nfunction normalizePath(value: string): string {\n return value.replace(/[?#].*$/, \"\")\n}\n\n/** Extract the URL from a computed `url(\"…\")` custom-property value, if any. */\nfunction cssUrl(value: string): string | null {\n const match = value.match(/url\\(\\s*[\"']?([^\"')]+)[\"']?\\s*\\)/)\n if (!match) return null\n // Bind-then-guard: index reads are only `string` under a checked-index\n // compiler. \"Matched but captured nothing\" collapses to the same `null` the\n // no-match branch returns, which every caller already handles.\n const url = match[1]\n if (url === undefined) return null\n return url\n}\n\n/**\n * Resolve the product roster: the explicit `manifest.products`, else inferred\n * from the distinct `scope` values across `docs` (first-appearance order).\n */\nfunction resolveProducts(manifest: DocsManifest): DocProductEntry[] {\n if (manifest.products && manifest.products.length > 0) {\n return manifest.products\n }\n const seen = new Map<string, DocProductEntry>()\n for (const doc of manifest.docs) {\n if (!Array.isArray(doc.scope)) continue\n for (const id of doc.scope) {\n if (!seen.has(id)) seen.set(id, { id, label: titleCase(id) })\n }\n }\n return [...seen.values()]\n}\n\n/** The product owning the doc at `path` (its first `scope`), if any. */\nfunction productForPath(docs: DocEntry[], path: string): string | undefined {\n const target = normalizePath(path)\n for (const doc of docs) {\n if (normalizePath(doc.href) !== target) continue\n if (Array.isArray(doc.scope) && doc.scope.length > 0) return doc.scope[0]\n return undefined\n }\n return undefined\n}\n\n// ─── brand slot ────────────────────────────────────────────────────────────────\n\n/**\n * The default brand slot: the active theme's `brand.logo` SVG when the theme\n * ships one (via the `--brand-logo` custom property, mode-aware light/dark),\n * else the manifest `brand` text wordmark (a leading glyph chip + the name).\n *\n * SSR-safe + hardened by construction: the resting state renders the text\n * wordmark (always visible), and a client effect upgrades to the theme logo\n * only once the image actually loads — a 404 keeps the wordmark, never an empty\n * slot, and there is never a flash of invisibility on reload.\n */\nfunction DocFrameBrand({ brand }: { brand?: string }) {\n const ref = React.useRef<HTMLSpanElement>(null)\n const [hasThemeLogo, setHasThemeLogo] = React.useState(false)\n\n React.useEffect(() => {\n const el = ref.current\n if (!el || typeof window === \"undefined\") return\n const value = window\n .getComputedStyle(el)\n .getPropertyValue(\"--brand-logo\")\n .trim()\n const url = value && value !== \"none\" ? cssUrl(value) : null\n if (!url) {\n setHasThemeLogo(false)\n return\n }\n // Hardening: probe-load the logo and only upgrade once it resolves, so a\n // failed load (a 404 theme-logo URL) leaves the visible wordmark in place.\n let cancelled = false\n const probe = new window.Image()\n probe.onload = () => {\n if (!cancelled) setHasThemeLogo(true)\n }\n probe.onerror = () => {\n if (!cancelled) setHasThemeLogo(false)\n }\n probe.src = url\n return () => {\n cancelled = true\n }\n }, [])\n\n const glyphChar = brand?.trim().charAt(0).toUpperCase()\n\n return (\n <span\n ref={ref}\n className={styles.brand}\n data-slot=\"doc-frame-brand\"\n data-theme-logo={hasThemeLogo || undefined}\n >\n <span\n className={styles.themeLogo}\n role=\"img\"\n aria-label={brand ?? \"Documentation\"}\n />\n {brand ? (\n <span className={styles.wordmark}>\n {glyphChar ? (\n <span className={styles.glyph} aria-hidden=\"true\">\n {glyphChar}\n </span>\n ) : null}\n {brand}\n </span>\n ) : null}\n </span>\n )\n}\n\n// ─── DocFrame ────────────────────────────────────────────────────────────────\n\nconst DocFrame = React.forwardRef<HTMLDivElement, DocFrameProps>(\n (\n {\n manifest,\n logo,\n home,\n meta,\n activeProduct,\n currentPath,\n theme,\n groupAccents,\n borderless,\n children,\n className,\n style,\n ...props\n },\n ref\n ) => {\n const resolvedPath =\n currentPath ??\n (typeof window !== \"undefined\" ? window.location.pathname : \"/\")\n\n const products = React.useMemo(() => resolveProducts(manifest), [manifest])\n const isMultiProduct = products.length > 1\n\n // Seed the open accordion product: explicit prop → the route's product →\n // the first product in the roster.\n const seededProduct =\n activeProduct ??\n productForPath(manifest.docs, resolvedPath) ??\n products[0]?.id\n\n const [openProduct, setOpenProduct] = React.useState<string | undefined>(\n seededProduct\n )\n\n // Keep the open product in sync when a controlling `activeProduct` changes.\n React.useEffect(() => {\n if (activeProduct) setOpenProduct(activeProduct)\n }, [activeProduct])\n\n const resolvedLogo = logo ?? <DocFrameBrand brand={manifest.brand} />\n\n // Borderless drops the pinned-group frame via `--doc-nav-pin-border`. Set it\n // explicitly with the `borderless` prop; a theme that is borderless by design\n // nulls the token in its own CSS rather than the component hardcoding (and\n // leaking) private theme names into the public bundle.\n const isBorderless = borderless ?? false\n\n // Per-group nav accents → DocNav's `--doc-nav-group-accent` hook. Rendered\n // as a frame-scoped <style> keyed to this instance so it never leaks to a\n // sibling DocFrame. DocNav only *reads* the variable (via a fallback), so\n // any value set here wins with no specificity battle.\n const frameId = React.useId()\n const resolvedAccents: Record<string, string> = {\n ...DEFAULT_GROUP_ACCENTS,\n ...groupAccents,\n }\n const accentCss = Object.entries(resolvedAccents)\n .map(\n ([key, value]) =>\n `[data-doc-frame=\"${frameId}\"] [data-group=\"${key}\"],` +\n `[data-doc-frame=\"${frameId}\"] [data-role=\"${key}\"]` +\n `{--doc-nav-group-accent:${value}}`\n )\n .join(\"\")\n\n const frameStyle = {\n ...(isBorderless\n ? {\n \"--doc-nav-pin-border\": \"transparent\",\n \"--doc-nav-pin-bg\": \"var(--surface-card)\",\n }\n : {}),\n ...style,\n } as React.CSSProperties\n\n return (\n <div\n ref={ref}\n data-slot=\"doc-frame\"\n data-doc-frame={frameId}\n data-active-product={isMultiProduct ? openProduct : undefined}\n data-borderless={isBorderless || undefined}\n className={cn(styles.frame, theme, className)}\n style={frameStyle}\n {...props}\n >\n {accentCss ? <style>{accentCss}</style> : null}\n <header data-slot=\"doc-frame-header\" className={styles.chrome}>\n <div className={styles.headerRow}>\n <div className={styles.brandSlot} data-slot=\"doc-frame-brand-slot\">\n {resolvedLogo}\n </div>\n {home ? (\n <a\n href={home.href}\n className={styles.home}\n data-slot=\"doc-frame-home\"\n >\n <CompassIcon\n className={styles.homeIcon}\n weight=\"regular\"\n aria-hidden=\"true\"\n />\n <span>{home.label}</span>\n </a>\n ) : null}\n <div className={styles.spacer} aria-hidden=\"true\" />\n {meta ? (\n <div className={styles.meta} data-slot=\"doc-frame-meta\">\n {meta}\n </div>\n ) : null}\n </div>\n <div className={styles.navRow} data-slot=\"doc-frame-nav\">\n <DocNav\n docs={manifest.docs}\n currentPath={resolvedPath}\n activeProduct={isMultiProduct ? openProduct : undefined}\n onProductToggle={isMultiProduct ? setOpenProduct : undefined}\n />\n </div>\n </header>\n\n <div className={styles.content} data-slot=\"doc-frame-content\">\n {children}\n </div>\n </div>\n )\n }\n)\nDocFrame.displayName = \"DocFrame\"\n\nexport { DocFrame }\n"
2841
2841
  },
2842
2842
  {
2843
2843
  "path": "components/ui/doc-frame/doc-frame.module.css",
@@ -3017,7 +3017,7 @@
3017
3017
  {
3018
3018
  "path": "components/ui/status-badge/status-badge.module.css",
3019
3019
  "type": "registry:ui",
3020
- "content": "/*\n * StatusBadge — semantic wrapper over Badge.\n *\n * These styles only cover the additions on top of Badge: the leading\n * indicator dot, its per-status color, the optional pulse animation, and\n * the visually-hidden \"Status:\" prefix. All visual chrome (padding, radius,\n * typography, background) is inherited from Badge.\n */\n\n/* Indicator dot — filled circle, 0.5rem, colored per status */\n.indicator {\n display: inline-block;\n width: 0.5rem;\n height: 0.5rem;\n flex-shrink: 0;\n border-radius: var(--radius-full, 9999px);\n background-color: currentColor;\n}\n\n/* Per-color-group fills. Use brand surface default tokens so the dot\n * reads as a saturated status color independent of the Badge subtle bg. */\n.indicatorSuccess {\n background-color: var(--surface-success-default, transparent);\n}\n\n.indicatorWarning {\n background-color: var(--surface-warning-default, transparent);\n}\n\n.indicatorDestructive {\n background-color: var(--surface-error-default, transparent);\n}\n\n.indicatorInfo {\n background-color: var(--surface-info-default, transparent);\n}\n\n.indicatorNeutral {\n background-color: var(--text-tertiary, currentColor);\n}\n\n/* In filled tone, the dot sits on top of a saturated brand fill —\n * paint it with the same inverse token the Badge uses for its text\n * so it's visible in every theme. */\n[data-tone=\"filled\"] .indicatorSuccess,\n[data-tone=\"filled\"] .indicatorWarning,\n[data-tone=\"filled\"] .indicatorDestructive,\n[data-tone=\"filled\"] .indicatorInfo {\n background-color: var(--text-inverse, currentColor);\n}\n\n/* Neutral's filled fill (filled-neutral) is a fixed dark gray in both modes,\n * so its dot stays white rather than following --text-inverse (which flips\n * dark in dark mode). */\n[data-tone=\"filled\"] .indicatorNeutral {\n background-color: var(--color-white, #ffffff);\n}\n\n/* Pulse animation — soft opacity + scale.\n * Keyframes are defined locally to keep the component self-contained. */\n.pulse {\n /* Doubled 800ms token gives ~1.6s per cycle — slow enough to read as a\n * heartbeat without drawing focus away from content. */\n animation: status-badge-pulse\n calc(var(--motion-duration-800, 800ms) * 2)\n var(--motion-easing-standard, ease-in-out) infinite;\n transform-origin: center;\n will-change: opacity, transform;\n}\n\n@keyframes status-badge-pulse {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.55;\n transform: scale(0.85);\n }\n}\n\n/* Respect reduced motion */\n@media (prefers-reduced-motion: reduce) {\n .pulse {\n animation: none;\n }\n}\n\n/* Mono label variant (Animal dialog substrate, VI-620) — monospace token\n * label at the 11px chrome step, for ID / code / token-style status readouts.\n * Overrides only the label typography; dot, color, and chrome are unchanged. */\n.labelMono {\n font-family: var(--font-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);\n font-size: var(--text-11, 0.6875rem);\n letter-spacing: 0.02em;\n}\n\n/* Visually hidden but announced to screen readers */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n"
3020
+ "content": "/*\n * StatusBadge — semantic wrapper over Badge.\n *\n * These styles only cover the additions on top of Badge: the leading\n * indicator dot, its per-status color, the optional pulse animation, and\n * the visually-hidden \"Status:\" prefix. All visual chrome (padding, radius,\n * typography, background) is inherited from Badge.\n */\n\n/* Indicator dot — filled circle, 0.5rem, colored per status.\n VI-625: --status-badge-dot-size / -dot-radius let a theme square the marker\n (Linear-style) or resize it without forking the component. */\n.indicator {\n display: inline-block;\n width: var(--status-badge-dot-size, 0.5rem);\n height: var(--status-badge-dot-size, 0.5rem);\n flex-shrink: 0;\n border-radius: var(--status-badge-dot-radius, var(--radius-full, 9999px));\n background-color: currentColor;\n}\n\n/* Per-color-group fills. Use brand surface default tokens so the dot\n * reads as a saturated status color independent of the Badge subtle bg. */\n.indicatorSuccess {\n background-color: var(--surface-success-default, transparent);\n}\n\n.indicatorWarning {\n background-color: var(--surface-warning-default, transparent);\n}\n\n.indicatorDestructive {\n background-color: var(--surface-error-default, transparent);\n}\n\n.indicatorInfo {\n background-color: var(--surface-info-default, transparent);\n}\n\n.indicatorNeutral {\n background-color: var(--text-tertiary, currentColor);\n}\n\n/* In filled tone, the dot sits on top of a saturated brand fill —\n * paint it with the same inverse token the Badge uses for its text\n * so it's visible in every theme. */\n[data-tone=\"filled\"] .indicatorSuccess,\n[data-tone=\"filled\"] .indicatorWarning,\n[data-tone=\"filled\"] .indicatorDestructive,\n[data-tone=\"filled\"] .indicatorInfo {\n background-color: var(--text-inverse, currentColor);\n}\n\n/* Neutral's filled fill (filled-neutral) is a fixed dark gray in both modes,\n * so its dot stays white rather than following --text-inverse (which flips\n * dark in dark mode). */\n[data-tone=\"filled\"] .indicatorNeutral {\n background-color: var(--color-white, #ffffff);\n}\n\n/* Pulse animation — soft opacity + scale.\n * Keyframes are defined locally to keep the component self-contained. */\n.pulse {\n /* Doubled 800ms token gives ~1.6s per cycle — slow enough to read as a\n * heartbeat without drawing focus away from content. */\n animation: status-badge-pulse\n calc(var(--motion-duration-800, 800ms) * 2)\n var(--motion-easing-standard, ease-in-out) infinite;\n transform-origin: center;\n will-change: opacity, transform;\n}\n\n@keyframes status-badge-pulse {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.55;\n transform: scale(0.85);\n }\n}\n\n/* Respect reduced motion */\n@media (prefers-reduced-motion: reduce) {\n .pulse {\n animation: none;\n }\n}\n\n/* Mono label variant (Animal dialog substrate, VI-620) — monospace token\n * label at the 11px chrome step, for ID / code / token-style status readouts.\n * Overrides only the label typography; dot, color, and chrome are unchanged. */\n.labelMono {\n font-family: var(--status-badge-mono-font-family, var(--font-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace));\n font-size: var(--status-badge-mono-font-size, var(--text-11, 0.6875rem));\n letter-spacing: var(--status-badge-mono-letter-spacing, 0.02em);\n}\n\n/* Visually hidden but announced to screen readers */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n"
3021
3021
  }
3022
3022
  ]
3023
3023
  },
@@ -4094,7 +4094,7 @@
4094
4094
  {
4095
4095
  "path": "blocks/command-dialog/command-dialog.module.css",
4096
4096
  "type": "registry:block",
4097
- "content": "/* CommandDialog block — ⌘K palette composing Command + Dialog primitives.\n * Ports the r3 admin-ui palette visual contract onto Visor tokens; no new\n * tokens are introduced (everything resolves through the active theme).\n */\n\n/* ── Dialog content override ─────────────────────────────────────────── */\n\n/* DialogContent reset — transparent, no padding, no border. The Command\n * primitive paints its own surface. Tight max width matches r3 (640px). */\n.dialog {\n background: transparent;\n padding: 0;\n border: 0;\n width: 40rem;\n max-width: 90vw;\n box-shadow: none;\n}\n\n/* Hide DialogContent's built-in close X — palette closes via Esc only. */\n.dialog > button:last-child {\n display: none;\n}\n\n/* Visually-hidden DialogTitle for a11y (Radix requires one). */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* ── Command root ────────────────────────────────────────────────────── */\n\n.command {\n background: var(--surface-card, #ffffff);\n box-shadow: var(--shadow-xl, 0 30px 80px rgba(0, 0, 0, 0.6));\n border-radius: var(--radius-lg, 0.5rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* ── Input row ──────────────────────────────────────────────────────── */\n\n.inputRow {\n position: relative;\n display: flex;\n align-items: center;\n background: var(--surface-interactive-default, transparent);\n}\n\n.inputRow :global([data-slot=\"command-input-wrapper\"]) {\n flex: 1;\n border-bottom: 0;\n padding: 0.875rem 1.25rem;\n gap: 0.625rem;\n background: transparent;\n}\n\n.input {\n font-size: var(--text-16, 1rem);\n color: var(--text-primary, #111827);\n background: transparent;\n}\n\n.input::placeholder {\n color: var(--text-tertiary, #9ca3af);\n}\n\n.scope {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n height: 1.375rem;\n padding: 0 0.5rem;\n border-radius: var(--radius-sm, 0.25rem);\n background: var(--surface-interactive-active, rgba(0, 0, 0, 0.04));\n font-size: var(--text-11, 0.6875rem);\n color: var(--text-primary, #111827);\n margin-right: 0.625rem;\n}\n\n.scopeLabel {\n color: var(--text-tertiary, #9ca3af);\n}\n\n.escKbd {\n margin-right: 1rem;\n}\n\n/* ── Results list ───────────────────────────────────────────────────── */\n\n.list {\n max-height: 27rem;\n padding: 0.375rem 0;\n background: var(--surface-card, #ffffff);\n}\n\n.empty {\n padding: 1.5rem;\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n text-align: center;\n}\n\n/* ── Group ──────────────────────────────────────────────────────────── */\n\n.group {\n padding: 0;\n}\n\n.group :global([cmdk-group-heading]) {\n padding: 0.375rem 1.25rem;\n font-size: var(--text-11, 0.6875rem);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n color: var(--text-tertiary, #9ca3af);\n}\n\n.groupHead {\n display: inline-flex;\n align-items: baseline;\n gap: 0.375rem;\n}\n\n.groupCount {\n color: var(--text-tertiary, #9ca3af);\n font-size: var(--text-11, 0.6875rem);\n}\n\n/* ── Item ───────────────────────────────────────────────────────────── */\n\n.item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n height: 2.25rem;\n padding: 0 1.25rem;\n border-radius: 0;\n font-size: var(--text-14, 0.875rem);\n color: var(--text-primary, #111827);\n}\n\n.item[data-selected=\"true\"] {\n background: var(--surface-selected, rgba(0, 0, 0, 0.04));\n color: var(--text-primary, #111827);\n}\n\n.item[data-selected=\"true\"] .itemIcon {\n color: var(--text-success, #10b981);\n}\n\n.itemIcon {\n flex: 0 0 auto;\n width: 1rem;\n height: 1rem;\n color: var(--text-tertiary, #9ca3af);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.itemLabel {\n flex: 0 0 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.itemMeta {\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n margin-left: 0.5rem;\n flex: 1 1 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n/* Hit-highlight inside labels — callers wrap matched substrings in\n * `<span data-hit>` and the rule paints them with the accent token. */\n.itemLabel :global([data-hit]) {\n color: var(--text-success, #10b981);\n}\n\n/* ── Footer ─────────────────────────────────────────────────────────── */\n\n.footer {\n display: flex;\n align-items: center;\n gap: 1rem;\n min-height: 2.25rem;\n padding: 0 1rem;\n background: var(--surface-interactive-default, transparent);\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n}\n\n.footerHints {\n display: inline-flex;\n align-items: center;\n gap: 1rem;\n}\n\n.hint {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.spacer {\n flex: 1;\n}\n\n.resultCount {\n color: var(--text-tertiary, #9ca3af);\n}\n"
4097
+ "content": "/* CommandDialog block — ⌘K palette composing Command + Dialog primitives.\n * Ports the r3 admin-ui palette visual contract onto Visor tokens; no new\n * tokens are introduced (everything resolves through the active theme).\n */\n\n/* ── Dialog content override ─────────────────────────────────────────── */\n\n/* DialogContent reset — transparent, no padding, no border. The Command\n * primitive paints its own surface. Tight max width matches r3 (640px). */\n.dialog {\n background: transparent;\n padding: 0;\n border: 0;\n width: 40rem;\n max-width: 90vw;\n box-shadow: none;\n}\n\n/* Hide DialogContent's built-in close X — palette closes via Esc only. */\n.dialog > button:last-child {\n display: none;\n}\n\n/* Visually-hidden DialogTitle for a11y (Radix requires one). */\n.srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n/* ── Command root ────────────────────────────────────────────────────── */\n\n/* Floating palette surface — opaque. `--surface-card` may be translucent glass\n * per theme (blackout: rgba(255,255,255,0.04)); the palette floats over the\n * dimmed backdrop with no opaque layer behind it, so a glass fill reads\n * see-through. `--surface-popover` is the opaque floating token (identical to\n * `--surface-card` on solid themes). `.list` below matches this surface. */\n.command {\n background: var(--surface-popover, var(--surface-page, #ffffff));\n box-shadow: var(--shadow-xl, 0 30px 80px rgba(0, 0, 0, 0.6));\n border-radius: var(--radius-lg, 0.5rem);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* ── Input row ──────────────────────────────────────────────────────── */\n\n.inputRow {\n position: relative;\n display: flex;\n align-items: center;\n background: var(--surface-interactive-default, transparent);\n}\n\n.inputRow :global([data-slot=\"command-input-wrapper\"]) {\n flex: 1;\n border-bottom: 0;\n padding: 0.875rem 1.25rem;\n gap: 0.625rem;\n background: transparent;\n}\n\n.input {\n font-size: var(--text-16, 1rem);\n color: var(--text-primary, #111827);\n background: transparent;\n}\n\n.input::placeholder {\n color: var(--text-tertiary, #9ca3af);\n}\n\n.scope {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n height: 1.375rem;\n padding: 0 0.5rem;\n border-radius: var(--radius-sm, 0.25rem);\n background: var(--surface-interactive-active, rgba(0, 0, 0, 0.04));\n font-size: var(--text-11, 0.6875rem);\n color: var(--text-primary, #111827);\n margin-right: 0.625rem;\n}\n\n.scopeLabel {\n color: var(--text-tertiary, #9ca3af);\n}\n\n.escKbd {\n margin-right: 1rem;\n}\n\n/* ── Results list ───────────────────────────────────────────────────── */\n\n.list {\n max-height: 27rem;\n padding: 0.375rem 0;\n /* Match `.command` — opaque floating surface, not glass `--surface-card`. */\n background: var(--surface-popover, var(--surface-page, #ffffff));\n}\n\n.empty {\n padding: 1.5rem;\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n text-align: center;\n}\n\n/* ── Group ──────────────────────────────────────────────────────────── */\n\n.group {\n padding: 0;\n}\n\n.group :global([cmdk-group-heading]) {\n padding: 0.375rem 1.25rem;\n font-size: var(--text-11, 0.6875rem);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.18em;\n color: var(--text-tertiary, #9ca3af);\n}\n\n.groupHead {\n display: inline-flex;\n align-items: baseline;\n gap: 0.375rem;\n}\n\n.groupCount {\n color: var(--text-tertiary, #9ca3af);\n font-size: var(--text-11, 0.6875rem);\n}\n\n/* ── Item ───────────────────────────────────────────────────────────── */\n\n.item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n height: 2.25rem;\n padding: 0 1.25rem;\n border-radius: 0;\n font-size: var(--text-14, 0.875rem);\n color: var(--text-primary, #111827);\n}\n\n.item[data-selected=\"true\"] {\n background: var(--surface-selected, rgba(0, 0, 0, 0.04));\n color: var(--text-primary, #111827);\n}\n\n.item[data-selected=\"true\"] .itemIcon {\n color: var(--text-success, #10b981);\n}\n\n.itemIcon {\n flex: 0 0 auto;\n width: 1rem;\n height: 1rem;\n color: var(--text-tertiary, #9ca3af);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.itemLabel {\n flex: 0 0 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.itemMeta {\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n margin-left: 0.5rem;\n flex: 1 1 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n/* Hit-highlight inside labels — callers wrap matched substrings in\n * `<span data-hit>` and the rule paints them with the accent token. */\n.itemLabel :global([data-hit]) {\n color: var(--text-success, #10b981);\n}\n\n/* ── Footer ─────────────────────────────────────────────────────────── */\n\n.footer {\n display: flex;\n align-items: center;\n gap: 1rem;\n min-height: 2.25rem;\n padding: 0 1rem;\n background: var(--surface-interactive-default, transparent);\n font-size: var(--text-13, 0.8125rem);\n color: var(--text-tertiary, #9ca3af);\n}\n\n.footerHints {\n display: inline-flex;\n align-items: center;\n gap: 1rem;\n}\n\n.hint {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.spacer {\n flex: 1;\n}\n\n.resultCount {\n color: var(--text-tertiary, #9ca3af);\n}\n"
4098
4098
  }
4099
4099
  ]
4100
4100
  },
@@ -4122,7 +4122,7 @@
4122
4122
  {
4123
4123
  "path": "blocks/dialog-form/dialog-form.module.css",
4124
4124
  "type": "registry:block",
4125
- "content": "/* DialogForm block — Animal dialog substrate shell (VI-620).\n *\n * Restyles the `dialog` atom's DialogContent into the compact admin modal\n * panel, then adds the header / title / body / footer chrome the 10 hand-rolled\n * Animal modals converge onto. Every value traces to a Visor token so the\n * active theme (Animal, or any other) swaps the whole shell — no bespoke CSS.\n */\n\n/* Compact panel — overrides the atom's default 32rem / spacing-6 content. The\n * max-width is the `width` axis (--widthSm/Md/Lg below); the hairline border is a\n * token seam (--dialog-form-panel-border) a theme or the `border=\"none\"` variant\n * can null out — AN-237 removed it (the hairline reads as chrome the design\n * doesn't want) without editing this copy. */\n.panel {\n border-radius: var(--radius-xl, 0.75rem);\n padding: var(--spacing-5, 1.25rem);\n background: var(--surface-card, #ffffff);\n border: var(\n --dialog-form-panel-border,\n var(--stroke-width-thin, 1px) solid\n var(--hairline, var(--border-default, #e5e7eb))\n );\n box-shadow: var(--shadow-lg);\n}\n\n/* Width axis — max-width only. Token-backed so a theme can retune each step;\n * `md` (default) preserves the VI-620 30rem panel. */\n.widthSm {\n max-width: var(--dialog-form-width-sm, 24rem);\n}\n\n.widthMd {\n max-width: var(--dialog-form-width-md, 30rem);\n}\n\n.widthLg {\n max-width: var(--dialog-form-width-lg, 40rem);\n}\n\n/* Borderless variant — nulls the panel-border seam (border=\"none\"). Consumers\n * can equivalently set --dialog-form-panel-border: none in a theme. */\n.panelBorderless {\n --dialog-form-panel-border: none;\n}\n\n/* Header — title + optional description, tight stack above the body. */\n.header {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Title — dlg-title. Bold, primary ink. Size axis reconciles the two AN\n * sources: the 13.5px surface scale (default `sm`) and reuse.html's 15px\n * shared/confirm dialogs (`md`). Normalized onto the --text-N chrome scale. */\n.title {\n font-weight: var(--font-weight-bold, 700);\n color: var(--text-primary, #111827);\n margin: 0;\n line-height: 1.3;\n}\n\n.titleSm {\n font-size: var(--text-13, 0.8125rem);\n}\n\n.titleMd {\n font-size: var(--text-16, 1rem);\n}\n\n/* Body — vertical stack of DialogField blocks. */\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4, 1rem);\n}\n\n/* Footer — right-aligned dlg-btn action row. */\n.footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n margin-top: var(--spacing-5, 1.25rem);\n}\n"
4125
+ "content": "/* DialogForm block — Animal dialog substrate shell (VI-620).\n *\n * Restyles the `dialog` atom's DialogContent into the compact admin modal\n * panel, then adds the header / title / body / footer chrome the 10 hand-rolled\n * Animal modals converge onto. Every value traces to a Visor token so the\n * active theme (Animal, or any other) swaps the whole shell — no bespoke CSS.\n */\n\n/* Compact panel — overrides the atom's default 32rem / spacing-6 content. The\n * max-width is the `width` axis (--widthSm/Md/Lg below); the hairline border is a\n * token seam (--dialog-form-panel-border) a theme or the `border=\"none\"` variant\n * can null out — AN-237 removed it (the hairline reads as chrome the design\n * doesn't want) without editing this copy. */\n.panel {\n border-radius: var(--radius-xl, 0.75rem);\n padding: var(--spacing-5, 1.25rem);\n /* Floating panel — MUST be an opaque surface. `--surface-card` is a card-in-\n * flow token a theme may make translucent glass (blackout: rgba(255,255,255,\n * 0.04)); over the dimmed backdrop it has no opaque layer behind it and reads\n * see-through. `--surface-popover` is the opaque floating-panel token and is\n * identical to `--surface-card` on solid themes, so only glass themes change. */\n background: var(--surface-popover, var(--surface-page, #ffffff));\n border: var(\n --dialog-form-panel-border,\n var(--stroke-width-thin, 1px) solid\n var(--hairline, var(--border-default, #e5e7eb))\n );\n box-shadow: var(--shadow-lg);\n}\n\n/* Width axis — max-width only. Token-backed so a theme can retune each step;\n * `md` (default) preserves the VI-620 30rem panel. */\n.widthSm {\n max-width: var(--dialog-form-width-sm, 24rem);\n}\n\n.widthMd {\n max-width: var(--dialog-form-width-md, 30rem);\n}\n\n.widthLg {\n max-width: var(--dialog-form-width-lg, 40rem);\n}\n\n/* Borderless variant — nulls the panel-border seam (border=\"none\"). Consumers\n * can equivalently set --dialog-form-panel-border: none in a theme. */\n.panelBorderless {\n --dialog-form-panel-border: none;\n}\n\n/* Header — title + optional description, tight stack above the body. */\n.header {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-1, 0.25rem);\n margin-bottom: var(--spacing-4, 1rem);\n}\n\n/* Title — dlg-title. Bold, primary ink. Size axis reconciles the two AN\n * sources: the 13.5px surface scale (default `sm`) and reuse.html's 15px\n * shared/confirm dialogs (`md`). Normalized onto the --text-N chrome scale. */\n.title {\n font-weight: var(--font-weight-bold, 700);\n color: var(--text-primary, #111827);\n margin: 0;\n line-height: 1.3;\n}\n\n.titleSm {\n font-size: var(--text-13, 0.8125rem);\n}\n\n.titleMd {\n font-size: var(--text-16, 1rem);\n}\n\n/* Body — vertical stack of DialogField blocks. */\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4, 1rem);\n}\n\n/* Footer — right-aligned dlg-btn action row. */\n.footer {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: var(--spacing-2, 0.5rem);\n margin-top: var(--spacing-5, 1.25rem);\n}\n"
4126
4126
  }
4127
4127
  ]
4128
4128
  },
@@ -5168,7 +5168,7 @@
5168
5168
  {
5169
5169
  "path": "components/devtools/source-inspector/visor-component-names.generated.ts",
5170
5170
  "type": "registry:devtool",
5171
- "content": "// THIS FILE IS GENERATED BY scripts/generate-visor-component-names.ts.\n// Do not edit by hand. Re-run `npm run generate:component-names` after\n// adding, removing, or renaming a Visor component.\n//\n// Source of truth: registry/registry-{ui,blocks,deck,visual,devtools}.ts\n// Used by: components/devtools/source-inspector/* (VI-311)\n\nexport const VISOR_COMPONENT_NAMES: ReadonlySet<string> = new Set([\n \"AccessibilitySection\",\n \"AccessibilitySlide\",\n \"AccessibilitySpecimen\",\n \"Accordion\",\n \"AccordionContent\",\n \"AccordionItem\",\n \"AccordionTrigger\",\n \"ActivityFeed\",\n \"ActivityFeedContext\",\n \"ActivityFeedItem\",\n \"ActivityFeedRoot\",\n \"AdminDashboard\",\n \"AdminDetail\",\n \"AdminDetailDrawer\",\n \"AdminListPage\",\n \"AdminListPageInner\",\n \"AdminSettingsPage\",\n \"AdminShell\",\n \"AdminTabbedEditor\",\n \"AdminWizard\",\n \"Alert\",\n \"AlertActions\",\n \"AlertDescription\",\n \"AlertTitle\",\n \"AmbientGlow\",\n \"Avatar\",\n \"AvatarFallback\",\n \"AvatarImage\",\n \"AvatarStack\",\n \"Badge\",\n \"Banner\",\n \"BannerAction\",\n \"BannerDescription\",\n \"BannerTitle\",\n \"BentoGrid\",\n \"BentoTile\",\n \"BentoTileBody\",\n \"BentoTileDescription\",\n \"BentoTileFigure\",\n \"BentoTileHeadline\",\n \"BentoTileMedia\",\n \"BentoTileMeta\",\n \"BentoTileTitle\",\n \"Box\",\n \"Breadcrumb\",\n \"BreadcrumbEllipsis\",\n \"BreadcrumbItem\",\n \"BreadcrumbLink\",\n \"BreadcrumbList\",\n \"BreadcrumbPage\",\n \"BreadcrumbSeparator\",\n \"BrowserFrame\",\n \"BulkActionBar\",\n \"Button\",\n \"ButtonSpecimenSection\",\n \"ButtonSpecimenSlide\",\n \"Calendar\",\n \"Capture\",\n \"Card\",\n \"CardContent\",\n \"CardDescription\",\n \"CardFooter\",\n \"CardGrid\",\n \"CardHeader\",\n \"CardLift\",\n \"CardTitle\",\n \"Carousel\",\n \"CarouselContent\",\n \"CarouselContext\",\n \"CarouselGallery\",\n \"CarouselItem\",\n \"CarouselNext\",\n \"CarouselPrevious\",\n \"ChallengeCard\",\n \"ChallengeCardAction\",\n \"ChallengeCardActions\",\n \"ChallengeCardBody\",\n \"ChallengeCardGate\",\n \"ChallengeCardHeader\",\n \"ChartContainer\",\n \"ChartContext\",\n \"ChartLegend\",\n \"ChartLegendContent\",\n \"ChartStyle\",\n \"ChartTooltip\",\n \"ChartTooltipContent\",\n \"Checkbox\",\n \"CheckGroup\",\n \"CheckRow\",\n \"Chip\",\n \"ChipGroup\",\n \"ChipGroupContext\",\n \"ChipGroupItem\",\n \"ChoiceChip\",\n \"ChromeButton\",\n \"ClosingSlide\",\n \"CodeBlock\",\n \"Collapsible\",\n \"CollapsibleContent\",\n \"CollapsibleTrigger\",\n \"ColorBar\",\n \"ColorPaletteSection\",\n \"ColorPicker\",\n \"ColorPickerSurface\",\n \"ColorSwatch\",\n \"ColorSwatchGrid\",\n \"Combobox\",\n \"ComboboxContent\",\n \"ComboboxContext\",\n \"ComboboxEmpty\",\n \"ComboboxGroup\",\n \"ComboboxInput\",\n \"ComboboxItem\",\n \"ComboboxSeparator\",\n \"Command\",\n \"CommandDialog\",\n \"CommandEmpty\",\n \"CommandGroup\",\n \"CommandInput\",\n \"CommandItem\",\n \"CommandList\",\n \"CommandLoading\",\n \"CommandSeparator\",\n \"CommandShortcut\",\n \"ComponentShowcaseContent\",\n \"ComponentShowcaseSection\",\n \"ComponentShowcaseSlide\",\n \"Composer\",\n \"ComposerContext\",\n \"ComposerField\",\n \"ComposerSend\",\n \"ComposerSpacer\",\n \"ComposerToolbar\",\n \"ComposerToolButton\",\n \"ConceptSlide\",\n \"ConfigurationPanel\",\n \"ConfirmDialog\",\n \"ConflictBanner\",\n \"Container\",\n \"ContextMenu\",\n \"ContextMenuCheckboxItem\",\n \"ContextMenuContent\",\n \"ContextMenuGroup\",\n \"ContextMenuItem\",\n \"ContextMenuLabel\",\n \"ContextMenuPortal\",\n \"ContextMenuRadioGroup\",\n \"ContextMenuRadioItem\",\n \"ContextMenuSeparator\",\n \"ContextMenuShortcut\",\n \"ContextMenuSub\",\n \"ContextMenuSubContent\",\n \"ContextMenuSubTrigger\",\n \"ContextMenuTrigger\",\n \"Controls\",\n \"CtaSection\",\n \"DataTable\",\n \"DataTableInner\",\n \"DatePicker\",\n \"DateRangePicker\",\n \"DeckContext\",\n \"DeckFooter\",\n \"DeckLayout\",\n \"DeckProvider\",\n \"DeckRenderer\",\n \"DesignSystemDeck\",\n \"DesignSystemSpecimen\",\n \"Dialog\",\n \"DialogClose\",\n \"DialogContent\",\n \"DialogDescription\",\n \"DialogFooter\",\n \"DialogHeader\",\n \"DialogOverlay\",\n \"DialogPortal\",\n \"DialogTitle\",\n \"DialogTrigger\",\n \"DocFrame\",\n \"DocFrameBrand\",\n \"DocNav\",\n \"DocNavGroup\",\n \"DocNavPill\",\n \"DotNav\",\n \"DropdownMenu\",\n \"DropdownMenuCheckboxItem\",\n \"DropdownMenuContent\",\n \"DropdownMenuGroup\",\n \"DropdownMenuItem\",\n \"DropdownMenuLabel\",\n \"DropdownMenuPortal\",\n \"DropdownMenuRadioGroup\",\n \"DropdownMenuRadioItem\",\n \"DropdownMenuSeparator\",\n \"DropdownMenuShortcut\",\n \"DropdownMenuSub\",\n \"DropdownMenuSubContent\",\n \"DropdownMenuSubTrigger\",\n \"DropdownMenuTrigger\",\n \"EditableBlock\",\n \"ElevationCard\",\n \"ElevationSlide\",\n \"EmptyState\",\n \"ErrorPlacard\",\n \"ExportMenu\",\n \"FeaturesGrid\",\n \"FidelityMirror\",\n \"Field\",\n \"FieldDescription\",\n \"FieldError\",\n \"FieldLabel\",\n \"Fieldset\",\n \"FieldsetLegend\",\n \"FileUpload\",\n \"FilterBar\",\n \"FilterChip\",\n \"FontShowcase\",\n \"FontShowcaseGrid\",\n \"Footer\",\n \"FooterSection\",\n \"Form\",\n \"FormError\",\n \"FormErrorDescription\",\n \"FormErrorTitle\",\n \"FormField\",\n \"FormSpecimenSection\",\n \"FormSpecimenSlide\",\n \"FullscreenOverlay\",\n \"FullscreenOverlayContent\",\n \"FullscreenOverlayTrigger\",\n \"GrainOverlay\",\n \"Grid\",\n \"Header\",\n \"Heading\",\n \"HeroGlow\",\n \"HeroSection\",\n \"HeroSlide\",\n \"HoverCard\",\n \"HoverCardContent\",\n \"HoverCardTrigger\",\n \"IconGrid\",\n \"IconGridSection\",\n \"IconSizeRow\",\n \"IconsSlide\",\n \"Image\",\n \"InfographicBar\",\n \"Inline\",\n \"Input\",\n \"Kbd\",\n \"KeyValueList\",\n \"Label\",\n \"Landing\",\n \"Lightbox\",\n \"LightboxContent\",\n \"LightboxContext\",\n \"LightboxTrigger\",\n \"LoginForm\",\n \"Marquee\",\n \"MarqueeBandRenderer\",\n \"MatrixCell\",\n \"MatrixTable\",\n \"MatrixTableInner\",\n \"Menubar\",\n \"MenubarCheckboxItem\",\n \"MenubarContent\",\n \"MenubarGroup\",\n \"MenubarItem\",\n \"MenubarLabel\",\n \"MenubarMenu\",\n \"MenubarRadioGroup\",\n \"MenubarRadioItem\",\n \"MenubarSeparator\",\n \"MenubarShortcut\",\n \"MenubarSub\",\n \"MenubarSubContent\",\n \"MenubarSubTrigger\",\n \"MenubarTrigger\",\n \"MonthCalendar\",\n \"MotionDuration\",\n \"MotionDurationSection\",\n \"MotionEasing\",\n \"MotionEasingSection\",\n \"MotionSlide\",\n \"NameRoster\",\n \"NameRosterItem\",\n \"Navbar\",\n \"NavbarBrand\",\n \"NavbarContent\",\n \"NavbarItem\",\n \"NavbarLink\",\n \"NavbarToggle\",\n \"NumberInput\",\n \"OfflineBanner\",\n \"OpacityBar\",\n \"OpacitySlide\",\n \"OTPInput\",\n \"PageHeader\",\n \"Pagination\",\n \"PaginationContent\",\n \"PaginationEllipsis\",\n \"PaginationItem\",\n \"PaginationLink\",\n \"PaginationNext\",\n \"PaginationPrevious\",\n \"PasswordInput\",\n \"PhoneInput\",\n \"PlatformChip\",\n \"Popover\",\n \"PopoverAnchor\",\n \"PopoverContent\",\n \"PopoverFooter\",\n \"PopoverSelectionItem\",\n \"PopoverSelectionLabel\",\n \"PopoverSelectionList\",\n \"PopoverTrigger\",\n \"PricingSection\",\n \"ProfileMenu\",\n \"Progress\",\n \"PrototypeReview\",\n \"QuickActions\",\n \"RadioGroup\",\n \"RadioGroupItem\",\n \"RadiusScale\",\n \"RadiusSection\",\n \"RadiusSlide\",\n \"RecordSection\",\n \"RightRailList\",\n \"ScoreIndicator\",\n \"ScrollArea\",\n \"ScrollBar\",\n \"SearchInput\",\n \"SectionHeader\",\n \"SectionIntro\",\n \"SectionNav\",\n \"SectionNavItem\",\n \"SegmentedProgress\",\n \"Select\",\n \"SelectContent\",\n \"SelectGroup\",\n \"SelectionListContext\",\n \"SelectItem\",\n \"SelectLabel\",\n \"SelectScrollDownButton\",\n \"SelectScrollUpButton\",\n \"SelectSeparator\",\n \"SelectTrigger\",\n \"SelectValue\",\n \"SemanticColorGrid\",\n \"SemanticColorItem\",\n \"SemanticTokensSlide\",\n \"SensitivePanel\",\n \"Separator\",\n \"SessionTimeout\",\n \"ShadowSection\",\n \"Sheet\",\n \"SheetClose\",\n \"SheetContent\",\n \"SheetDescription\",\n \"SheetFooter\",\n \"SheetHeader\",\n \"SheetOverlay\",\n \"SheetPortal\",\n \"SheetTitle\",\n \"SheetTrigger\",\n \"Sidebar\",\n \"SidebarContent\",\n \"SidebarContext\",\n \"SidebarFooter\",\n \"SidebarGroup\",\n \"SidebarGroupAction\",\n \"SidebarGroupContent\",\n \"SidebarGroupLabel\",\n \"SidebarHeader\",\n \"SidebarInset\",\n \"SidebarMenu\",\n \"SidebarMenuAction\",\n \"SidebarMenuBadge\",\n \"SidebarMenuButton\",\n \"SidebarMenuItem\",\n \"SidebarMenuSub\",\n \"SidebarMenuSubButton\",\n \"SidebarMenuSubItem\",\n \"SidebarProvider\",\n \"SidebarRail\",\n \"SidebarSeparator\",\n \"SidebarTrigger\",\n \"Skeleton\",\n \"SkeletonDetail\",\n \"SkeletonList\",\n \"SkeletonTable\",\n \"Slide\",\n \"SlideHeader\",\n \"Slider\",\n \"SliderControl\",\n \"SlideThemeContext\",\n \"SlideThemeProvider\",\n \"SlowNetworkBar\",\n \"SourceInspector\",\n \"SourceInspectorContext\",\n \"SourceInspectorDevImpl\",\n \"SourceInspectorProvider\",\n \"SourceInspectorRunner\",\n \"SourceInspectorToggle\",\n \"SpacingScale\",\n \"SpacingSection\",\n \"SpacingSlide\",\n \"Sparkline\",\n \"SpecimenCard\",\n \"SpecimenCardFooter\",\n \"Sphere\",\n \"SpherePlayground\",\n \"Spinner\",\n \"Stack\",\n \"Stage\",\n \"StatCard\",\n \"StatHero\",\n \"StationSpectrum\",\n \"StatusBadge\",\n \"StatusColorsSlide\",\n \"StatusDot\",\n \"Stepper\",\n \"StepperContext\",\n \"StepperDescription\",\n \"StepperItem\",\n \"StepperSeparator\",\n \"StepperTitle\",\n \"StepperTrigger\",\n \"StepsSection\",\n \"StructuredPrompt\",\n \"StructuredPromptBody\",\n \"StructuredPromptHeader\",\n \"StructuredPromptHint\",\n \"StructuredPromptSlot\",\n \"SuccessLiveRegion\",\n \"SurfaceRow\",\n \"SurfaceScaleStack\",\n \"SurfaceSection\",\n \"Switch\",\n \"Table\",\n \"TableBody\",\n \"TableCaption\",\n \"TableCell\",\n \"TableFooter\",\n \"TableHead\",\n \"TableHeader\",\n \"TableRow\",\n \"Tabs\",\n \"TabsContent\",\n \"TabsList\",\n \"TabsTrigger\",\n \"TagInput\",\n \"TestimonialAttribution\",\n \"TestimonialSection\",\n \"Text\",\n \"Textarea\",\n \"ThemeArchitectureSlide\",\n \"ThemeColorsSlide\",\n \"ThemeSwitcher\",\n \"Timeline\",\n \"TimelineContent\",\n \"TimelineDescription\",\n \"TimelineIcon\",\n \"TimelineItem\",\n \"TimelineTimestamp\",\n \"TimelineTitle\",\n \"TitleSlide\",\n \"ToastCard\",\n \"ToastCardStack\",\n \"Toaster\",\n \"TOCSlide\",\n \"ToggleButton\",\n \"ToggleDevImpl\",\n \"ToggleGroup\",\n \"ToggleGroupContext\",\n \"ToggleGroupItem\",\n \"Tooltip\",\n \"TooltipContent\",\n \"TooltipProvider\",\n \"TooltipTrigger\",\n \"TypeBodySlide\",\n \"TypeDisplaySlide\",\n \"TypeScaleStack\",\n \"TypeSpecimen\",\n \"TypographySection\",\n \"Vignette\",\n \"WorkspaceSwitcher\",\n])\n"
5171
+ "content": "// THIS FILE IS GENERATED BY scripts/generate-visor-component-names.ts.\n// Do not edit by hand. Re-run `npm run generate:component-names` after\n// adding, removing, or renaming a Visor component.\n//\n// Source of truth: registry/registry-{ui,blocks,deck,visual,devtools}.ts\n// Used by: components/devtools/source-inspector/* (VI-311)\n\nexport const VISOR_COMPONENT_NAMES: ReadonlySet<string> = new Set([\n \"AccessibilitySection\",\n \"AccessibilitySlide\",\n \"AccessibilitySpecimen\",\n \"Accordion\",\n \"AccordionContent\",\n \"AccordionItem\",\n \"AccordionTrigger\",\n \"ActivityFeed\",\n \"ActivityFeedContext\",\n \"ActivityFeedItem\",\n \"ActivityFeedRoot\",\n \"AdminDashboard\",\n \"AdminDetail\",\n \"AdminDetailDrawer\",\n \"AdminListPage\",\n \"AdminListPageInner\",\n \"AdminSettingsPage\",\n \"AdminShell\",\n \"AdminTabbedEditor\",\n \"AdminWizard\",\n \"Alert\",\n \"AlertActions\",\n \"AlertDescription\",\n \"AlertTitle\",\n \"AmbientGlow\",\n \"Avatar\",\n \"AvatarFallback\",\n \"AvatarImage\",\n \"AvatarStack\",\n \"Badge\",\n \"Banner\",\n \"BannerAction\",\n \"BannerDescription\",\n \"BannerTitle\",\n \"BentoGrid\",\n \"BentoTile\",\n \"BentoTileBody\",\n \"BentoTileDescription\",\n \"BentoTileFigure\",\n \"BentoTileHeadline\",\n \"BentoTileMedia\",\n \"BentoTileMeta\",\n \"BentoTileTitle\",\n \"Box\",\n \"Breadcrumb\",\n \"BreadcrumbEllipsis\",\n \"BreadcrumbItem\",\n \"BreadcrumbLink\",\n \"BreadcrumbList\",\n \"BreadcrumbPage\",\n \"BreadcrumbSeparator\",\n \"BrowserFrame\",\n \"BulkActionBar\",\n \"Button\",\n \"ButtonSpecimenSection\",\n \"ButtonSpecimenSlide\",\n \"Calendar\",\n \"Capture\",\n \"Card\",\n \"CardContent\",\n \"CardDescription\",\n \"CardFooter\",\n \"CardGrid\",\n \"CardHeader\",\n \"CardLift\",\n \"CardTitle\",\n \"Carousel\",\n \"CarouselContent\",\n \"CarouselContext\",\n \"CarouselGallery\",\n \"CarouselItem\",\n \"CarouselNext\",\n \"CarouselPrevious\",\n \"ChallengeCard\",\n \"ChallengeCardAction\",\n \"ChallengeCardActions\",\n \"ChallengeCardBody\",\n \"ChallengeCardGate\",\n \"ChallengeCardHeader\",\n \"ChartContainer\",\n \"ChartContext\",\n \"ChartLegend\",\n \"ChartLegendContent\",\n \"ChartStyle\",\n \"ChartTooltip\",\n \"ChartTooltipContent\",\n \"Checkbox\",\n \"CheckGroup\",\n \"CheckRow\",\n \"Chip\",\n \"ChipGroup\",\n \"ChipGroupContext\",\n \"ChipGroupItem\",\n \"ChoiceChip\",\n \"ChromeButton\",\n \"ClosingSlide\",\n \"CodeBlock\",\n \"Collapsible\",\n \"CollapsibleContent\",\n \"CollapsibleTrigger\",\n \"ColorBar\",\n \"ColorPaletteSection\",\n \"ColorPicker\",\n \"ColorPickerSurface\",\n \"ColorSwatch\",\n \"ColorSwatchGrid\",\n \"Combobox\",\n \"ComboboxContent\",\n \"ComboboxContext\",\n \"ComboboxEmpty\",\n \"ComboboxGroup\",\n \"ComboboxInput\",\n \"ComboboxItem\",\n \"ComboboxSeparator\",\n \"Command\",\n \"CommandDialog\",\n \"CommandEmpty\",\n \"CommandGroup\",\n \"CommandInput\",\n \"CommandItem\",\n \"CommandList\",\n \"CommandLoading\",\n \"CommandSeparator\",\n \"CommandShortcut\",\n \"ComponentShowcaseContent\",\n \"ComponentShowcaseSection\",\n \"ComponentShowcaseSlide\",\n \"Composer\",\n \"ComposerContext\",\n \"ComposerField\",\n \"ComposerSend\",\n \"ComposerSpacer\",\n \"ComposerToolbar\",\n \"ComposerToolButton\",\n \"ConceptSlide\",\n \"ConfigurationPanel\",\n \"ConfirmDialog\",\n \"ConflictBanner\",\n \"Container\",\n \"ContextMenu\",\n \"ContextMenuCheckboxItem\",\n \"ContextMenuContent\",\n \"ContextMenuGroup\",\n \"ContextMenuItem\",\n \"ContextMenuLabel\",\n \"ContextMenuPortal\",\n \"ContextMenuRadioGroup\",\n \"ContextMenuRadioItem\",\n \"ContextMenuSeparator\",\n \"ContextMenuShortcut\",\n \"ContextMenuSub\",\n \"ContextMenuSubContent\",\n \"ContextMenuSubTrigger\",\n \"ContextMenuTrigger\",\n \"Controls\",\n \"CtaSection\",\n \"DataTable\",\n \"DataTableInner\",\n \"DatePicker\",\n \"DateRangePicker\",\n \"DeckContext\",\n \"DeckFooter\",\n \"DeckLayout\",\n \"DeckProvider\",\n \"DeckRenderer\",\n \"DesignSystemDeck\",\n \"DesignSystemSpecimen\",\n \"Dialog\",\n \"DialogClose\",\n \"DialogContent\",\n \"DialogDescription\",\n \"DialogField\",\n \"DialogFieldControl\",\n \"DialogFieldLabel\",\n \"DialogFooter\",\n \"DialogForm\",\n \"DialogFormBody\",\n \"DialogFormClose\",\n \"DialogFormContent\",\n \"DialogFormDescription\",\n \"DialogFormFooter\",\n \"DialogFormHeader\",\n \"DialogFormTitle\",\n \"DialogFormTrigger\",\n \"DialogHeader\",\n \"DialogOverlay\",\n \"DialogPortal\",\n \"DialogTitle\",\n \"DialogTrigger\",\n \"DocFrame\",\n \"DocFrameBrand\",\n \"DocNav\",\n \"DocNavGroup\",\n \"DocNavPill\",\n \"DotNav\",\n \"DropdownMenu\",\n \"DropdownMenuCheckboxItem\",\n \"DropdownMenuContent\",\n \"DropdownMenuGroup\",\n \"DropdownMenuItem\",\n \"DropdownMenuLabel\",\n \"DropdownMenuPortal\",\n \"DropdownMenuRadioGroup\",\n \"DropdownMenuRadioItem\",\n \"DropdownMenuSeparator\",\n \"DropdownMenuShortcut\",\n \"DropdownMenuSub\",\n \"DropdownMenuSubContent\",\n \"DropdownMenuSubTrigger\",\n \"DropdownMenuTrigger\",\n \"EditableBlock\",\n \"ElevationCard\",\n \"ElevationSlide\",\n \"EmptyState\",\n \"ErrorPlacard\",\n \"ExportMenu\",\n \"FeaturesGrid\",\n \"FidelityMirror\",\n \"Field\",\n \"FieldDescription\",\n \"FieldError\",\n \"FieldLabel\",\n \"Fieldset\",\n \"FieldsetLegend\",\n \"FileUpload\",\n \"FilterBar\",\n \"FilterChip\",\n \"FontShowcase\",\n \"FontShowcaseGrid\",\n \"Footer\",\n \"FooterSection\",\n \"Form\",\n \"FormError\",\n \"FormErrorDescription\",\n \"FormErrorTitle\",\n \"FormField\",\n \"FormSpecimenSection\",\n \"FormSpecimenSlide\",\n \"FullscreenOverlay\",\n \"FullscreenOverlayContent\",\n \"FullscreenOverlayTrigger\",\n \"GrainOverlay\",\n \"Grid\",\n \"Header\",\n \"Heading\",\n \"HeroGlow\",\n \"HeroSection\",\n \"HeroSlide\",\n \"HoverCard\",\n \"HoverCardContent\",\n \"HoverCardTrigger\",\n \"IconGrid\",\n \"IconGridSection\",\n \"IconSizeRow\",\n \"IconsSlide\",\n \"Image\",\n \"InfographicBar\",\n \"Inline\",\n \"Input\",\n \"Kbd\",\n \"KeyValueList\",\n \"Label\",\n \"Landing\",\n \"Lightbox\",\n \"LightboxContent\",\n \"LightboxContext\",\n \"LightboxTrigger\",\n \"LoginForm\",\n \"Marquee\",\n \"MarqueeBandRenderer\",\n \"MatrixCell\",\n \"MatrixTable\",\n \"MatrixTableInner\",\n \"Menubar\",\n \"MenubarCheckboxItem\",\n \"MenubarContent\",\n \"MenubarGroup\",\n \"MenubarItem\",\n \"MenubarLabel\",\n \"MenubarMenu\",\n \"MenubarRadioGroup\",\n \"MenubarRadioItem\",\n \"MenubarSeparator\",\n \"MenubarShortcut\",\n \"MenubarSub\",\n \"MenubarSubContent\",\n \"MenubarSubTrigger\",\n \"MenubarTrigger\",\n \"MonthCalendar\",\n \"MotionDuration\",\n \"MotionDurationSection\",\n \"MotionEasing\",\n \"MotionEasingSection\",\n \"MotionSlide\",\n \"NameRoster\",\n \"NameRosterItem\",\n \"Navbar\",\n \"NavbarBrand\",\n \"NavbarContent\",\n \"NavbarItem\",\n \"NavbarLink\",\n \"NavbarToggle\",\n \"NumberInput\",\n \"OfflineBanner\",\n \"OpacityBar\",\n \"OpacitySlide\",\n \"OTPInput\",\n \"PageHeader\",\n \"Pagination\",\n \"PaginationContent\",\n \"PaginationEllipsis\",\n \"PaginationItem\",\n \"PaginationLink\",\n \"PaginationNext\",\n \"PaginationPrevious\",\n \"PasswordInput\",\n \"PhoneInput\",\n \"PlatformChip\",\n \"Popover\",\n \"PopoverAnchor\",\n \"PopoverContent\",\n \"PopoverFooter\",\n \"PopoverSelectionItem\",\n \"PopoverSelectionLabel\",\n \"PopoverSelectionList\",\n \"PopoverTrigger\",\n \"PricingSection\",\n \"ProfileMenu\",\n \"Progress\",\n \"PrototypeReview\",\n \"QuickActions\",\n \"RadioGroup\",\n \"RadioGroupItem\",\n \"RadiusScale\",\n \"RadiusSection\",\n \"RadiusSlide\",\n \"RecordSection\",\n \"RightRailList\",\n \"ScoreIndicator\",\n \"ScrollArea\",\n \"ScrollBar\",\n \"SearchInput\",\n \"SectionHeader\",\n \"SectionIntro\",\n \"SectionNav\",\n \"SectionNavItem\",\n \"SegmentedProgress\",\n \"Select\",\n \"SelectContent\",\n \"SelectGroup\",\n \"SelectionListContext\",\n \"SelectItem\",\n \"SelectLabel\",\n \"SelectScrollDownButton\",\n \"SelectScrollUpButton\",\n \"SelectSeparator\",\n \"SelectTrigger\",\n \"SelectValue\",\n \"SemanticColorGrid\",\n \"SemanticColorItem\",\n \"SemanticTokensSlide\",\n \"SensitivePanel\",\n \"Separator\",\n \"SessionTimeout\",\n \"ShadowSection\",\n \"Sheet\",\n \"SheetClose\",\n \"SheetContent\",\n \"SheetDescription\",\n \"SheetFooter\",\n \"SheetHeader\",\n \"SheetOverlay\",\n \"SheetPortal\",\n \"SheetTitle\",\n \"SheetTrigger\",\n \"Sidebar\",\n \"SidebarContent\",\n \"SidebarContext\",\n \"SidebarFooter\",\n \"SidebarGroup\",\n \"SidebarGroupAction\",\n \"SidebarGroupContent\",\n \"SidebarGroupLabel\",\n \"SidebarHeader\",\n \"SidebarInset\",\n \"SidebarMenu\",\n \"SidebarMenuAction\",\n \"SidebarMenuBadge\",\n \"SidebarMenuButton\",\n \"SidebarMenuItem\",\n \"SidebarMenuSub\",\n \"SidebarMenuSubButton\",\n \"SidebarMenuSubItem\",\n \"SidebarProvider\",\n \"SidebarRail\",\n \"SidebarSeparator\",\n \"SidebarTrigger\",\n \"Skeleton\",\n \"SkeletonDetail\",\n \"SkeletonList\",\n \"SkeletonTable\",\n \"Slide\",\n \"SlideHeader\",\n \"Slider\",\n \"SliderControl\",\n \"SlideThemeContext\",\n \"SlideThemeProvider\",\n \"SlowNetworkBar\",\n \"SourceInspector\",\n \"SourceInspectorContext\",\n \"SourceInspectorDevImpl\",\n \"SourceInspectorProvider\",\n \"SourceInspectorRunner\",\n \"SourceInspectorToggle\",\n \"SpacingScale\",\n \"SpacingSection\",\n \"SpacingSlide\",\n \"Sparkline\",\n \"SpecimenCard\",\n \"SpecimenCardFooter\",\n \"Sphere\",\n \"SpherePlayground\",\n \"Spinner\",\n \"Stack\",\n \"Stage\",\n \"StatCard\",\n \"StatHero\",\n \"StationSpectrum\",\n \"StatusBadge\",\n \"StatusColorsSlide\",\n \"StatusDot\",\n \"Stepper\",\n \"StepperContext\",\n \"StepperDescription\",\n \"StepperItem\",\n \"StepperSeparator\",\n \"StepperTitle\",\n \"StepperTrigger\",\n \"StepsSection\",\n \"StructuredPrompt\",\n \"StructuredPromptBody\",\n \"StructuredPromptHeader\",\n \"StructuredPromptHint\",\n \"StructuredPromptSlot\",\n \"SuccessLiveRegion\",\n \"SurfaceRow\",\n \"SurfaceScaleStack\",\n \"SurfaceSection\",\n \"Switch\",\n \"Table\",\n \"TableBody\",\n \"TableCaption\",\n \"TableCell\",\n \"TableFooter\",\n \"TableHead\",\n \"TableHeader\",\n \"TableRow\",\n \"Tabs\",\n \"TabsContent\",\n \"TabsList\",\n \"TabsTrigger\",\n \"TagInput\",\n \"TestimonialAttribution\",\n \"TestimonialSection\",\n \"Text\",\n \"Textarea\",\n \"ThemeArchitectureSlide\",\n \"ThemeColorsSlide\",\n \"ThemeSwitcher\",\n \"Timeline\",\n \"TimelineContent\",\n \"TimelineDescription\",\n \"TimelineIcon\",\n \"TimelineItem\",\n \"TimelineTimestamp\",\n \"TimelineTitle\",\n \"TitleSlide\",\n \"ToastCard\",\n \"ToastCardStack\",\n \"Toaster\",\n \"TOCSlide\",\n \"ToggleButton\",\n \"ToggleDevImpl\",\n \"ToggleGroup\",\n \"ToggleGroupContext\",\n \"ToggleGroupItem\",\n \"Tooltip\",\n \"TooltipContent\",\n \"TooltipProvider\",\n \"TooltipTrigger\",\n \"TypeBodySlide\",\n \"TypeDisplaySlide\",\n \"TypeScaleStack\",\n \"TypeSpecimen\",\n \"TypographySection\",\n \"Vignette\",\n \"WorkspaceSwitcher\",\n])\n"
5172
5172
  }
5173
5173
  ]
5174
5174
  },