{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "base",
  "title": "Base",
  "description": "Tokens, color scales, theme, fonts, reset and focus ring every component builds on.",
  "dependencies": [
    "@stylexjs/stylex",
    "@radix-ui/colors"
  ],
  "devDependencies": [
    "@stylexjs/unplugin"
  ],
  "files": [
    {
      "path": "src/lib/constella/tokens.stylex.ts",
      "content": "// The theme's variables, which base.css reads by name; nothing imports them by value.\nimport \"./theme.stylex\";\nimport * as stylex from \"@stylexjs/stylex\";\n\n// A group `Sx` reads stays `as const`: without it the values widen to `string` and `sx` takes anything.\n// Grouped by family; each family's tokens share its slots, so `<family><Slot>` names one.\nexport const colors = stylex.defineVars({\n  // Surfaces.\n  background: \"var(--background)\",\n  overlay: \"var(--overlay)\",\n  // Floating surfaces: popovers, menus, dialogs, toasts.\n  raised: \"var(--raised)\",\n  sidebar: \"var(--sidebar)\",\n  well: \"var(--well)\",\n\n  textPrimary: \"var(--text-primary)\",\n  textSecondary: \"var(--text-secondary)\",\n  textMuted: \"var(--text-muted)\",\n\n  border: \"var(--border)\",\n  borderSubtle: \"var(--border-subtle)\",\n  borderStrong: \"var(--border-strong)\",\n\n  // Translucent, so they read the same on any surface; `fillOpaque` hides what's under it.\n  fillSubtle: \"var(--fill-subtle)\",\n  fill: \"var(--fill)\",\n  fillStrong: \"var(--fill-strong)\",\n  fillOpaque: \"var(--fill-opaque)\",\n  fillOnStrong: \"var(--fill-on-strong)\",\n\n  accentSolid: \"var(--accent-solid)\",\n  accentOnSolid: \"var(--accent-on-solid)\",\n\n  invertedSolid: \"var(--inverted-solid)\",\n  invertedOnSolid: \"var(--inverted-on-solid)\",\n  invertedFill: \"var(--inverted-fill)\",\n\n  // Status: `Text` for icons and short labels, `Subtle` tints with their hover, `Solid`\n  // fills a shape that carries the status alone, like a dot.\n  dangerText: \"var(--danger-text)\",\n  dangerSubtle: \"var(--danger-subtle)\",\n  dangerSubtleHover: \"var(--danger-subtle-hover)\",\n  dangerSolid: \"var(--danger-solid)\",\n  dangerBorder: \"var(--danger-border)\",\n  infoText: \"var(--info-text)\",\n  infoSubtle: \"var(--info-subtle)\",\n  infoSubtleHover: \"var(--info-subtle-hover)\",\n  infoSolid: \"var(--info-solid)\",\n  infoOnSolid: \"var(--info-on-solid)\",\n  successText: \"var(--success-text)\",\n  successSubtle: \"var(--success-subtle)\",\n  successSubtleHover: \"var(--success-subtle-hover)\",\n  successSolid: \"var(--success-solid)\",\n  warningText: \"var(--warning-text)\",\n  warningSubtle: \"var(--warning-subtle)\",\n  warningSubtleHover: \"var(--warning-subtle-hover)\",\n  warningSolid: \"var(--warning-solid)\",\n\n  // Chart series, in order; a legend or a tooltip's swatch reads them too.\n  chart1: \"var(--chart-1)\",\n  chart2: \"var(--chart-2)\",\n  chart3: \"var(--chart-3)\",\n  chart4: \"var(--chart-4)\",\n  chart5: \"var(--chart-5)\",\n  chart6: \"var(--chart-6)\",\n} as const);\n\n// Multipliers for the corners and edge where an item meets its neighbor: 1 keeps them,\n// 0 joins them. ButtonGroup sets them, along its row (`inline`), its column (`block`) or\n// either; outside a group they stay 1, so nothing changes.\nexport const joins = stylex.defineVars({\n  block: \"1\",\n  either: \"1\",\n  inline: \"1\",\n});\n\n// An avatar's diameter, its badge's, the hole its photo leaves for the badge, and its\n// corner. Avatar sets them per size, so the masks that cut it apart from its neighbors and\n// badge can do math on them.\nexport const avatarVars = stylex.defineVars({\n  badge: \"0px\",\n  badgeCut: \"none\",\n  corner: \"9999px\",\n  size: \"0px\",\n});\n\n// The diameter of an avatar leading a badge. Badge sets it per size, Avatar reads it.\nexport const badgeVars = stylex.defineVars({\n  avatar: \"0px\",\n});\n\n// A card's padding, which its header, content and footer read so they line up at every size.\nexport const cardVars = stylex.defineVars({\n  spacing: \"0px\",\n});\n\n// A text field's type size over 16px, below which iOS zooms on focus. InputGroup sets it on touch screens.\nexport const inputVars = stylex.defineVars({\n  scale: \"1\",\n});\n\nexport const codeBlockVars = stylex.defineVars({\n  padBlock: \"0px\",\n  padInline: \"0px\",\n});\n\n// Steps by height, after Interfere's: `default` under a control, `lg` under a popup, `xl`\n// under a dialog. A step's slots follow it: `Hover`, `Pressed`, `Invalid`, `Ring` (draws its\n// own edge, `Subtle` in `borderSubtle`). The theme's depth sets `default`'s drops (`--shadow-*`):\n// a flat theme drops them, and a ring keeps its edge. `panel`, `lg` and `xl` stand off the page, so every theme keeps them.\nexport const shadows = stylex.defineConsts({\n  // The border is the control's edge, these its height. It rises a step on hover and\n  // settles flat while pressed.\n  default: \"var(--shadow-default)\",\n  defaultHover: \"var(--shadow-default-hover)\",\n  defaultPressed: \"var(--shadow-default-pressed)\",\n  defaultInvalid: \"0 0 0 2px var(--red-a4)\",\n  defaultRing: \"0 0 0 1px var(--border), var(--shadow-default-ring)\",\n  defaultRingSubtle:\n    \"0 0 0 1px var(--border-subtle), var(--shadow-default-ring-subtle)\",\n  defaultRingInvalid: \"0 0 0 1px var(--danger-border), 0 0 0 3px var(--red-a4)\",\n  // A side panel beside the page: it stands apart from the content in every depth, so the theme doesn't set it.\n  panel:\n    \"0 0 0 0.5px var(--border-subtle), 0 1px 2px var(--black-a1), 0 4px 12px -6px var(--black-a1)\",\n  // A near shadow for contact and a far one for height, so a surface sits at a distance.\n  lg: \"0 0 0 1px var(--border-subtle), 0 1px 2px var(--black-a1), 0 4px 16px -4px var(--black-a3)\",\n  xl: \"0 0 0 1px var(--border-subtle), 0 1px 3px var(--black-a1), 0 16px 40px -8px var(--black-a4)\",\n} as const);\n\n// The theme's fonts, which base.css sets on the page and reset.css on controls and code. Figures to compare take `mono`.\nexport const fonts = stylex.defineConsts({\n  mono: \"var(--font-mono)\",\n  sans: \"var(--font-sans)\",\n});\n\n// The bases the type scale multiplies, 4px at a 16px root. In rem, so text follows the\n// reader's browser font size; apart, so leading can change without the sizes.\nconst typeUnit = \"0.25rem\";\nconst leadingUnit = \"0.25rem\";\n\n// The type scale, after Interfere's: headings rank 1 (largest) to 6, text steps around\n// `textBase`. Each size has a line height of the same name; set them together. 15, 13 and\n// 11 add 1px to a step, as Interfere's do.\nexport const fontSizes = stylex.defineConsts({\n  heading1: `calc(${typeUnit} * 14)`,\n  heading2: `calc(${typeUnit} * 11)`,\n  heading3: `calc(${typeUnit} * 9)`,\n  heading4: `calc(${typeUnit} * 7)`,\n  heading5: `calc(${typeUnit} * 6)`,\n  heading6: `calc(${typeUnit} * 5)`,\n  textLg: `calc(${typeUnit} * 4.5)`,\n  // Reading copy.\n  textBase: `calc(${typeUnit} * 3.5 + 1px)`,\n  // A sidebar's rows.\n  textMd: `calc(${typeUnit} * 3.5)`,\n  // Controls and the text inside components.\n  textSm: `calc(${typeUnit} * 3 + 1px)`,\n  // Badges, shortcuts, group labels.\n  captionLg: `calc(${typeUnit} * 3)`,\n  captionBase: `calc(${typeUnit} * 2.5 + 1px)`,\n  captionSm: `calc(${typeUnit} * 2.5)`,\n  captionXs: `calc(${typeUnit} * 2)`,\n  // Under 16px, iOS Safari zooms the page when a field takes focus; fields switch to this on touch.\n  touch: \"16px\",\n} as const);\n\nexport const lineHeights = stylex.defineConsts({\n  heading1: `calc(${leadingUnit} * 14)`,\n  heading2: `calc(${leadingUnit} * 14)`,\n  heading3: `calc(${leadingUnit} * 11)`,\n  heading4: `calc(${leadingUnit} * 9)`,\n  heading5: `calc(${leadingUnit} * 8)`,\n  heading6: `calc(${leadingUnit} * 6)`,\n  textLg: `calc(${leadingUnit} * 6)`,\n  textBase: `calc(${leadingUnit} * 6)`,\n  textMd: `calc(${leadingUnit} * 5)`,\n  textSm: `calc(${leadingUnit} * 5)`,\n  captionLg: `calc(${leadingUnit} * 4)`,\n  captionBase: `calc(${leadingUnit} * 4)`,\n  captionSm: `calc(${leadingUnit} * 4)`,\n  captionXs: `calc(${leadingUnit} * 3)`,\n  touch: `calc(${leadingUnit} * 6)`,\n});\n\n// Large type tightens a little, so a heading doesn't read as spaced out.\nexport const letterSpacings = stylex.defineConsts({\n  heading: \"-0.01em\",\n  // Uppercase reads cramped at its own spacing.\n  caps: \"0.05em\",\n});\n\nexport const fontWeights = stylex.defineConsts({\n  medium: \"500\",\n  regular: \"400\",\n  // 450 and 550 need a variable font; a static one renders them as 500 and 600.\n  semimedium: \"450\",\n  semibold: \"550\",\n});\n\n// The base space, sizes and the theme's radius multiply, 4px at a 16px root, as Tailwind's\n// `--spacing`. In rem, so controls and their padding grow with the text.\nconst spaceUnit = \"0.25rem\";\n\n// Values a theme can change point at variables `base.css` defaults and a theme overrides.\n// `xxxs` and `xxs` stay fixed across themes: they're hairline gaps.\nexport const space = stylex.defineConsts({\n  xxxs: `calc(${spaceUnit} * 0.5)`,\n  xxs: spaceUnit,\n  xs: \"var(--space-xs)\",\n  sm: \"var(--space-sm)\",\n  md: \"var(--space-md)\",\n  lg: \"var(--space-lg)\",\n  xl: \"var(--space-xl)\",\n  // shadcn's off-grid insets, a hairline under a step so they follow the preset: a row's 6, a popup's 10.\n  row: `calc(var(--space-xs) - ${spaceUnit} * 0.5)`,\n  popup: `calc(var(--space-sm) - ${spaceUnit} * 0.5)`,\n} as const);\n\nexport const radii = stylex.defineConsts({\n  // The 20px pieces, Badge and Kbd; off the 4px grid, as 4 reads square and 8 a pill.\n  chip: \"var(--radius-chip)\",\n  full: \"9999px\",\n  lg: \"var(--radius-lg)\",\n  md: \"var(--radius-md)\",\n  xl: \"var(--radius-xl)\",\n  sm: \"var(--radius-sm)\",\n  xs: \"var(--radius-xs)\",\n} as const);\n\nexport const sizes = stylex.defineConsts({\n  controlLg: \"var(--size-control-lg)\",\n  controlMd: \"var(--size-control-md)\",\n  controlSm: \"var(--size-control-sm)\",\n  controlXs: \"var(--size-control-xs)\",\n  controlXxs: \"var(--size-control-xxs)\",\n  // A small badge: a hairline under `controlXxs`, so it follows the preset.\n  controlXxxs: `calc(var(--size-control-xxs) - ${spaceUnit} * 0.5)`,\n  // A vertical attachment card's width, and its wide variant's.\n  attachment: `calc(${spaceUnit} * 24)`,\n  attachmentWide: `calc(${spaceUnit} * 30)`,\n  // A citation pill's widest; a longer publication name truncates.\n  citation: `calc(${spaceUnit} * 40)`,\n  dialog: `calc(${spaceUnit} * 96)`,\n  dialogLg: `calc(${spaceUnit} * 128)`,\n  dialogSm: `calc(${spaceUnit} * 80)`,\n  // The top row the sidebar and the page share, so their headers line up.\n  header: `calc(${spaceUnit} * 14)`,\n  hitArea: `calc(${spaceUnit} * 10)`,\n  icon: `calc(${spaceUnit} * 4)`,\n  iconLg: `calc(${spaceUnit} * 5)`,\n  iconSm: `calc(${spaceUnit} * 3.5)`,\n  iconXs: `calc(${spaceUnit} * 3)`,\n  iconXxs: `calc(${spaceUnit} * 2)`,\n  kbd: `calc(${spaceUnit} * 5)`,\n  // An item's image or avatar, beside a title and description.\n  media: `calc(${spaceUnit} * 10)`,\n  // The least an item's text keeps beside its actions before they drop under it.\n  itemText: `calc(${spaceUnit} * 40)`,\n  menu: `calc(${spaceUnit} * 36)`,\n  // A default or large menu's least width, roomy enough for a label beside its value.\n  menuLg: `calc(${spaceUnit} * 50)`,\n  menuHeight: `calc(${spaceUnit} * 80)`,\n  // A model picker's list: short names beside a search field.\n  modelPicker: `calc(${spaceUnit} * 60)`,\n  // A centered block of short text, like an empty state's.\n  measure: `calc(${spaceUnit} * 96)`,\n  popover: `calc(${spaceUnit} * 72)`,\n  // A settings dialog's nav and page side by side, capped to the window.\n  settingsDialog: `calc(${spaceUnit} * 250)`,\n  settingsDialogHeight: `calc(${spaceUnit} * 160)`,\n  // A side panel beside the content.\n  sidePanel: `calc(${spaceUnit} * 100)`,\n  // A sidebar open, and collapsed to its icons (a control and its group padding).\n  sidebar: `calc(${spaceUnit} * 64)`,\n  sidebarIcon: `calc(${spaceUnit} * 12)`,\n  thumb: `calc(${spaceUnit} * 3)`,\n  toast: `calc(${spaceUnit} * 89)`,\n  tooltip: `calc(${spaceUnit} * 80)`,\n} as const);\n\n// Lines stay in px, so a hairline stays one crisp device pixel at any text size.\nexport const strokes = stylex.defineConsts({\n  border: \"1px\",\n  // Hugeicons' weight beside medium text; read in JS, so a bare number. In the icon's own\n  // units, so it scales with the icon.\n  icon: \"1.75\",\n  // The line under a `line` tab, and the bar beside a vertical one.\n  indicator: \"2px\",\n  spinner: \"1.5px\",\n  // A slider or progress bar: a size, not a line, so it grows with its thumb.\n  track: spaceUnit,\n});\n\n// Popup distances from their trigger, read in JS by Base UI, so bare numbers.\nexport const offsets = stylex.defineConsts({\n  popover: \"4\",\n  tooltip: \"8\",\n});\n\nexport const layers = stylex.defineConsts({\n  // Floats over the page, under a dialog's backdrop.\n  bar: \"30\",\n  dialog: \"40\",\n  // Above a dialog, so a select or menu opened from one sits on top of it.\n  popover: \"45\",\n  toast: \"50\",\n  tooltip: \"60\",\n} as const);\n\nexport const opacities = stylex.defineConsts({\n  busy: \"0.8\",\n  // A skeleton at the low point of its pulse.\n  pulse: \"0.6\",\n  disabled: \"0.6\",\n  hover: \"0.88\",\n});\n\nexport const media = stylex.defineConsts({\n  coarse: \"@media (pointer: coarse)\",\n  hover: \"@media (hover: hover)\",\n  reducedMotion: \"@media (prefers-reduced-motion: reduce)\",\n  sm: \"@media (min-width: 640px)\",\n  // Below it, a sidebar opens as a sheet; JS reads the query.\n  md: \"@media (min-width: 768px)\",\n});\n\nexport const presses = stylex.defineConsts({\n  icon: \"scale(0.95)\",\n  link: \"scale(0.97)\",\n  row: \"scale(0.99)\",\n});\n\n// Shared by name so the tokens and the CSS variables below can't drift.\nconst easeOut = \"cubic-bezier(0.23, 1, 0.32, 1)\";\nconst easeCrossfade = \"cubic-bezier(0.2, 0, 0, 1)\";\nconst popoverIn = \"180ms\";\nconst popoverOut = \"120ms\";\nconst crossfadeFor = \"300ms\";\nconst surfaceFor = \"200ms\";\n\nexport const easings = stylex.defineConsts({\n  inOut: \"cubic-bezier(0.77, 0, 0.175, 1)\",\n  out: easeOut,\n  overshoot: \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n  layout: \"cubic-bezier(0.32, 0.72, 0, 1)\",\n  crossfade: easeCrossfade,\n} as const);\n\nexport const durations = stylex.defineConsts({\n  hover: \"150ms\",\n  move: \"250ms\",\n  popover: popoverIn,\n  popoverExit: popoverOut,\n  press: \"160ms\",\n  layout: \"300ms\",\n  layoutExit: surfaceFor,\n  spin: \"1s\",\n  // One breath of a skeleton: slow, so loading reads as calm.\n  pulse: \"2s\",\n  // One sweep of a shimmer across working text.\n  shimmer: \"2s\",\n  crossfade: crossfadeFor,\n  // How long a confirmation (copied, saved) holds before it reverts; read in JS.\n  confirm: \"1500ms\",\n  dialog: surfaceFor,\n  chart: surfaceFor,\n  sidebar: \"250ms\",\n  sidebarExit: surfaceFor,\n  // The gap between siblings arriving one after another.\n  stagger: \"50ms\",\n  // How long an image may load before its fallback shows; read in JS.\n  fallbackDelay: \"600ms\",\n  // Half Base UI's 600ms, for a tooltip and a hover card alike.\n  tooltipDelay: \"300ms\",\n  hoverCardDelay: \"300ms\",\n  hoverCardCloseDelay: \"300ms\",\n} as const);\n\n// The motion CSS outside StyleX's reach reads (view transitions in base.css and the site),\n// under literal names.\nexport const motionVars = stylex.defineVars({\n  \"--duration-crossfade\": crossfadeFor,\n  \"--duration-popover\": popoverIn,\n  \"--duration-popover-exit\": popoverOut,\n  \"--ease-crossfade\": easeCrossfade,\n  \"--ease-out\": easeOut,\n});\n\n// The shapes motion moves between: where a crossfade starts, how far an exit drops,\n// how much each surface behind a stack shrinks.\nexport const motion = stylex.defineConsts({\n  // How far a row rises as it arrives.\n  arriveOffset: \"4px\",\n  dialogScale: \"0.96\",\n  dotScale: \"0.5\",\n  exitOffset: \"8px\",\n  popoverScale: \"0.96\",\n  stackScale: \"0.05\",\n  crossfadeBlur: \"4px\",\n  crossfadeScale: \"0.25\",\n  crossfadeTextBlur: \"2px\",\n});\n\nexport const blurs = stylex.defineConsts({\n  backdrop: \"4px\",\n});\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constella/theme.stylex.ts",
      "content": "// Your theme: edit these to make Constella yours. Everything else in the system reads them.\n//\n// - Scales: `--neutral-*` and `--accent-*` point at Radix Colors scales by name. To change\n//   one, import its four files and point the steps at it (the theme builder writes this).\n// - Roles: what components ask for (`--border`, `--fill`…), each a scale step; change one alone.\n//   The ones that differ between light and dark live in base.css.\n// - Shape and density: one radius (the rest derive from it), control heights, spacing. In rem\n//   (1rem is 16px at the default root), so they grow with the reader's browser font size.\n// - Depth: `--shadow-*`, the drops under controls and in-page surfaces. Raised lifts them;\n//   flat leaves them out, so edges are borders and fills. Floating surfaces keep theirs.\n// - Fonts: `--font-sans` for text, `--font-mono` for code and figures. Named, not shipped: load\n//   them or put yours first. base.css sets them on the page and reset.css on controls and code; components inherit.\n//\n// The defaults are Polaris: Radix gray, an ink accent, 8px corners, default sizing, raised depth, Inter and Geist Mono.\n// Literal names, so `base.css` can read them. For more themes to switch between at\n// runtime, `stylex.createTheme(theme, { … })` and apply it to <html>.\nimport * as stylex from \"@stylexjs/stylex\";\n\nexport const theme = stylex.defineVars({\n  \"--neutral-1\": \"var(--gray-1)\",\n  \"--neutral-2\": \"var(--gray-2)\",\n  \"--neutral-3\": \"var(--gray-3)\",\n  \"--neutral-4\": \"var(--gray-4)\",\n  \"--neutral-5\": \"var(--gray-5)\",\n  \"--neutral-6\": \"var(--gray-6)\",\n  \"--neutral-7\": \"var(--gray-7)\",\n  \"--neutral-8\": \"var(--gray-8)\",\n  \"--neutral-9\": \"var(--gray-9)\",\n  \"--neutral-10\": \"var(--gray-10)\",\n  \"--neutral-11\": \"var(--gray-11)\",\n  \"--neutral-12\": \"var(--gray-12)\",\n  \"--neutral-a1\": \"var(--gray-a1)\",\n  \"--neutral-a2\": \"var(--gray-a2)\",\n  \"--neutral-a3\": \"var(--gray-a3)\",\n  \"--neutral-a4\": \"var(--gray-a4)\",\n  \"--neutral-a5\": \"var(--gray-a5)\",\n  \"--neutral-a6\": \"var(--gray-a6)\",\n  \"--neutral-a7\": \"var(--gray-a7)\",\n  \"--neutral-a8\": \"var(--gray-a8)\",\n  \"--neutral-a9\": \"var(--gray-a9)\",\n  \"--neutral-a10\": \"var(--gray-a10)\",\n  \"--neutral-a11\": \"var(--gray-a11)\",\n  \"--neutral-a12\": \"var(--gray-a12)\",\n  \"--accent-1\": \"var(--neutral-1)\",\n  \"--accent-2\": \"var(--neutral-2)\",\n  \"--accent-3\": \"var(--neutral-3)\",\n  \"--accent-4\": \"var(--neutral-4)\",\n  \"--accent-5\": \"var(--neutral-5)\",\n  \"--accent-6\": \"var(--neutral-6)\",\n  \"--accent-7\": \"var(--neutral-7)\",\n  \"--accent-8\": \"var(--neutral-8)\",\n  \"--accent-9\": \"var(--neutral-9)\",\n  \"--accent-10\": \"var(--neutral-10)\",\n  \"--accent-11\": \"var(--neutral-11)\",\n  \"--accent-12\": \"var(--neutral-12)\",\n  \"--accent-a1\": \"var(--neutral-a1)\",\n  \"--accent-a2\": \"var(--neutral-a2)\",\n  \"--accent-a3\": \"var(--neutral-a3)\",\n  \"--accent-a4\": \"var(--neutral-a4)\",\n  \"--accent-a5\": \"var(--neutral-a5)\",\n  \"--accent-a6\": \"var(--neutral-a6)\",\n  \"--accent-a7\": \"var(--neutral-a7)\",\n  \"--accent-a8\": \"var(--neutral-a8)\",\n  \"--accent-a9\": \"var(--neutral-a9)\",\n  \"--accent-a10\": \"var(--neutral-a10)\",\n  \"--accent-a11\": \"var(--neutral-a11)\",\n  \"--accent-a12\": \"var(--neutral-a12)\",\n  \"--accent-contrast\": \"var(--neutral-1)\",\n  // Roles: what components ask for, valued by a scale step.\n  \"--background\": \"var(--neutral-1)\",\n  \"--sidebar\": \"var(--neutral-2)\",\n  \"--text-primary\": \"var(--neutral-12)\",\n  \"--text-secondary\": \"var(--neutral-11)\",\n  // Halfway to secondary: gray-10 is too faint for small labels, gray-11 would merge with secondary.\n  \"--text-muted\": \"color-mix(in oklab, var(--neutral-10), var(--neutral-11))\",\n  \"--fill-subtle\": \"var(--neutral-a2)\",\n  \"--fill\": \"var(--neutral-a3)\",\n  \"--fill-strong\": \"var(--neutral-a4)\",\n  \"--fill-opaque\": \"var(--neutral-3)\",\n  // Edges are solid steps: a translucent one shows whatever it crosses.\n  \"--border-subtle\": \"var(--neutral-4)\",\n  \"--border\": \"var(--neutral-6)\",\n  // A control's own edge, like an empty checkbox's: 3:1 on the page, as WCAG asks of controls.\n  \"--border-strong\": \"var(--neutral-9)\",\n  \"--inverted-solid\": \"var(--neutral-12)\",\n  \"--inverted-on-solid\": \"var(--neutral-1)\",\n  \"--font-mono\":\n    '\"Geist Mono Variable\", \"Geist Mono\", ui-monospace, \"SF Mono\", Menlo, monospace',\n  \"--font-sans\":\n    '\"Inter Variable\", \"Inter\", -apple-system, BlinkMacSystemFont, sans-serif',\n  \"--overlay\": \"var(--black-a5)\",\n  \"--accent-solid\": \"var(--neutral-12)\",\n  \"--accent-on-solid\": \"var(--neutral-1)\",\n  \"--focus-ring\": \"var(--neutral-8)\",\n  \"--selection\": \"var(--neutral-a5)\",\n  \"--radius\": \"0.5rem\",\n  \"--size-control-xxs\": \"1.25rem\",\n  \"--size-control-xs\": \"1.5rem\",\n  \"--size-control-sm\": \"1.75rem\",\n  \"--size-control-md\": \"2rem\",\n  \"--size-control-lg\": \"2.25rem\",\n  \"--space-xs\": \"0.5rem\",\n  \"--space-sm\": \"0.75rem\",\n  \"--space-md\": \"1rem\",\n  \"--space-lg\": \"1.5rem\",\n  \"--space-xl\": \"3rem\",\n  // Depth: raised. Flat sets each to `0 0 transparent`, a drop that draws nothing, as `none` can't sit in a list.\n  \"--shadow-default\":\n    \"0 1px 2px -1px var(--black-a1), 0 2px 4px 0 var(--black-a1)\",\n  \"--shadow-default-hover\":\n    \"0 1px 2px -1px var(--black-a2), 0 2px 4px 0 var(--black-a1)\",\n  \"--shadow-default-pressed\": \"0 1px 1px -1px var(--black-a2)\",\n  \"--shadow-default-ring\": \"0 1px 3px var(--black-a5)\",\n  \"--shadow-default-ring-subtle\":\n    \"0 1px 2px var(--black-a1), 0 4px 12px -6px var(--black-a1)\",\n});\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constella/base.css",
      "content": "@import \"@radix-ui/colors/black-alpha.css\";\n@import \"@radix-ui/colors/white-alpha.css\";\n@import \"@radix-ui/colors/gray.css\";\n@import \"@radix-ui/colors/gray-alpha.css\";\n@import \"@radix-ui/colors/gray-dark.css\";\n@import \"@radix-ui/colors/gray-dark-alpha.css\";\n@import \"@radix-ui/colors/amber.css\";\n@import \"@radix-ui/colors/amber-alpha.css\";\n@import \"@radix-ui/colors/amber-dark.css\";\n@import \"@radix-ui/colors/amber-dark-alpha.css\";\n@import \"@radix-ui/colors/blue.css\";\n@import \"@radix-ui/colors/blue-alpha.css\";\n@import \"@radix-ui/colors/blue-dark.css\";\n@import \"@radix-ui/colors/blue-dark-alpha.css\";\n@import \"@radix-ui/colors/green.css\";\n@import \"@radix-ui/colors/green-alpha.css\";\n@import \"@radix-ui/colors/green-dark.css\";\n@import \"@radix-ui/colors/green-dark-alpha.css\";\n@import \"@radix-ui/colors/red.css\";\n@import \"@radix-ui/colors/red-alpha.css\";\n@import \"@radix-ui/colors/red-dark.css\";\n@import \"@radix-ui/colors/red-dark-alpha.css\";\n@import \"@radix-ui/colors/plum.css\";\n@import \"@radix-ui/colors/plum-dark.css\";\n@import \"@radix-ui/colors/teal.css\";\n@import \"@radix-ui/colors/teal-dark.css\";\n@import \"@radix-ui/colors/violet.css\";\n@import \"@radix-ui/colors/violet-dark.css\";\n@import \"@radix-ui/colors/cyan.css\";\n@import \"@radix-ui/colors/cyan-dark.css\";\n@import \"@radix-ui/colors/orange.css\";\n@import \"@radix-ui/colors/orange-dark.css\";\n@import \"@radix-ui/colors/pink.css\";\n@import \"@radix-ui/colors/pink-dark.css\";\n@import \"./reset.css\";\n\n@layer base {\n  :root {\n    color-scheme: light;\n    font-family: var(--font-sans);\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n\n  /* Native controls and scrollbars follow the theme too. */\n  .dark {\n    color-scheme: dark;\n  }\n\n  /* Status tints are the vivid step 9 (info, its own solid) at low opacity: Radix's dark alpha\n     steps go muddy. Listed on each theme class, so the mix resolves where the theme's color does. */\n  :root,\n  .light,\n  .dark {\n    --danger-subtle: color-mix(in oklab, var(--red-9) 14%, transparent);\n    --danger-subtle-hover: color-mix(in oklab, var(--red-9) 22%, transparent);\n    --info-subtle: color-mix(in oklab, var(--info-solid) 14%, transparent);\n    --info-subtle-hover: color-mix(\n      in oklab,\n      var(--info-solid) 22%,\n      transparent\n    );\n    --success-subtle: color-mix(in oklab, var(--green-9) 14%, transparent);\n    --success-subtle-hover: color-mix(\n      in oklab,\n      var(--green-9) 22%,\n      transparent\n    );\n    --warning-subtle: color-mix(in oklab, var(--amber-9) 14%, transparent);\n    --warning-subtle-hover: color-mix(\n      in oklab,\n      var(--amber-9) 22%,\n      transparent\n    );\n    /* Chart series, in this order: no status hue, and each neighbor stays apart under\n       red-green color blindness. TanStack Charts reads its palette by the `--ts-chart-*` names. */\n    --chart-1: var(--plum-9);\n    --chart-2: var(--teal-9);\n    --chart-3: var(--violet-9);\n    --chart-4: var(--cyan-9);\n    --chart-6: var(--pink-9);\n    --ts-chart-1: var(--chart-1);\n    --ts-chart-2: var(--chart-2);\n    --ts-chart-3: var(--chart-3);\n    --ts-chart-4: var(--chart-4);\n    --ts-chart-5: var(--chart-5);\n    --ts-chart-6: var(--chart-6);\n  }\n\n  /* Status dots, and corners derived from the theme's one radius. The theme's roles and\n     scales live in theme.stylex.ts. */\n  :root {\n    --danger-solid: var(--red-9);\n    --danger-border: var(--danger-text);\n    --info-on-solid: #fff;\n    --success-solid: var(--green-9);\n    /* Corners climb from the one radius a `space.xxs` step at a time, so each nests\n       concentrically in the next; the step shrinks with a small radius so all reach 0 together. */\n    --radius-step: min(var(--radius), 0.25rem);\n    --radius-xs: calc(var(--radius) - var(--radius-step));\n    --radius-sm: var(--radius);\n    --radius-md: calc(var(--radius) + var(--radius-step));\n    --radius-lg: calc(var(--radius) + 2 * var(--radius-step));\n    --radius-xl: calc(var(--radius) + 3 * var(--radius-step));\n    --radius-chip: max(0px, var(--radius) - 0.125rem);\n  }\n\n  /* Light step 11 drops under 4.5:1 on its tint, so status text leans 30% toward step 12\n     (info leans its solid 40%); amber 9 is 1.5:1 on the light page, so its dot takes step 11. */\n  :root,\n  .light {\n    --inverted-fill: var(--white-a4);\n    /* Floating surfaces sit above the page: white on the off-white page, a step lighter in dark. */\n    --raised: #fff;\n    /* A prompt input's tray: a2 is enough against white; dark needs a3 to part from `raised`. */\n    --well: var(--neutral-a2);\n    /* A mark on a `fillStrong` track, like a switch's off thumb: white in light; dark takes step 12 to stand off its track. */\n    --fill-on-strong: #fff;\n    --danger-text: color-mix(in oklab, var(--red-11), var(--red-12) 30%);\n    /* Radix blue 9 reads sky; info is a deeper royal blue, so a switch's on state looks set. */\n    --info-solid: #195eff;\n    --info-text: color-mix(in oklab, var(--info-solid), var(--blue-12) 40%);\n    --success-text: color-mix(in oklab, var(--green-11), var(--green-12) 30%);\n    --warning-text: color-mix(in oklab, var(--amber-11), var(--amber-12) 30%);\n    --warning-solid: var(--amber-11);\n    --chart-5: var(--orange-9);\n  }\n\n  .dark {\n    --inverted-fill: var(--black-a2);\n    --raised: var(--neutral-2);\n    --well: var(--neutral-a3);\n    --fill-on-strong: var(--neutral-12);\n    --danger-text: var(--red-11);\n    --info-solid: #427eff;\n    --info-text: color-mix(in oklab, var(--info-solid), #fff 35%);\n    --success-text: var(--green-11);\n    --warning-text: var(--amber-11);\n    --warning-solid: var(--amber-9);\n    /* Step 9 is too light for the dark page's chart band. */\n    --chart-5: var(--orange-8);\n  }\n\n  /* Selected text is the page's, not a component's, so the theme tints it here. */\n  ::selection {\n    background-color: var(--selection);\n  }\n\n  /* Flush outside the edge, so it shows on any fill. */\n  :where(\n    :focus-visible,\n    [data-slot=\"input-group\"]:has(\n      [data-slot=\"input-group-control\"]:focus-visible\n    ),\n    [data-slot=\"questionnaire-choice\"]:has(\n      > [data-slot=\"questionnaire-choice-input\"]:focus-visible\n    )\n  ) {\n    outline: 1px solid var(--focus-ring);\n    outline-offset: 0;\n  }\n\n  :where(\n    [aria-invalid=\"true\"],\n    [data-invalid],\n    [data-slot=\"input-group\"]:has(\n      [data-slot=\"input-group-control\"]:is(\n        [aria-invalid=\"true\"],\n        [data-invalid]\n      )\n    )\n  ):where(\n    :focus-visible,\n    :has([data-slot=\"input-group-control\"]:focus-visible),\n    :has(> [data-slot=\"questionnaire-choice-input\"]:focus-visible)\n  ) {\n    outline-color: var(--red-11);\n  }\n\n  /* The composer shows focus with its caret: a text field matches :focus-visible on a click too. */\n  :where([data-slot=\"prompt-input\"] [data-slot=\"input-group\"]) {\n    outline: none;\n  }\n\n  /* A popup or questionnaire step takes focus to hold it, not as a control. */\n  :where(\n    [role=\"dialog\"],\n    [role=\"alertdialog\"],\n    [role=\"menu\"],\n    [role=\"listbox\"],\n    [data-slot=\"questionnaire-item\"]\n  ):focus-visible {\n    outline: none;\n  }\n\n  /* A list item shows the keyboard on its highlight fill, so it draws no ring. */\n  :where(\n    [role=\"option\"],\n    [role=\"menuitem\"],\n    [role=\"menuitemcheckbox\"],\n    [role=\"menuitemradio\"]\n  ):focus-visible {\n    outline: none;\n  }\n\n  /* A table row spans its scroll container, which would clip the ring's sides, so it draws inside. */\n  :where(tr):focus-visible {\n    outline-offset: -1px;\n  }\n\n  /* StyleX can't style a child it didn't render, so an outline badge's icon takes its color here. */\n  :where([data-slot=\"badge\"][data-variant=\"outline\"]) {\n    &[data-color=\"success\"] > [data-icon] {\n      color: var(--success-text);\n    }\n    &[data-color=\"info\"] > [data-icon] {\n      color: var(--info-text);\n    }\n    &[data-color=\"warning\"] > [data-icon] {\n      color: var(--warning-text);\n    }\n    &[data-color=\"danger\"] > [data-icon] {\n      color: var(--danger-text);\n    }\n  }\n\n  /* StyleX can't style a child it didn't render, so an alert's icon sits beside its title,\n     centered on the title's 20px line, and takes its color here. */\n  :where([data-slot=\"alert\"]) > :where(svg) {\n    grid-row: span 2;\n    translate: 0 2px;\n  }\n  :where([data-slot=\"alert\"][data-color=\"success\"]) > :where(svg) {\n    color: var(--success-text);\n  }\n  :where([data-slot=\"alert\"][data-color=\"info\"]) > :where(svg) {\n    color: var(--info-text);\n  }\n  :where([data-slot=\"alert\"][data-color=\"warning\"]) > :where(svg) {\n    color: var(--warning-text);\n  }\n  :where([data-slot=\"alert\"][data-color=\"danger\"]) > :where(svg) {\n    color: var(--danger-text);\n  }\n\n  /* StyleX can't style a child it didn't render, so a row's icon keeps its size beside a\n     long label. */\n  :where(\n      [data-slot=\"sidebar-menu-button\"],\n      [data-slot=\"dropdown-menu-item\"],\n      [data-slot=\"dropdown-menu-sub-trigger\"],\n      [data-slot=\"dropdown-menu-checkbox-item\"],\n      [data-slot=\"dropdown-menu-radio-item\"],\n      [data-slot=\"command-item\"]\n    )\n    > :where(svg) {\n    flex-shrink: 0;\n  }\n\n  /* A view transition's snapshots hang off the root, out of StyleX's reach, so a sidebar's\n     section change slides here, like a stack: the old section leaves and the new arrives\n     from the other side, `motion.exitOffset` apart under `motion.crossfadeBlur`\n     (`durations.popoverExit` out, `durations.popover` in, on `easings.crossfade`). Each\n     region clips to its own box, and the page swaps at once. */\n  :root:active-view-transition-type(sidebar-forward) {\n    --sidebar-blur: 4px;\n    --sidebar-shift: -8px;\n  }\n  :root:active-view-transition-type(sidebar-back),\n  :root[dir=\"rtl\"]:active-view-transition-type(sidebar-forward) {\n    --sidebar-blur: 4px;\n    --sidebar-shift: 8px;\n  }\n  :root[dir=\"rtl\"]:active-view-transition-type(sidebar-back) {\n    --sidebar-shift: -8px;\n  }\n  @media (prefers-reduced-motion: reduce) {\n    :root:active-view-transition-type(sidebar-forward, sidebar-back) {\n      --sidebar-blur: 0px;\n      --sidebar-shift: 0px;\n    }\n  }\n  :root:active-view-transition-type(\n      sidebar-forward, sidebar-back\n    )::view-transition-old(root),\n  :root:active-view-transition-type(\n      sidebar-forward, sidebar-back\n    )::view-transition-new(root) {\n    animation: none;\n  }\n  ::view-transition-group(*.sidebar-swap) {\n    animation-duration: var(--duration-popover);\n    animation-timing-function: var(--ease-crossfade);\n    overflow: clip;\n  }\n  ::view-transition-old(*.sidebar-swap) {\n    animation: sidebar-leave var(--duration-popover-exit) var(--ease-crossfade)\n      both;\n  }\n  ::view-transition-new(*.sidebar-swap) {\n    animation: sidebar-arrive var(--duration-popover) var(--ease-crossfade) both;\n  }\n  @keyframes sidebar-leave {\n    to {\n      filter: blur(var(--sidebar-blur));\n      opacity: 0;\n      translate: var(--sidebar-shift) 0;\n    }\n  }\n  @keyframes sidebar-arrive {\n    from {\n      filter: blur(var(--sidebar-blur));\n      opacity: 0;\n      translate: calc(-1 * var(--sidebar-shift)) 0;\n    }\n  }\n\n  /* Base UI renders the hover card viewport's content wrappers, so their crossfade between\n     triggers lives here: the old content fades out on top while the new fades in\n     (`durations.popover` on `easings.out`). */\n  :where([data-slot=\"hover-card-viewport\"])\n    > :where([data-current], [data-previous]) {\n    transition: opacity var(--duration-popover) var(--ease-out);\n  }\n  :where([data-slot=\"hover-card-viewport\"]) > :where([data-previous]) {\n    inset: 0 auto auto 0;\n    position: absolute;\n    width: 100%;\n  }\n  :where([data-slot=\"hover-card-viewport\"])\n    > :where(\n      [data-current][data-starting-style],\n      [data-previous][data-ending-style]\n    ) {\n    opacity: 0;\n  }\n\n  /* StyleX can't style a child it didn't render, so an item's or attachment's image fills its media box here. */\n  :where(\n      [data-slot=\"item-media\"][data-variant=\"image\"],\n      [data-slot=\"attachment-media\"][data-variant=\"image\"]\n    )\n    > :where(img) {\n    height: 100%;\n    object-fit: cover;\n    width: 100%;\n  }\n\n  /* A directional icon is the consumer's child, which StyleX can't reach, so one attribute\n     turns it to face the reading direction. */\n  :where([dir=\"rtl\"] [data-rtl-flip]) {\n    transform: scaleX(-1);\n  }\n\n  /* TanStack Charts renders the chart's focusable svg, which StyleX can't reach, so its ring's\n     corners round here to nest inside a card's. */\n  :where([data-slot=\"chart\"] svg.ts-chart) {\n    border-radius: var(--radius-sm);\n  }\n\n  /* A click focuses that svg too, and the browser rings it; only the keyboard should. */\n  :where([data-slot=\"chart\"] svg.ts-chart:focus:not(:focus-visible)) {\n    outline: none;\n  }\n\n  /* An input group rings its whole box, so the borderless control inside draws none. */\n  :where([data-slot=\"input-group-control\"]:focus-visible) {\n    outline: none;\n  }\n}\n\n/* Torph puts each letter of a Swap Text on its own layer, out of reach of a Shimmer's\n   `background-clip: text`, which blanks it. Unlayered, since Torph's own rule is. */\n[data-slot=\"shimmer\"] [torph-item]:not(br) {\n  will-change: auto;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constella/styles.ts",
      "content": "import * as stylex from \"@stylexjs/stylex\";\n\nimport type { layers } from \"@/lib/constella/tokens.stylex\";\nimport {\n  colors,\n  durations,\n  easings,\n  fontSizes,\n  fontWeights,\n  joins,\n  letterSpacings,\n  lineHeights,\n  media,\n  radii,\n  shadows,\n  sizes,\n  space,\n  strokes,\n} from \"@/lib/constella/tokens.stylex\";\n\n// A group's own parts, not the focus guards Base UI drops beside an open popup's trigger.\nconst notFirst = \":not(:nth-child(1 of [data-slot]))\";\nconst notLast = \":not(:nth-last-child(1 of [data-slot]))\";\n\n// The neighbor before draws the shared edge, or the separator does.\nconst joined =\n  \":not(:nth-child(1 of [data-slot]), [data-slot='button-group-separator'] + *)\";\n\n// Whole on their own; in a ButtonGroup, the corners that meet a neighbor take its join.\nconst corners = (radius: Values<typeof radii>) => ({\n  borderEndEndRadius: {\n    default: radius,\n    [notLast]: `calc(${radius} * ${joins.either})` as const,\n  },\n  borderEndStartRadius: {\n    default: radius,\n    [notFirst]: `calc(${radius} * ${joins.inline})` as const,\n    [notLast]: {\n      default: `calc(${radius} * ${joins.block})` as const,\n      [notFirst]: `calc(${radius} * ${joins.either})` as const,\n    },\n  },\n  borderStartEndRadius: {\n    default: radius,\n    [notFirst]: `calc(${radius} * ${joins.block})` as const,\n    [notLast]: {\n      default: `calc(${radius} * ${joins.inline})` as const,\n      [notFirst]: `calc(${radius} * ${joins.either})` as const,\n    },\n  },\n  borderStartStartRadius: {\n    default: radius,\n    [notFirst]: `calc(${radius} * ${joins.either})` as const,\n  },\n});\n\n/** Corners and start edge that join a neighbor in a ButtonGroup. Apply after a part's own styles. */\nconst joinStyles = stylex.create({\n  edges: {\n    borderBlockStartWidth: {\n      default: strokes.border,\n      [joined]: `calc(${strokes.border} * ${joins.block})`,\n    },\n    borderInlineStartWidth: {\n      default: strokes.border,\n      [joined]: `calc(${strokes.border} * ${joins.inline})`,\n    },\n  },\n  full: corners(radii.full),\n  md: corners(radii.md),\n  sm: corners(radii.sm),\n  xs: corners(radii.xs),\n});\n\nconst visuallyHidden = stylex.create({\n  base: {\n    clipPath: \"inset(50%)\",\n    height: 1,\n    overflow: \"hidden\",\n    position: \"absolute\",\n    whiteSpace: \"nowrap\",\n    width: 1,\n  },\n});\n\n// The type scale's steps, each a size with its line height. Put one before a part's own\n// styles: `stylex.props(typeStyles.textSm, styles.label)`.\nconst typeStyles = stylex.create({\n  heading1: {\n    fontSize: fontSizes.heading1,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading1,\n  },\n  heading2: {\n    fontSize: fontSizes.heading2,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading2,\n  },\n  heading3: {\n    fontSize: fontSizes.heading3,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading3,\n  },\n  heading4: {\n    fontSize: fontSizes.heading4,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading4,\n  },\n  heading5: {\n    fontSize: fontSizes.heading5,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading5,\n  },\n  heading6: {\n    fontSize: fontSizes.heading6,\n    letterSpacing: letterSpacings.heading,\n    lineHeight: lineHeights.heading6,\n  },\n  textLg: { fontSize: fontSizes.textLg, lineHeight: lineHeights.textLg },\n  textBase: { fontSize: fontSizes.textBase, lineHeight: lineHeights.textBase },\n  textMd: { fontSize: fontSizes.textMd, lineHeight: lineHeights.textMd },\n  textSm: { fontSize: fontSizes.textSm, lineHeight: lineHeights.textSm },\n  captionLg: {\n    fontSize: fontSizes.captionLg,\n    lineHeight: lineHeights.captionLg,\n  },\n  captionBase: {\n    fontSize: fontSizes.captionBase,\n    lineHeight: lineHeights.captionBase,\n  },\n  captionSm: {\n    fontSize: fontSizes.captionSm,\n    lineHeight: lineHeights.captionSm,\n  },\n  captionXs: {\n    fontSize: fontSizes.captionXs,\n    lineHeight: lineHeights.captionXs,\n  },\n});\n\n// The open state repeats inside the media block, which StyleX ranks above a bare condition.\nconst interactive = <R, A>(rest: R, active: A) => ({\n  default: rest,\n  \":is([data-popup-open])\": active,\n  [media.hover]: {\n    default: rest,\n    \":is([data-popup-open])\": active,\n    \":hover:not(:disabled)\": active,\n  },\n  \":active:not(:disabled)\": active,\n});\n\nconst pressed = \":is([data-pressed])\";\nconst hoverUnpressed = \":hover:not(:disabled, [data-pressed])\";\nconst activeUnpressed = \":active:not(:disabled, [data-pressed])\";\nconst hoverPressed = \":is([data-pressed]):hover:not(:disabled, :active)\";\n\n// Selected is `fill`; hover stays a step under, and hovering a selected part stacks\n// `fillSubtle` on its fill as an inset shadow, so the two never read alike.\nconst selectable = <R>(rest: R) => ({\n  default: rest,\n  [media.hover]: {\n    default: rest,\n    [hoverUnpressed]: colors.fillSubtle,\n    [pressed]: colors.fill,\n  },\n  [activeUnpressed]: colors.fillSubtle,\n  [pressed]: colors.fill,\n});\nconst lit = `inset 0 0 0 100vmax ${colors.fillSubtle}`;\nconst unlit = \"inset 0 0 0 100vmax transparent\";\n\n// A pressable's fill and height by variant, for a part that looks like a button but isn't\n// one. Apply after the part's own styles: each replaces its whole property.\nconst stateStyles = stylex.create({\n  ghost: { backgroundColor: interactive(\"transparent\", colors.fillSubtle) },\n  outline: {\n    backgroundColor: interactive(colors.background, colors.fillSubtle),\n  },\n  secondary: { backgroundColor: interactive(colors.fillSubtle, colors.fill) },\n  // A ghost toggle, selected while `[data-pressed]`.\n  selectedFill: {\n    backgroundColor: selectable(\"transparent\"),\n    boxShadow: {\n      default: unlit,\n      [media.hover]: { default: unlit, [hoverPressed]: lit },\n    },\n  },\n  // An outline toggle: `selectedFill` on a background, with the resting shadow under it.\n  selectedFillLifted: {\n    backgroundColor: selectable(colors.background),\n    boxShadow: {\n      default: `${unlit}, ${shadows.default}`,\n      [media.hover]: {\n        default: `${unlit}, ${shadows.default}`,\n        \":hover:not(:disabled, :active)\": `${unlit}, ${shadows.defaultHover}`,\n        [hoverPressed]: `${lit}, ${shadows.defaultHover}`,\n        \":active:not(:disabled)\": `${unlit}, ${shadows.defaultPressed}`,\n      },\n      \":active:not(:disabled)\": `${unlit}, ${shadows.defaultPressed}`,\n    },\n  },\n});\n\n// A control's icon by its size, for an icon the part renders itself (`size={controlIconSize[size]}`).\nconst controlIconSize = {\n  xs: sizes.iconXs,\n  \"icon-xs\": sizes.iconXs,\n  sm: sizes.iconSm,\n  \"icon-sm\": sizes.icon,\n  default: sizes.icon,\n  icon: sizes.icon,\n  lg: sizes.icon,\n  \"icon-lg\": sizes.icon,\n} as const;\n\n// The bar Progress and Meter share, and the label and value above it. A slider's track takes `colors.fillOpaque` too.\nconst trackStyles = stylex.create({\n  root: {\n    alignItems: \"center\",\n    display: \"flex\",\n    flexWrap: \"wrap\",\n    gap: space.sm,\n    width: \"100%\",\n  },\n  track: {\n    backgroundColor: colors.fillOpaque,\n    borderStartStartRadius: radii.full,\n    borderStartEndRadius: radii.full,\n    borderEndStartRadius: radii.full,\n    borderEndEndRadius: radii.full,\n    display: \"flex\",\n    height: strokes.track,\n    overflow: \"hidden\",\n    position: \"relative\",\n    width: \"100%\",\n  },\n  indicator: {\n    backgroundColor: colors.accentSolid,\n    borderStartStartRadius: radii.full,\n    borderStartEndRadius: radii.full,\n    borderEndStartRadius: radii.full,\n    borderEndEndRadius: radii.full,\n    height: \"100%\",\n    transitionDuration: {\n      default: durations.move,\n      [media.reducedMotion]: \"0s\",\n    },\n    transitionProperty: \"width, background-color\",\n    transitionTimingFunction: easings.out,\n  },\n  label: {\n    color: colors.textPrimary,\n    fontWeight: fontWeights.medium,\n  },\n  value: {\n    color: colors.textSecondary,\n    fontVariantNumeric: \"tabular-nums\",\n    marginInlineStart: \"auto\",\n  },\n});\n\n// The weights and colors Text, Heading and Caption take as props.\nconst textWeights = [\"regular\", \"semimedium\", \"medium\", \"semibold\"] as const;\nconst textColors = [\n  \"primary\",\n  \"secondary\",\n  \"muted\",\n  \"danger\",\n  \"success\",\n  \"warning\",\n  \"info\",\n] as const;\n\ntype TextWeight = (typeof textWeights)[number];\ntype TextColor = (typeof textColors)[number];\n\nconst textWeightStyles = stylex.create({\n  regular: { fontWeight: fontWeights.regular },\n  semimedium: { fontWeight: fontWeights.semimedium },\n  medium: { fontWeight: fontWeights.medium },\n  semibold: { fontWeight: fontWeights.semibold },\n});\n\nconst textColorStyles = stylex.create({\n  primary: { color: colors.textPrimary },\n  secondary: { color: colors.textSecondary },\n  muted: { color: colors.textMuted },\n  danger: { color: colors.dangerText },\n  success: { color: colors.successText },\n  warning: { color: colors.warningText },\n  info: { color: colors.infoText },\n});\n\ntype Values<T> = T[keyof T];\ntype SpaceValue = Values<typeof space>;\n\ntype ColorProperty =\n  | \"accentColor\"\n  | \"backgroundColor\"\n  | \"borderBlockColor\"\n  | \"borderBlockEndColor\"\n  | \"borderBlockStartColor\"\n  | \"borderBottomColor\"\n  | \"borderColor\"\n  | \"borderInlineColor\"\n  | \"borderInlineEndColor\"\n  | \"borderInlineStartColor\"\n  | \"borderLeftColor\"\n  | \"borderRightColor\"\n  | \"borderTopColor\"\n  | \"caretColor\"\n  | \"color\"\n  | \"columnRuleColor\"\n  | \"fill\"\n  | \"outlineColor\"\n  | \"stroke\"\n  | \"textDecorationColor\";\ntype SpaceProperty =\n  | `${\"margin\" | \"padding\"}${\"\" | \"Block\" | \"BlockEnd\" | \"BlockStart\" | \"Bottom\" | \"Inline\" | \"InlineEnd\" | \"InlineStart\" | \"Left\" | \"Right\" | \"Top\"}`\n  | \"columnGap\"\n  | \"gap\"\n  | \"rowGap\";\ntype RadiusProperty =\n  | \"borderBottomLeftRadius\"\n  | \"borderBottomRightRadius\"\n  | \"borderEndEndRadius\"\n  | \"borderEndStartRadius\"\n  | \"borderRadius\"\n  | \"borderStartEndRadius\"\n  | \"borderStartStartRadius\"\n  | \"borderTopLeftRadius\"\n  | \"borderTopRightRadius\";\ntype DurationProperty =\n  | \"animationDelay\"\n  | \"animationDuration\"\n  | \"transitionDelay\"\n  | \"transitionDuration\";\ntype EasingProperty = \"animationTimingFunction\" | \"transitionTimingFunction\";\n\ntype ShorthandProperty =\n  | \"animation\"\n  | \"background\"\n  | \"border\"\n  | \"borderBlock\"\n  | \"borderBlockEnd\"\n  | \"borderBlockStart\"\n  | \"borderBottom\"\n  | \"borderInline\"\n  | \"borderInlineEnd\"\n  | \"borderInlineStart\"\n  | \"borderLeft\"\n  | \"borderRight\"\n  | \"borderTop\"\n  | \"columnRule\"\n  | \"font\"\n  | \"outline\"\n  | \"textDecoration\"\n  | \"transition\";\n\ntype SpaceToken = SpaceValue | Values<typeof sizes>;\ntype DurationValue = Values<typeof durations>;\ntype EasingValue = Values<typeof easings> | \"ease\" | \"linear\";\n\ntype TokenStyles = Partial<\n  Record<\n    ColorProperty,\n    | Values<(typeof colors)[\"__tokens\"]>\n    | \"currentColor\"\n    | \"inherit\"\n    | \"transparent\"\n    | null\n  > &\n    Record<\n      SpaceProperty,\n      | SpaceToken\n      | 0\n      | \"auto\"\n      | `${\"calc\" | \"clamp\" | \"max\" | \"min\"}(${string}${SpaceToken}${string}`\n      | null\n    > &\n    Record<\n      RadiusProperty,\n      | Values<typeof radii>\n      | `calc(${Values<typeof radii>} * ${string})`\n      | 0\n      | \"inherit\"\n      | null\n    > &\n    Record<\n      DurationProperty,\n      DurationValue | `${DurationValue}, ${string}` | 0 | null\n    > &\n    Record<EasingProperty, EasingValue | `${EasingValue}, ${string}` | null> &\n    Record<\n      \"boxShadow\",\n      Values<typeof shadows> | `${string}var(--${string}` | \"none\" | null\n    > &\n    Record<\"fontSize\", Values<typeof fontSizes> | \"inherit\" | null> &\n    Record<\"zIndex\", Values<typeof layers> | 0 | \"auto\" | null>\n>;\n\n/** A part's `sx`: colors, space, radii, shadows, type sizes, layers and motion take only tokens, as longhands; other properties stay open. A property that takes a token joins the lists above. */\ntype Sx = stylex.StyleXStyles<\n  Omit<stylex.CSSProperties, keyof TokenStyles | ShorthandProperty> &\n    TokenStyles\n>;\n\nexport {\n  controlIconSize,\n  joinStyles,\n  stateStyles,\n  textColors,\n  textColorStyles,\n  textWeights,\n  textWeightStyles,\n  trackStyles,\n  typeStyles,\n  visuallyHidden,\n};\nexport type { Sx, TextColor, TextWeight };\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constella/motion.ts",
      "content": "import { useState } from \"react\";\n\ninterface OpenChangeDetails {\n  event: Event;\n  reason: string;\n}\n\n// Base UI marks only some keyboard changes `data-instant`, and differently per primitive.\nfunction skipsMotion({ event, reason }: OpenChangeDetails) {\n  switch (reason) {\n    case \"escape-key\":\n    case \"item-press\":\n    case \"list-navigation\": {\n      return true;\n    }\n    case \"trigger-press\": {\n      // A click from Enter or Space has no click count.\n      return event instanceof UIEvent && event.detail === 0;\n    }\n    default: {\n      return false;\n    }\n  }\n}\n\n/** Wraps a popup root's `onOpenChange`; `skip` is true while the keyboard, Esc or a picked item moved it. */\nfunction useSkipMotion<Details extends OpenChangeDetails>(\n  onOpenChange?: (open: boolean, details: Details) => void\n) {\n  const [skip, setSkip] = useState(false);\n  function handleOpenChange(open: boolean, details: Details) {\n    setSkip(skipsMotion(details));\n    onOpenChange?.(open, details);\n  }\n  return [skip, handleOpenChange] as const;\n}\n\nexport { useSkipMotion };\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constella/reset.css",
      "content": "/* What the browser styles that components and pages rely on being otherwise, after Tailwind's\n   preflight. In base's layer, so any StyleX rule wins over it. */\n@layer base {\n  /* Padding and borders count in a box's size; spacing is the layout's, never the browser's. */\n  *,\n  ::before,\n  ::after {\n    box-sizing: border-box;\n    margin: 0;\n  }\n\n  html {\n    /* iOS otherwise enlarges text in wide blocks such as code. */\n    -webkit-text-size-adjust: 100%;\n    text-size-adjust: 100%;\n    -webkit-tap-highlight-color: transparent;\n    line-height: 1.5;\n    text-rendering: optimizelegibility;\n  }\n\n  body {\n    min-height: 100dvh;\n  }\n\n  :where(img, picture, video, canvas, svg) {\n    display: block;\n    max-width: 100%;\n  }\n\n  /* The browser gives controls a font and color of their own, so they take the page's. */\n  :where(button, input, select, optgroup, textarea),\n  ::file-selector-button {\n    color: inherit;\n    font: inherit;\n    letter-spacing: inherit;\n  }\n\n  :where(button) {\n    background: none;\n    border: none;\n    cursor: pointer;\n    padding: 0;\n    text-align: unset;\n  }\n\n  :where(button:disabled) {\n    cursor: not-allowed;\n  }\n\n  /* A link looks like its text until a part draws it, as Link does. */\n  :where(a) {\n    color: inherit;\n    text-decoration: inherit;\n  }\n\n  :where(p, li, figcaption) {\n    overflow-wrap: break-word;\n    text-wrap: pretty;\n  }\n\n  :where(h1, h2, h3, h4, h5, h6) {\n    text-wrap: balance;\n  }\n\n  /* Code takes the theme's mono; a part that wants the page font names it, as Kbd does. */\n  :where(code, kbd, samp, pre) {\n    font-family: var(--font-mono);\n  }\n\n  /* Important so a component's own `display` can't show what a primitive hid. */\n  [hidden]:not([hidden=\"until-found\"]) {\n    display: none !important;\n  }\n}\n",
      "type": "registry:lib"
    }
  ],
  "docs": "Needs the StyleX compiler with an `@/*` alias. `@import` lib/constella/base.css from the stylesheet the root route or layout links. Toggle dark mode with a `.dark` class on <html>. Setup per framework: https://constella.aayush.cv/docs/installation/index.md. Every docs page: https://constella.aayush.cv/llms.txt",
  "type": "registry:lib"
}