{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"base","title":"Siteplane UI Base","description":"Provider-free baseline styles and utilities.","dependencies":["@base-ui/react@1.7.0","clsx@2.1.1","tailwind-merge@3.6.0"],"devDependencies":["tailwindcss@4.3.3"],"registryDependencies":[],"files":[{"path":"packages/ui/src/system/styles/base.css","content":"@layer theme, base, components, siteplane, utilities;\n@import \"tailwindcss\";\n@source \"../..\";\n/* Tests render no page: the strings they assert on make no utilities. */\n@source not \"../../**/*.test.ts\";\n@source not \"../../**/*.test.tsx\";\n@import \"./generated/tokens.css\" layer(siteplane);\n@import \"./generated/tailwind.css\";\n@import \"./defaults.css\" layer(base);\n@import \"./recipes.css\" layer(components);\n@import \"./motion.css\" layer(components);\n@import \"./design-rework.css\" layer(siteplane);\n\n@theme inline {\n  --color-state-hover: var(--state-hover);\n  --color-state-pressed: var(--state-pressed);\n  --color-state-selected: var(--state-selected);\n  --color-sidebar: var(--sidebar);\n  --color-sidebar-foreground: var(--sidebar-foreground);\n  --color-sidebar-primary: var(--sidebar-primary);\n  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n  --color-sidebar-accent: var(--sidebar-accent);\n  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n  --color-sidebar-border: var(--sidebar-border);\n  --color-sidebar-ring: var(--sidebar-ring);\n  --color-chart-1: var(--chart-1);\n  --color-chart-2: var(--chart-2);\n  --color-chart-3: var(--chart-3);\n  --color-chart-4: var(--chart-4);\n  --color-chart-5: var(--chart-5);\n  --color-chart-6: var(--chart-6);\n  --color-chart-7: var(--chart-7);\n  --color-chart-8: var(--chart-8);\n  /* rounded-control, rounded-surface and rounded-overlay take the role radii\n   * (design-rework.css), so they follow the provider's radius like the kit. */\n  --radius-control: var(--sp-rw-radius-control);\n  --radius-surface: var(--sp-rw-radius-surface);\n  --radius-overlay: var(--sp-rw-radius-dialog);\n}\n\n@custom-variant dark (&:where(.dark, .dark *, [data-theme=\"dark\"], [data-theme=\"dark\"] *));\n\n/* Text renders with grayscale antialiasing, as the kit is designed: macOS\n * browsers otherwise apply the system's font smoothing, which draws it\n * heavier (Lukas, 15 October 2026). The root carries it, so portals inherit\n * it, and the siteplane layer lets an application's own font-smoothing win.\n * Other systems ignore both properties. */\n@layer siteplane {\n  :root {\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/base.css"},{"path":"packages/ui/src/system/styles/defaults.css","content":":root {\n  color: var(--sp-semantic-color-foreground);\n  background: var(--sp-semantic-color-background);\n  font-family: var(--sp-semantic-font-family);\n  font-synthesis: none;\n  /* --radius and every radius derived from it: design-rework.css. */\n  --badge-sm-font-size: 0.625rem;\n  --code-inline-font-size: 0.85em;\n  --code-block-sm-font-size: 0.6875rem;\n  /* Motion (rulebook M1). Every duration and easing of the kit comes from\n   * this set, and the Motion settings page documents it. Components read the\n   * tokens in their CSS; the Motion parts and the charts read them when a run\n   * starts.\n   *\n   * The duration scale: the steps that most motion takes. */\n  --motion-duration-micro: 100ms;\n  --motion-duration-fast: 150ms;\n  --motion-duration-base: 180ms;\n  --motion-duration-moderate: 240ms;\n  --motion-duration-slow: 320ms;\n  --motion-duration-linger: 380ms;\n  /* A backdrop fades, and a popover or tooltip cross-fades between its\n   * panes. */\n  --motion-duration-overlay: 160ms;\n  /* A panel expands or collapses, an item enters or leaves a list, and the\n   * panels of Tabs follow a new panel's height. */\n  --motion-duration-expand: 200ms;\n  /* A dialog opens and closes. */\n  --motion-duration-modal: 210ms;\n  /* What follows a pointer glides to its next place without overshooting:\n   * the crosshair, the dots and the pill of a chart over the follow duration,\n   * and the tooltip card, which trails them, over the trail duration. */\n  --motion-duration-follow: 280ms;\n  --motion-duration-follow-trail: 520ms;\n  /* A NavigationMenu's popup, and the switch between its groups. */\n  --motion-duration-navigation: 300ms;\n  /* A figure that changes in place: digits roll and chart marks glide to the\n   * new data. */\n  --motion-duration-value: var(--motion-duration-linger);\n  /* A Sheet or Drawer panel slides in from its edge, and its backdrop fades\n   * with it. A Drawer that is let go after a swipe settles within the release\n   * time, scaled by the strength of the swipe. */\n  --motion-duration-panel: 450ms;\n  --motion-duration-panel-release: 400ms;\n  /* A carousel page settles like a flicked track: fast start, long calm\n   * finish. */\n  --motion-duration-carousel: 800ms;\n  /* One cycle of a loading loop: a turn of the Spinner and the base of its\n   * other variants, and half a sweep of the Skeleton shimmer. */\n  --motion-duration-loop: 1000ms;\n  /* One loop of a featured icon's ring pulse. A ring travels from the glyph to\n   * where it has faded during the first 80% of the loop (3.2 s) and rests,\n   * faded, for the last 20% (0.8 s). The second ring runs half a loop behind,\n   * so a new ring leaves the glyph every half loop (2.0 s). */\n  --motion-duration-ring-pulse: 4000ms;\n  /* One turn of the Badge light: both lights run once around the badge. */\n  --motion-duration-badge-light: 5000ms;\n  /* The standard curve: a quick start that settles softly. What appears,\n   * moves, draws or changes takes it unless its motion names another curve. */\n  --motion-ease-standard: cubic-bezier(0.16, 1, 0.3, 1);\n  /* State changes: hover, press, focus and selection on fields, choice\n   * controls, tabs and accordions, and a scrollbar that fades. */\n  --motion-ease-state: cubic-bezier(0.4, 0, 0.2, 1);\n  /* Even in and out: a shape that morphs, a fill that sweeps, a series that\n   * dims, and content that fades out of a swap. */\n  --motion-ease-soft-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  /* A Collapsible's panel and chevron. */\n  --motion-ease-expand: cubic-bezier(0.7, 0.1, 0.5, 0.9);\n  /* A Sheet or Drawer panel and its backdrop. */\n  --motion-ease-panel: cubic-bezier(0.32, 0.72, 0, 1);\n  /* The browser's default ease: a NavigationMenu, the Icon Morphs, calendar\n   * days, the fades of the toast stack and a menu row's Switch thumb. */\n  --motion-ease-navigation: cubic-bezier(0.25, 0.1, 0.25, 1);\n  /* A carousel page that settles (rulebook M3). */\n  --motion-ease-carousel: cubic-bezier(0.25, 1, 0.5, 1);\n  /* A Switch thumb on its way to the other end. */\n  --motion-ease-switch: cubic-bezier(0, 0.55, 0.45, 1);\n  /* The toast replay: a quick start that eases into place. */\n  --motion-ease-toast-replay: cubic-bezier(0.5, 1, 0.89, 1);\n  /* A loading loop that swells and settles: the Spinner's comet, dots and\n   * bars. */\n  --motion-ease-loop: ease-in-out;\n  /* A ping that leaves fast and slows as it fades: the Spinner's pulse. */\n  --motion-ease-ping: cubic-bezier(0, 0, 0.2, 1);\n  /* An even pace: a value that streams in, a loop that turns, a stroke that\n   * draws, the Sidebar's width and slide, so that the rail, the labels and\n   * the content stay in step, and a featured icon's traveling rings, so\n   * that the two stay evenly spaced. */\n  --motion-ease-linear: linear;\n  /* How far a NavigationMenu popup or a popover that flips sides travels as\n   * it settles, and the scale the flipping popover starts from. */\n  --motion-overlay-y: 4px;\n  --motion-overlay-scale: 0.985;\n  /* How far the content of a NavigationMenu travels when its group changes. */\n  --motion-navigation-distance: 0.75rem;\n  /* How far a floating panel travels, and the scale it grows from, as it\n   * opens. */\n  --motion-floating-panel-offset: 7px;\n  --motion-floating-panel-scale: 0.955;\n  /* Items of a flow list scale slightly as they enter and leave. */\n  --motion-presence-scale: 0.96;\n  /* A checked indicator pops in from this scale as its check draws in and\n   * shrinks back to it as the check draws out: the Checkbox and Radio\n   * indicators and the check glyph of a menu row, a list item or a swatch. */\n  --motion-check-pop-scale: 0.82;\n  /* How far a featured icon's ring travels on its way out, as a scale of its\n   * 28 px circle: from 21 px (0.75), just outside the glyph, to about\n   * 43 px. */\n  --motion-ring-pulse-scale: 1.55;\n  /* The opacity a featured icon's traveling ring reaches a tenth of the way\n   * out, just after it leaves the glyph; it fades from there. */\n  --motion-ring-pulse-opacity: 0.24;\n  /* How many loops of --motion-duration-ring-pulse a featured icon's rings\n   * travel: by default for as long as the icon is shown. A number stops them\n   * after that many waves, and the glyph then stands alone, without rings. */\n  --motion-ring-pulse-count: infinite;\n  --state-hover: rgb(0 0 0 / 4%);\n  --state-pressed: rgb(0 0 0 / 8%);\n  --state-selected: color-mix(in srgb, var(--color-primary) 8%, transparent);\n  --background: var(--sp-semantic-color-background);\n  --foreground: var(--sp-semantic-color-foreground);\n  --card: var(--sp-semantic-color-card);\n  --primary: var(--sp-semantic-color-primary);\n  --primary-foreground: var(--sp-semantic-color-primary-foreground);\n  --secondary: var(--sp-semantic-color-secondary);\n  --secondary-foreground: var(--sp-semantic-color-secondary-foreground);\n  --accent: var(--sp-semantic-color-accent);\n  --destructive: var(--sp-semantic-color-destructive);\n  --input: var(--sp-semantic-color-input);\n  --ring: var(--sp-semantic-color-ring);\n  --info: var(--sp-semantic-color-info);\n  --success: var(--sp-semantic-color-success);\n  --warning: var(--sp-semantic-color-warning);\n  --button-text: oklch(0.466 0.027 271.6);\n  /* Categorical chart palette in a fixed order: blue, orange, teal, purple, gold,\n   * cyan, rose, green. Each series keeps its hue in Dark, which is lighter and\n   * more saturated. Every mark is at least 3:1 on the card and page surfaces of\n   * its theme. The last three hues sit halfway in the widest gaps the first five\n   * leave on the hue circle, so every two hues are at least 40 degrees apart,\n   * and neighbours in the order stay as far apart as the first five. */\n  --chart-1: oklch(57.5% 0.185 265);\n  --chart-2: oklch(63.5% 0.172 48);\n  --chart-3: oklch(63% 0.112 181);\n  --chart-4: oklch(50.5% 0.196 325);\n  --chart-5: oklch(62% 0.122 91);\n  --chart-6: oklch(61% 0.11 223);\n  --chart-7: oklch(58% 0.19 7);\n  --chart-8: oklch(60% 0.155 136);\n  /* The brand colour of a chart, whose shades are its default palette: the\n   * brand itself in Light, and in Dark the lifted brand that progress fills\n   * take, so that a mark and its lighter shades still read on a dark card. The\n   * shades fade into the card in Light; in Dark they fade towards a deeper\n   * brand (`--chart-brand-fade`), so that they stay blue instead of greying\n   * into the dark card, and text on the lightest shade stays legible. */\n  --chart-brand: var(--sp-rw-bg-accent);\n  --sidebar: var(--color-neutral-50);\n  /* 70% keeps 12px group labels at 4.5:1 on the light sidebar surfaces. */\n  --sidebar-foreground: color-mix(\n    in srgb,\n    var(--color-neutral-800) 70%,\n    var(--sidebar)\n  );\n  --sidebar-primary: var(--color-neutral-800);\n  --sidebar-primary-foreground: var(--color-neutral-50);\n  --sidebar-accent: rgb(0 0 0 / 4%);\n  --sidebar-accent-foreground: var(--color-neutral-800);\n  --sidebar-border: rgb(0 0 0 / 6%);\n  --sidebar-ring: var(--color-neutral-400);\n}\n\n/* Same theme scopes as the generated tokens and design-rework.css: the kit is\n * light until the application sets .dark or data-theme=\"dark\". */\n.dark,\n[data-theme=\"dark\"] {\n  --state-hover: rgb(255 255 255 / 4%);\n  --state-pressed: rgb(255 255 255 / 8%);\n  --state-selected: color-mix(in srgb, var(--color-primary) 12%, transparent);\n  --button-text: oklch(0.879 0.016 270);\n  --chart-1: oklch(64% 0.19 265);\n  --chart-2: oklch(66.5% 0.18 48);\n  --chart-3: oklch(66.5% 0.116 181);\n  --chart-4: oklch(61% 0.21 325);\n  --chart-5: oklch(66.5% 0.126 91);\n  --chart-6: oklch(66.5% 0.116 223);\n  --chart-7: oklch(64% 0.2 7);\n  --chart-8: oklch(66.5% 0.165 136);\n  --chart-brand: var(--sp-rw-progress-fill);\n  --chart-brand-fade: color-mix(in oklab, var(--chart-brand) 50%, black);\n  --sidebar: color-mix(\n    in srgb,\n    var(--color-neutral-950) 97%,\n    var(--color-white)\n  );\n  --sidebar-foreground: color-mix(\n    in srgb,\n    var(--color-neutral-100) 64%,\n    var(--sidebar)\n  );\n  --sidebar-primary: var(--color-neutral-100);\n  --sidebar-primary-foreground: var(--color-neutral-800);\n  --sidebar-accent: rgb(255 255 255 / 4%);\n  --sidebar-accent-foreground: var(--color-neutral-100);\n  --sidebar-border: rgb(255 255 255 / 5%);\n  --sidebar-ring: var(--color-neutral-400);\n}\n\n/* Reduced motion is instant (rulebook M5). Under each of the kit's three\n * signals, the system setting, html[data-motion-reduce=\"true\"] and the\n * provider preset data-sp-motion=\"reduced\", every duration token but the loops\n * is 1 ms on every element in scope, so transitions and animations end at once\n * and still settle: their end events fire and getAnimations() empties. The\n * declarations are important, so a token an app overrides does not bring\n * motion back. The loop, ring pulse and Badge light durations keep their\n * values: the Spinner, Skeleton, indeterminate Progress, the Badge light and a\n * featured icon's rings stop under reduced motion instead. */\n@media (prefers-reduced-motion: reduce) {\n  :where(:root, :root *) {\n    --motion-duration-micro: 1ms !important;\n    --motion-duration-fast: 1ms !important;\n    --motion-duration-base: 1ms !important;\n    --motion-duration-moderate: 1ms !important;\n    --motion-duration-slow: 1ms !important;\n    --motion-duration-linger: 1ms !important;\n    --motion-duration-overlay: 1ms !important;\n    --motion-duration-expand: 1ms !important;\n    --motion-duration-modal: 1ms !important;\n    --motion-duration-follow: 1ms !important;\n    --motion-duration-follow-trail: 1ms !important;\n    --motion-duration-navigation: 1ms !important;\n    --motion-duration-value: 1ms !important;\n    --motion-duration-panel: 1ms !important;\n    --motion-duration-panel-release: 1ms !important;\n    --motion-duration-carousel: 1ms !important;\n  }\n}\n\n:where(\n  :root[data-motion-reduce=\"true\"],\n  :root[data-motion-reduce=\"true\"] *,\n  [data-sp-motion=\"reduced\"],\n  [data-sp-motion=\"reduced\"] *\n) {\n  --motion-duration-micro: 1ms !important;\n  --motion-duration-fast: 1ms !important;\n  --motion-duration-base: 1ms !important;\n  --motion-duration-moderate: 1ms !important;\n  --motion-duration-slow: 1ms !important;\n  --motion-duration-linger: 1ms !important;\n  --motion-duration-overlay: 1ms !important;\n  --motion-duration-expand: 1ms !important;\n  --motion-duration-modal: 1ms !important;\n  --motion-duration-follow: 1ms !important;\n  --motion-duration-follow-trail: 1ms !important;\n  --motion-duration-navigation: 1ms !important;\n  --motion-duration-value: 1ms !important;\n  --motion-duration-panel: 1ms !important;\n  --motion-duration-panel-release: 1ms !important;\n  --motion-duration-carousel: 1ms !important;\n}\n\n.animate-skeleton {\n  animation: skeleton calc(var(--motion-duration-loop) * 2)\n    calc(var(--motion-duration-loop) * -1) infinite var(--motion-ease-linear);\n}\n\n@keyframes skeleton {\n  to {\n    background-position: -200% 0;\n  }\n}\n\n:where(*, *::before, *::after) {\n  box-sizing: border-box;\n  border-color: var(--color-border);\n  outline-color: color-mix(in oklab, var(--color-ring) 50%, transparent);\n}\n\n:where(button),\n:where(input),\n:where(textarea),\n:where(select) {\n  font: inherit;\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/defaults.css"},{"path":"packages/ui/src/system/styles/motion.css","content":".sp-floating-panel {\n  --sp-floating-panel-x: 0px;\n  --sp-floating-panel-y: calc(var(--motion-floating-panel-offset) * -1);\n  transform-origin: var(--transform-origin, top center);\n  transition-property: opacity, transform;\n  transition-duration: var(--motion-duration-moderate);\n  transition-timing-function: var(--motion-ease-standard);\n  will-change: opacity, transform;\n}\n\n.sp-floating-panel[data-side=\"top\"] {\n  --sp-floating-panel-y: var(--motion-floating-panel-offset);\n}\n\n.sp-floating-panel[data-side=\"left\"],\n.sp-floating-panel[data-side=\"inline-start\"] {\n  --sp-floating-panel-x: var(--motion-floating-panel-offset);\n  --sp-floating-panel-y: 0px;\n}\n\n.sp-floating-panel[data-side=\"right\"],\n.sp-floating-panel[data-side=\"inline-end\"] {\n  --sp-floating-panel-x: calc(var(--motion-floating-panel-offset) * -1);\n  --sp-floating-panel-y: 0px;\n}\n\n/* A right-to-left page: the dir attribute is named beside :dir(rtl), which a\n * build for older browsers turns into a list of right-to-left languages, as\n * Tailwind's rtl variant does. */\n.sp-floating-panel[data-side=\"inline-start\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  ) {\n  --sp-floating-panel-x: calc(var(--motion-floating-panel-offset) * -1);\n}\n\n.sp-floating-panel[data-side=\"inline-end\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  ) {\n  --sp-floating-panel-x: var(--motion-floating-panel-offset);\n}\n\n.sp-floating-panel[data-side=\"none\"] {\n  --sp-floating-panel-y: 0px;\n}\n\n.sp-floating-panel[data-starting-style],\n.sp-floating-panel[data-ending-style] {\n  opacity: 0;\n  transform: translate(var(--sp-floating-panel-x), var(--sp-floating-panel-y))\n    scale(var(--motion-floating-panel-scale));\n}\n\n/* Fade through for a pane that Base UI mounts and unmounts and fades with CSS\n * transitions, such as a Tabs panel (rulebook M8; swapFadeClassName of the\n * Motion component). The pane that takes over fades in during the last micro\n * duration of the base duration; the pane that leaves fades out at once. Zero\n * specificity, so the reduced-motion rules below and a consumer's utility both\n * win. */\n:where(.sp-swap-fade) {\n  transition-property: opacity;\n  transition-duration: var(--motion-duration-micro);\n  transition-timing-function: var(--motion-ease-standard);\n  transition-delay: max(\n    0ms,\n    calc(var(--motion-duration-base) - var(--motion-duration-micro))\n  );\n}\n\n:where(.sp-swap-fade:is([data-starting-style], [data-ending-style])) {\n  opacity: 0;\n}\n\n:where(.sp-swap-fade[data-ending-style]) {\n  transition-timing-function: var(--motion-ease-soft-in-out);\n  transition-delay: 0ms;\n}\n\n/* A Tabs panel switches at once (rulebook M8; panelSwitchClassName of the\n * Motion component), as tabs do in the WAI-ARIA pattern and most interfaces:\n * the panel that leaves is hidden the moment the new one shows, and the\n * indicator and the height of the panels' container carry the motion. A\n * dissolve showed both panels' text at once wherever a panel has no surface of\n * its own (Lukas, 13 October 2026). Zero specificity, so a consumer's utility\n * wins. */\n:where(.sp-panel-switch[data-ending-style]) {\n  opacity: 0;\n}\n\n/* The height follow (followHeight in motion-follow.ts; rulebook M7, M8): a\n * container whose content changes moves to the height of the new content.\n * JavaScript measures the height the container shows and the height of its\n * new content and writes them as its height; this transition animates between\n * them, as Base UI's Collapsible and Accordion panels animate the height they\n * measure. It starts with the frame that renders it and retargets from the\n * height shown when content that builds later changes the height again. A\n * container carries the class only while it follows. Its rows keep their own\n * height meanwhile: a container held taller than its content would stretch\n * the lines of a wrapping row or a grid's rows. Zero specificity, so a\n * consumer's utility wins (P6); with reduced motion nothing follows (M5). A\n * swap moves its height over the whole swap, everything else over the expand\n * duration. */\n:where(.sp-height-follow) {\n  align-content: start;\n  transition-property: height;\n  transition-duration: var(--motion-duration-expand);\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:where(.sp-height-follow.sp-motion-swap) {\n  transition-duration: var(--motion-duration-base);\n}\n\n@keyframes sp-calendar-slide-in-from-right {\n  from {\n    transform: translateX(100%);\n  }\n\n  to {\n    transform: translateX(0);\n  }\n}\n\n@keyframes sp-calendar-slide-out-to-left {\n  from {\n    transform: translateX(0);\n  }\n\n  to {\n    transform: translateX(-100%);\n  }\n}\n\n@keyframes sp-calendar-slide-in-from-left {\n  from {\n    transform: translateX(-100%);\n  }\n\n  to {\n    transform: translateX(0);\n  }\n}\n\n@keyframes sp-calendar-slide-out-to-right {\n  from {\n    transform: translateX(0);\n  }\n\n  to {\n    transform: translateX(100%);\n  }\n}\n\n.sp-icon-pop {\n  animation: sp-icon-pop var(--motion-duration-base) var(--motion-ease-standard);\n}\n\n/* A carousel caption fades in as its page slides in, instead of jumping. */\n@keyframes sp-carousel-caption-in {\n  from {\n    opacity: 0;\n  }\n\n  to {\n    opacity: 1;\n  }\n}\n\n.sp-carousel-caption {\n  animation: sp-carousel-caption-in var(--motion-duration-slow)\n    var(--motion-ease-soft-in-out);\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-carousel-caption,\n[data-sp-motion=\"reduced\"] .sp-carousel-caption {\n  animation: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sp-carousel-caption {\n    animation: none;\n  }\n}\n\n/* A Toggle with `lift` lifts its icon once as it turns on: up and back with a\n * slight tilt, a small reaction such as a like (Lukas, 12 October 2026).\n * toggle.tsx sets data-lift=\"on\" for one play and clears it when it ends. A\n * soft curve on the way up and the way down, from the icon's base, on its own\n * layer so the small move does not snap to whole pixels (Lukas, 12 October\n * 2026: smoother and subtler). */\n.sp-base-toggle[data-lift=\"on\"] > svg:first-child {\n  animation: sp-toggle-lift var(--motion-duration-slow)\n    var(--motion-ease-soft-in-out);\n  transform-origin: 50% 100%;\n  will-change: transform;\n}\n\n@keyframes sp-toggle-lift {\n  45% {\n    transform: translateY(-1.5px) rotate(-6deg);\n  }\n}\n\nhtml[data-motion-reduce=\"true\"]\n  .sp-base-toggle[data-lift=\"on\"]\n  > svg:first-child,\n[data-sp-motion=\"reduced\"] .sp-base-toggle[data-lift=\"on\"] > svg:first-child {\n  animation: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sp-base-toggle[data-lift=\"on\"] > svg:first-child {\n    animation: none;\n  }\n}\n\n@keyframes sp-icon-pop {\n  from {\n    opacity: 0;\n    transform: scale(0.5);\n  }\n\n  to {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n\n/* A check that draws itself in and out (rulebook M1, M5). Every check of the\n * kit draws through these rules, so a check moves the same everywhere: the\n * Checkbox, the checkbox and radio items of Menu, DropdownMenu, ContextMenu and\n * DrawerMenu, the selected-item checks of Select, Combobox, ListBox and\n * ColorSwatchPicker, the selection mark of a ResourceList row and the selection\n * box of a Table. The path draws in when the indicator that holds the glyph is\n * checked and draws back out when it is not. The indicator is the parent of the\n * glyph: the element that carries data-checked or data-indeterminate (the Base\n * UI Checkbox, Radio and menu items, the indicator of a Table box) or\n * data-selected (the Base UI Select and Combobox item indicators, a swatch, the\n * selection slot of a ResourceList row, the indicator of a Table box) and,\n * while it enters or leaves, data-starting-style and data-ending-style. A glyph\n * that sits deeper inside the element that owns the state, as the check of a\n * ListBox row does, names that element with `sp-check-item`. A glyph\n * with a geometry of its own, as the mark of a ResourceList row and the Table\n * box have, takes pathLength 24 so that the dash of 24 covers its path exactly\n * and the draw runs along its whole length. */\n.sp-check-path-motion,\n.sp-checkbox-indeterminate {\n  opacity: 0;\n  stroke-dasharray: 24;\n  stroke-dashoffset: 24;\n  transition-duration: var(--motion-duration-slow);\n  transition-property: opacity, stroke-dashoffset;\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:is([data-checked], [data-selected]) > svg > .sp-check-path-motion,\n.sp-check-item:is([data-checked], [data-selected]) .sp-check-path-motion,\n[data-indeterminate] > svg > .sp-checkbox-indeterminate {\n  opacity: 1;\n  stroke-dashoffset: 0;\n}\n\n[data-starting-style] > svg > .sp-check-path-motion,\n[data-starting-style] > svg > .sp-checkbox-indeterminate,\n[data-ending-style] > svg > .sp-check-path-motion,\n[data-ending-style] > svg > .sp-checkbox-indeterminate {\n  opacity: 0;\n  stroke-dashoffset: 24;\n}\n\n/* The glyph of a row also pops with its check, as the indicator of the\n * Checkbox does: it grows from --motion-check-pop-scale as the check draws in\n * and shrinks back as it draws out. The Checkbox and Radio pop their whole\n * indicator instead (checkbox.css, radio-group.css), with the same token. */\n.sp-check-glyph-motion {\n  transform: scale(var(--motion-check-pop-scale));\n  transform-origin: center;\n  transition-duration: var(--motion-duration-slow);\n  transition-property: transform;\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:is([data-checked], [data-selected]) > .sp-check-glyph-motion,\n.sp-check-item:is([data-checked], [data-selected]) .sp-check-glyph-motion {\n  transform: none;\n}\n\n[data-starting-style] > .sp-check-glyph-motion,\n[data-ending-style] > .sp-check-glyph-motion {\n  transform: scale(var(--motion-check-pop-scale));\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-check-path-motion,\nhtml[data-motion-reduce=\"true\"] .sp-checkbox-indeterminate,\n[data-sp-motion=\"reduced\"] .sp-check-path-motion,\n[data-sp-motion=\"reduced\"] .sp-checkbox-indeterminate {\n  animation: none;\n  stroke-dashoffset: 0;\n  transition-duration: 1ms;\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-check-glyph-motion,\n[data-sp-motion=\"reduced\"] .sp-check-glyph-motion {\n  transform: none;\n  transition-duration: 1ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sp-check-path-motion,\n  .sp-checkbox-indeterminate {\n    animation: none;\n    stroke-dashoffset: 0;\n    transition-duration: 1ms;\n  }\n\n  .sp-check-glyph-motion {\n    transform: none;\n    transition-duration: 1ms;\n  }\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-floating-panel,\n:where([data-sp-motion=\"reduced\"]) .sp-floating-panel {\n  transition-duration: 1ms;\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-swap-fade,\n:where([data-sp-motion=\"reduced\"]) .sp-swap-fade {\n  transition-duration: 1ms;\n  transition-delay: 0ms;\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-icon-pop,\n:where([data-sp-motion=\"reduced\"]) .sp-icon-pop {\n  animation-duration: 1ms;\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-floating-panel[data-starting-style],\nhtml[data-motion-reduce=\"true\"] .sp-floating-panel[data-ending-style],\n:where([data-sp-motion=\"reduced\"]) .sp-floating-panel[data-starting-style],\n:where([data-sp-motion=\"reduced\"]) .sp-floating-panel[data-ending-style] {\n  transform: none;\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-skeleton-motion,\n:where([data-sp-motion=\"reduced\"]) .sp-skeleton-motion {\n  animation: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .sp-floating-panel {\n    transition-duration: 1ms;\n  }\n\n  .sp-swap-fade {\n    transition-duration: 1ms;\n    transition-delay: 0ms;\n  }\n\n  .sp-floating-panel[data-starting-style],\n  .sp-floating-panel[data-ending-style] {\n    transform: none;\n  }\n\n  .sp-icon-pop {\n    animation-duration: 1ms;\n  }\n\n  .sp-skeleton-motion {\n    animation: none;\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/motion.css"},{"path":"packages/ui/src/system/styles/design-rework.css","content":"/* Canonical Siteplane visual system. */\n:root {\n  color-scheme: light;\n  /* Spacing tokens stay calc() expressions of the base unit. */\n  --sp-rw-space: 0.25rem;\n  --sp-rw-space-0-5: calc(var(--sp-rw-space) * 0.5);\n  --sp-rw-space-1: var(--sp-rw-space);\n  --sp-rw-space-1-5: calc(var(--sp-rw-space) * 1.5);\n  --sp-rw-space-2: calc(var(--sp-rw-space) * 2);\n  --sp-rw-space-2-5: calc(var(--sp-rw-space) * 2.5);\n  --sp-rw-space-3: calc(var(--sp-rw-space) * 3);\n  --sp-rw-space-4: calc(var(--sp-rw-space) * 4);\n  --sp-rw-space-5: calc(var(--sp-rw-space) * 5);\n  --sp-rw-space-6: calc(var(--sp-rw-space) * 6);\n  --sp-rw-space-7: calc(var(--sp-rw-space) * 7);\n  --sp-rw-space-8: calc(var(--sp-rw-space) * 8);\n  --sp-rw-space-9: calc(var(--sp-rw-space) * 9);\n  --sp-rw-space-10: calc(var(--sp-rw-space) * 10);\n  --sp-rw-space-11: calc(var(--sp-rw-space) * 11);\n  --sp-rw-space-12: calc(var(--sp-rw-space) * 12);\n\n  /* Composition contracts: Button owns its icon gap; action owners own theirs. */\n  --sp-rw-button-content-gap: var(--sp-rw-space-1-5);\n  --sp-rw-action-gap: var(--sp-rw-space-2);\n\n  /* Menu row slot (rulebook I8): the inline padding, the icon and the gap to\n   * the label. An icon, a check or radio indicator and the room an inset row\n   * reserves all take it, so every label in a menu starts at the same x. */\n  --sp-rw-menu-row-padding-inline: var(--sp-rw-space-1-5);\n  --sp-rw-menu-row-icon: var(--sp-rw-control-icon);\n  --sp-rw-menu-row-gap: var(--sp-rw-space-2);\n\n  /* The corner radii follow --radius; see \"Corner radius\" below. A pill or a\n   * circle is a shape, not a radius, and keeps the full radius. */\n  --sp-rw-radius-full: 9999px;\n\n  --sp-rw-control-xs: var(--sp-rw-space-6);\n  --sp-rw-control-sm: var(--sp-rw-space-7);\n  --sp-rw-control-md: var(--sp-rw-space-8);\n  --sp-rw-control-lg: var(--sp-rw-space-9);\n  --sp-rw-control-xl: var(--sp-rw-space-12);\n  --sp-rw-hit-target: var(--sp-rw-space-11);\n  /* Choice sizes: the Checkbox and Radio box and the Switch thumb at sm,\n   * default, lg and xl. A Field reads them too (field-choice-indent). */\n  --sp-rw-choice-sm: calc(var(--sp-rw-space) * 3.5);\n  --sp-rw-choice-md: var(--sp-rw-space-4);\n  --sp-rw-choice-lg: calc(var(--sp-rw-space) * 4.5);\n  --sp-rw-choice-xl: var(--sp-rw-space-5);\n\n  /* One icon size per control size: 12 px in the 2xs icon box, 14 px at xs,\n   * 16 px from sm to lg and 20 px at xl. Button, Toggle, Tabs and the menu rows\n   * read them; their labels are 12 px at xs, 14 px from sm to lg and 16 px at\n   * xl. */\n  --sp-rw-control-icon-2xs: var(--sp-rw-space-3);\n  --sp-rw-control-icon-xs: calc(var(--sp-rw-space) * 3.5);\n  --sp-rw-control-icon: var(--sp-rw-space-4);\n  --sp-rw-control-icon-xl: var(--sp-rw-space-5);\n\n  --sp-rw-font-10: 0.625rem;\n  --sp-rw-font-12: 0.75rem;\n  --sp-rw-font-13: 0.8125rem;\n  --sp-rw-font-14: 0.875rem;\n  --sp-rw-font-16: 1rem;\n  --sp-rw-font-18: 1.125rem;\n  --sp-rw-font-20: 1.25rem;\n  --sp-rw-line-16: 1rem;\n  --sp-rw-line-18: 1.125rem;\n  --sp-rw-line-20: 1.25rem;\n  --sp-rw-line-21: 1.3125rem;\n  --sp-rw-line-24: 1.5rem;\n  --sp-rw-line-27: 1.6875rem;\n  --sp-rw-line-30: 1.875rem;\n  /* The typeface is Inter (Lukas, 13 October 2026). An application that uses\n   * another one sets these three after the base import; Theming lists the\n   * values for Wix Madefor Text, Geist, Roboto, DM Sans and the system font. */\n  --sp-rw-font-family-default:\n    \"Inter Variable\", \"Inter\", ui-sans-serif, system-ui, sans-serif;\n  --sp-rw-font-size-default: var(--sp-rw-font-14);\n  --sp-rw-font-weight-default: 400;\n  --sp-rw-letter-spacing-default: -0.015em;\n  --sp-rw-line-height-default: var(--sp-rw-line-21);\n  --sp-semantic-font-family: var(--sp-rw-font-family-default);\n  /* The weights of the text roles above body text; Theming lists their parts\n   * and the values for each typeface. Titles: the Card, Dialog, Sheet, Drawer\n   * and Alert Dialog titles. Labels: field labels, group labels and the small\n   * titles of Alerts, Popovers, Toasts and lists. Strong: semibold emphasis,\n   * such as a Fieldset legend or a Frame title. Body text, controls and\n   * inputs keep --sp-rw-font-weight-default. */\n  --sp-rw-font-weight-title: 450;\n  --sp-rw-font-weight-label: 500;\n  --sp-rw-font-weight-strong: 600;\n\n  --sp-rw-fg-primary: oklch(10% 0 0);\n  --sp-rw-fg-secondary: oklch(30% 0.001 107);\n  --sp-rw-fg-tertiary: oklch(50% 0.001 107);\n  --sp-rw-fg-quaternary: oklch(69% 0.001 107);\n  --sp-rw-fg-inverse: oklch(98.51% 0 89.88);\n  --sp-rw-fg-emphasis: oklch(100% 0 0);\n  /* Text and glyphs on an accent fill: the accent Button and Badge, the\n   * verified Badge, a checked box or radio and a selected calendar day. A light\n   * brand sets it dark; destructive fills keep --sp-rw-fg-emphasis. */\n  --sp-rw-fg-on-accent: var(--sp-rw-fg-emphasis);\n  --sp-rw-fg-accent: oklch(43.93% 0.2025 274.2);\n  --sp-rw-fg-positive: oklch(45.82% 0.1382 138.23);\n  --sp-rw-fg-attention: oklch(49.33% 0.1528 40.04);\n  --sp-rw-fg-destructive: oklch(49.84% 0.197 26.56);\n\n  --sp-rw-bg-base: var(--sp-semantic-color-background);\n  --sp-rw-bg-depth: oklch(95.49% 0 107);\n  --sp-rw-bg-primary: oklch(98.51% 0 107);\n  --sp-rw-bg-secondary: oklch(99.99% 0 107);\n  --sp-rw-bg-tertiary: oklch(99.99% 0 107);\n  --sp-rw-bg-muted: oklch(56.74% 0.009 106.68 / 16%);\n  --sp-rw-bg-translucent: oklch(100% 0 0 / 80%);\n  --sp-rw-bg-inverse: oklch(0% 0 0 / 88%);\n  --sp-rw-bg-accent: oklch(55% 0.227 274.2);\n  --sp-rw-bg-accent-translucent: oklch(55% 0.227 274.2 / 16%);\n  --sp-rw-bg-positive: oklch(54.42% 0.1689 138.23);\n  --sp-rw-bg-positive-translucent: oklch(54.42% 0.1689 138.23 / 16%);\n  --sp-rw-bg-attention: oklch(58.01% 0.1819 40.04);\n  --sp-rw-bg-attention-translucent: oklch(58.01% 0.1819 40.04 / 16%);\n  --sp-rw-bg-destructive: oklch(58.53% 0.2203 26.56);\n  --sp-rw-bg-destructive-translucent: oklch(58.53% 0.2203 26.56 / 16%);\n  /* The amber highlight beside the brand accent. Only the Badge light uses it;\n   * dark themes keep the same value. */\n  --sp-rw-bg-highlight: oklch(80% 0.15 75);\n  --sp-rw-bg-scrim: var(--sp-semantic-color-overlay-backdrop);\n  --sp-rw-progress-track: hsl(0 0% 80.8%);\n  /* A deeper accent, derived from it, so a brand override reaches progress\n   * (oklch(43.93% 0.2025 274.2) for the default accent). */\n  --sp-rw-progress-fill: oklch(\n    from var(--sp-rw-bg-accent) calc(l - 0.1107) calc(c - 0.0245) h\n  );\n\n  --sp-rw-border-default: 1px;\n  --sp-rw-border-translucent: oklch(55% 0.002 107 / 16%);\n  --sp-rw-border-focus: oklch(61.32% 0.1995 274.2);\n  --sp-rw-border-accent: oklch(61.32% 0.1995 274.2 / 40%);\n  --sp-rw-border-attention: oklch(66.84% 0.1815 40.04 / 40%);\n  --sp-rw-border-destructive: oklch(67.3% 0.2102 26.56 / 40%);\n  --sp-rw-glow-hover: oklch(0% 0 0 / 4%);\n  --sp-rw-glow-pressed: oklch(0% 0 0 / 8%);\n  --sp-rw-glow-on-fill-hover: oklch(0% 0 0 / 8%);\n  --sp-rw-glow-on-fill-pressed: oklch(0% 0 0 / 16%);\n\n  /* Interaction ladders (Lukas, 16 October 2026): the opacity of a Button's\n   * interaction layer. One ladder for rows and ghost controls on a\n   * transparent surface (ghost Buttons, ResourceList rows, the Sidebar and\n   * navigation rows, Table rows), one for the filled default and outline\n   * Buttons. Dark sets its own steps. */\n  --sp-rw-interaction-hover: 0.03;\n  --sp-rw-interaction-pressed: 0.06;\n  --sp-rw-interaction-selected: 0.05;\n  --sp-rw-interaction-on-fill-hover: 0.04;\n  --sp-rw-interaction-on-fill-pressed: 0.07;\n\n  /* Disabled opacity (T1): a disabled element fades once, never compounded.\n   * Fields, choice controls, the Slider and swatches fade by the semantic\n   * --sp-semantic-state-disabled-opacity (0.5); actions, rows, chips and\n   * labels keep their established 0.64; a disabled calendar day and a day\n   * outside the range fade by 0.4. An unavailable day is struck through and\n   * does not fade. */\n  --sp-rw-disabled-opacity-action: 0.64;\n  --sp-rw-disabled-opacity-calendar-day: 0.4;\n\n  --sp-rw-control-depth:\n    0 2px 2px -1px oklch(0% 0 0 / 4%), 0 4px 4px -2px oklch(0% 0 0 / 2%);\n  --sp-rw-control-spotlights:\n    inset 0 2px 0 -1px oklch(100% 0 0),\n    inset 0 -2px 0 -1px oklch(100% 0 0 / 80%);\n  --sp-rw-control-rim: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(0% 0 0 / 16%);\n  /* The hairline of every outline control, the outline Button and the outline\n   * Toggle alike: 25% black in Light, 25% white in Dark. */\n  --sp-rw-outline-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(oklch(0% 0 0 / 25%), oklch(100% 0 0 / 25%));\n  /* The edge of an unchecked Checkbox or Radio box. The edge is the only cue\n   * that the control is there, so it reaches 3:1 against the surfaces it sits\n   * on, from the depth surface to white (WCAG 1.4.11): 48% black in Light, 36%\n   * white in Dark. Fields keep --sp-rw-control-rim; the unchecked Switch track\n   * takes the same grey (its rule below). */\n  --sp-rw-choice-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(oklch(0% 0 0 / 48%), oklch(100% 0 0 / 36%));\n  --sp-rw-control-micro:\n    inset 0 0.5px 0 oklch(0% 0 0 / 8%), inset 0 -0.5px 0 oklch(0% 0 0 / 16%);\n  --sp-rw-control-interaction: oklch(0% 0 0);\n  --sp-rw-control-dome: linear-gradient(\n    in srgb,\n    transparent,\n    oklch(0% 0 0 / 4%)\n  );\n  --sp-rw-control-shadow:\n    var(--sp-rw-control-depth), var(--sp-rw-control-spotlights),\n    var(--sp-rw-control-rim), var(--sp-rw-control-micro);\n  /* The pressed-in depth of a field, below its contour. A Checkbox or Radio box\n   * draws the same depth with the choice rim. */\n  --sp-rw-field-depth:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 2%), 0 1px 0 oklch(100% 0 0);\n  --sp-rw-field-shadow: var(--sp-rw-field-depth), var(--sp-rw-control-rim);\n  --sp-rw-field-focus-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 2%), 0 1px 0 oklch(100% 0 0),\n    inset 0 0 0 var(--sp-rw-border-default) var(--sp-rw-border-focus);\n  --sp-rw-field-invalid-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 2%), 0 1px 0 oklch(100% 0 0),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(67.3% 0.2102 26.56 / 40%);\n  /* One edge strength for every surface: the contour of a card, frame or panel\n   * is 24% in light and a 52% hairline in dark, and the inset and elevated\n   * hairlines are 24% too. It reads on the page canvas and on a surface of its\n   * own colour alike; there is no second strength. */\n  --sp-rw-surface-xs-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(100% 0 0),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-primary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(55% 0.002 107 / 24%),\n    0 2px 1px oklch(0% 0 0 / 4%), 0 2px 1px -0.5px oklch(0% 0 0 / 4%);\n  --sp-rw-surface-sm-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(100% 0 0),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-primary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(55% 0.002 107 / 24%),\n    0 2px 1px oklch(0% 0 0 / 4%), 0 2px 1px -0.5px oklch(0% 0 0 / 4%),\n    0 4px 3px -1.5px oklch(0% 0 0 / 4%);\n  --sp-rw-surface-xl-shadow:\n    var(--sp-rw-surface-sm-shadow), 0 7px 6px -3px oklch(0% 0 0 / 4%),\n    0 13px 12px -6px oklch(0% 0 0 / 4%), 0 25px 24px -12px oklch(0% 0 0 / 4%);\n  /* Pressed-in surface inside another surface: soft inner top shadow and the\n   * native hairline. Shared by both themes. */\n  --sp-rw-surface-inset-shadow:\n    inset 0 1px 2px oklch(0% 0 0 / 6%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 24%);\n  /* Elevated surface: hairline plus a soft all-round shadow that also shows the\n   * top edge, biased only slightly downward. The elevated field draws this same\n   * token. */\n  --sp-rw-surface-elevated-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 24%),\n    0 1px 3px oklch(0% 0 0 / 6%), 0 2px 6px -1px oklch(0% 0 0 / 5%);\n  --sp-rw-floating-md-shadow:\n    0 0 0 var(--sp-rw-border-default) transparent,\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(55% 0.002 107 / 28%),\n    0 0 24px 6px oklch(0% 0 0 / 4%), 0 1.5px 1px oklch(0% 0 0 / 4%),\n    0 1.5px 1px -0.5px oklch(0% 0 0 / 4%),\n    0 3.5px 3px -1.5px oklch(0% 0 0 / 4%), 0 6.5px 6px -3px oklch(0% 0 0 / 4%),\n    0 12.5px 12px -6px oklch(0% 0 0 / 4%),\n    0 24.5px 24px -12px oklch(0% 0 0 / 4%);\n  --sp-rw-floating-lg-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(100% 0 0),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(55% 0.002 107 / 16%),\n    0 0 32px 8px oklch(0% 0 0 / 4%), 0 2px 1px oklch(0% 0 0 / 4%),\n    0 2px 1px -0.5px oklch(0% 0 0 / 4%), 0 4px 3px -1.5px oklch(0% 0 0 / 4%),\n    0 7px 6px -3px oklch(0% 0 0 / 4%), 0 13px 12px -6px oklch(0% 0 0 / 4%),\n    0 25px 24px -12px oklch(0% 0 0 / 4%), 0 49px 48px -24px oklch(0% 0 0 / 4%);\n}\n\n/* SiteplaneUIProvider and its portal scopes add no box of their own, so they\n * never change the flex or grid layout around them. A stylesheet holds it,\n * not a style attribute, which a Content Security Policy may block. The rule\n * has no specificity: a className or style the app gives the provider wins. */\n:where(.sp-siteplane-ui-provider),\n:where(.sp-siteplane-ui-portal-scope) {\n  display: contents;\n}\n\n/* Corner radius. One base, --radius, and every corner of the kit derived from\n * it: the role radii that controls, surfaces and dialogs read, and the\n * --radius-* ladder behind the rounded-* utilities. At the md base of 10 px\n * the roles are 4 (xs), 6 (sm), 8 (control), 12 (surface) and 16 px\n * (dialog), and every radius scales with the base.\n *\n * The root takes SiteplaneUIProvider's md, so an app without a provider looks\n * like one with it. The provider and its portal scopes write data-radius; one\n * with another radius sets its own base and derives the scale again on its\n * element, because a custom property resolves var() where it is declared. An\n * md scope inherits the root's values, so a root override of --radius or of a\n * role token reaches it. The bases match siteplaneRadiusValues. */\n:root {\n  --radius: 0.625rem;\n}\n\n:is(\n  .sp-siteplane-ui-provider,\n  .sp-siteplane-ui-portal-scope\n)[data-radius=\"none\"] {\n  --radius: 0rem;\n}\n\n:is(\n  .sp-siteplane-ui-provider,\n  .sp-siteplane-ui-portal-scope\n)[data-radius=\"sm\"] {\n  --radius: 0.5rem;\n}\n\n/* An md provider inside a provider with another radius returns to md. */\n:is(.sp-siteplane-ui-provider, .sp-siteplane-ui-portal-scope)[data-radius]:not(\n    [data-radius=\"md\"]\n  )\n  :is(\n    .sp-siteplane-ui-provider,\n    .sp-siteplane-ui-portal-scope\n  )[data-radius=\"md\"] {\n  --radius: 0.625rem;\n}\n\n:is(\n  .sp-siteplane-ui-provider,\n  .sp-siteplane-ui-portal-scope\n)[data-radius=\"lg\"] {\n  --radius: 0.875rem;\n}\n\n:is(\n  .sp-siteplane-ui-provider,\n  .sp-siteplane-ui-portal-scope\n)[data-radius=\"xl\"] {\n  --radius: 1.25rem;\n}\n\n:root,\n:is(.sp-siteplane-ui-provider, .sp-siteplane-ui-portal-scope)[data-radius]:not(\n    [data-radius=\"md\"]\n  ),\n:is(.sp-siteplane-ui-provider, .sp-siteplane-ui-portal-scope)[data-radius]:not(\n    [data-radius=\"md\"]\n  )\n  :is(\n    .sp-siteplane-ui-provider,\n    .sp-siteplane-ui-portal-scope\n  )[data-radius=\"md\"] {\n  --sp-rw-radius-xs: calc(var(--radius) * 0.4);\n  --sp-rw-radius-sm: calc(var(--radius) * 0.6);\n  --sp-rw-radius-control: calc(var(--radius) * 0.8);\n  --sp-rw-radius-surface: calc(var(--radius) * 1.2);\n  --sp-rw-radius-dialog: calc(var(--radius) * 1.6);\n  --radius-sm: calc(var(--radius) * 0.6);\n  --radius-md: calc(var(--radius) * 0.8);\n  --radius-lg: var(--radius);\n  --radius-xl: calc(var(--radius) * 1.4);\n  --radius-2xl: calc(var(--radius) * 1.8);\n  --radius-3xl: calc(var(--radius) * 2.2);\n  --radius-4xl: calc(var(--radius) * 2.6);\n  --code-inline-radius: max(0px, calc(var(--radius) - 4px));\n}\n\n/* Glass surface, the same in Light and Dark: a dark card with light text, the\n * combination that reads on light and dark backgrounds alike. A smoked film,\n * light blur with a little more brightness and saturation, a top highlight with\n * a soft specular spot, a bright inner rim, a dark contact hairline and a soft\n * drop shadow.\n *\n * data-glass-tone=\"light\" (a faint white film) and data-glass-ink=\"dark\"\n * change the card and the text. Set them on a glass component, or on any\n * element to change every glass component inside it; \"dark\" and \"light\"\n * return to the defaults inside such an element. */\n:root,\n[data-glass-tone=\"dark\"] {\n  --sp-rw-glass-background: oklab(0 0 0 / 18%);\n  /* The accent's own lightness sits close to the dark film; a lighter accent\n   * keeps a checked switch distinct. It is derived from the accent, so a brand\n   * override reaches it (oklch(63% 0.2 274.2) for the default accent). */\n  --sp-rw-glass-switch-checked: oklch(\n    from var(--sp-rw-bg-accent) calc(l + 0.08) calc(c - 0.027) h\n  );\n  --sp-rw-glass-filter: blur(8px) brightness(1.05) saturate(1.4);\n  --sp-rw-glass-sheen:\n    radial-gradient(140% 90% at 18% 0%, rgb(255 255 255 / 8%), transparent 55%),\n    linear-gradient(180deg, rgb(255 255 255 / 8%) 0%, transparent 100%);\n  --sp-rw-glass-shadow:\n    0 10px 28px -10px rgb(0 0 0 / 14%), 0 2px 6px -2px rgb(0 0 0 / 8.4%),\n    0 0 0 0.5px rgb(0 0 0 / 72%), inset 0 1px 0 rgb(255 255 255 / 36%),\n    inset 0 -0.5px 0 rgb(255 255 255 / 18%),\n    inset 0 0 0 0.5px rgb(255 255 255 / 28%);\n}\n\n[data-glass-tone=\"light\"] {\n  --sp-rw-glass-background: oklab(1 0 0 / 10%);\n  --sp-rw-glass-switch-checked: var(--sp-rw-bg-accent);\n  --sp-rw-glass-filter: blur(7px) saturate(1.3);\n  --sp-rw-glass-sheen:\n    radial-gradient(\n      140% 90% at 18% 0%,\n      rgb(255 255 255 / 11%),\n      transparent 55%\n    ),\n    linear-gradient(180deg, rgb(255 255 255 / 12%) 0%, transparent 100%);\n  --sp-rw-glass-shadow:\n    0 10px 28px -10px rgb(0 0 0 / 10%), 0 2px 6px -2px rgb(0 0 0 / 6%),\n    0 0 0 0.5px rgb(0 0 0 / 12%), inset 0 1px 0 rgb(255 255 255 / 28%),\n    inset 0 -0.5px 0 rgb(255 255 255 / 14%),\n    inset 0 0 0 0.5px rgb(255 255 255 / 31%);\n}\n\n/* The ink also sets the red of destructive text (the lighter red of Dark next\n * to light text: the darker red was too dark on glass) and the colour scheme of\n * the controls on the glass. */\n:root,\n[data-glass-ink=\"light\"] {\n  --sp-rw-glass-foreground: oklch(100% 0 0);\n  --sp-rw-glass-destructive: oklch(76.15% 0.1564 26.56);\n  --sp-rw-glass-scheme: dark;\n}\n\n[data-glass-ink=\"dark\"] {\n  --sp-rw-glass-foreground: oklch(10% 0 0);\n  --sp-rw-glass-destructive: oklch(49.84% 0.197 26.56);\n  --sp-rw-glass-scheme: light;\n}\n\n.dark,\n[data-theme=\"dark\"] {\n  color-scheme: dark;\n  /* Dark is a neutral grey palette without a tint, set by OKLCH lightness: the\n   * page (the semantic background), a deeper depth surface, the panel and the\n   * raised surface, and the primary, secondary, muted and disabled text. The\n   * tertiary surface is a 10% white control fill as it shows on the raised\n   * surface, made opaque, because the default button's fill has no alpha and\n   * stays lighter than every surface. */\n  --sp-rw-fg-primary: oklch(95.21% 0 0);\n  --sp-rw-fg-secondary: oklch(81.18% 0 0);\n  --sp-rw-fg-tertiary: oklch(62.33% 0 0);\n  --sp-rw-fg-quaternary: oklch(100% 0 0 / 35%);\n  --sp-rw-fg-inverse: oklch(23.85% 0.004 106.68);\n  --sp-rw-fg-accent: oklch(70.62% 0.1318 274.2);\n  --sp-rw-fg-positive: oklch(72.92% 0.155 138.23);\n  --sp-rw-fg-attention: oklch(75.82% 0.1594 40.04);\n  --sp-rw-fg-destructive: oklch(76.15% 0.1564 26.56);\n  --sp-rw-border-focus: oklch(55% 0.227 274.2);\n  --sp-rw-border-accent: oklch(55% 0.227 274.2 / 40%);\n  --sp-rw-border-attention: oklch(58.01% 0.1819 40.04 / 40%);\n  --sp-rw-border-destructive: oklch(76.15% 0.1564 26.56 / 40%);\n  --sp-rw-bg-base: var(--sp-semantic-color-background);\n  --sp-rw-bg-depth: oklch(14.96% 0 0);\n  --sp-rw-bg-primary: oklch(21.74% 0 0);\n  --sp-rw-bg-secondary: oklch(24.31% 0 0);\n  --sp-rw-bg-tertiary: oklch(27.2% 0 0);\n  --sp-rw-bg-muted: oklch(56.74% 0.009 106.68 / 8%);\n  /* Translucent, it lightens with the surface below: at 8 % a Select on a Card\n   * or Dialog matches the default Button, as both are the Card's white in\n   * Light (Lukas, 13 October 2026; 16 % put it above the Button). */\n  --sp-rw-bg-translucent: oklch(56.74% 0.009 106.68 / 8%);\n  --sp-rw-bg-inverse: oklch(100% 0 0 / 96%);\n  --sp-rw-bg-accent-translucent: oklch(55% 0.227 274.2 / 20%);\n  --sp-rw-bg-positive-translucent: oklch(54.42% 0.1689 138.23 / 20%);\n  --sp-rw-bg-attention-translucent: oklch(58.01% 0.1819 40.04 / 20%);\n  --sp-rw-bg-destructive-translucent: oklch(58.53% 0.2203 26.56 / 20%);\n  --sp-rw-bg-scrim: var(--sp-semantic-color-overlay-backdrop);\n  --sp-rw-progress-track: hsl(60 3.1% 37.6%);\n  /* A lighter accent, derived from it (oklch(61.32% 0.1995 274.2) for the\n   * default accent). */\n  --sp-rw-progress-fill: oklch(\n    from var(--sp-rw-bg-accent) calc(l + 0.0632) calc(c - 0.0275) h\n  );\n  --sp-rw-glow-hover: oklch(100% 0 0 / 4%);\n  --sp-rw-glow-pressed: oklch(100% 0 0 / 8%);\n  --sp-rw-glow-on-fill-hover: oklch(100% 0 0 / 8%);\n  --sp-rw-glow-on-fill-pressed: oklch(100% 0 0 / 16%);\n  --sp-rw-interaction-hover: 0.035;\n  --sp-rw-interaction-pressed: 0.07;\n  --sp-rw-interaction-selected: 0.06;\n  --sp-rw-interaction-on-fill-hover: 0.06;\n  --sp-rw-interaction-on-fill-pressed: 0.1;\n  --sp-rw-control-depth:\n    0 2px 2px -1px oklch(0% 0 0 / 12%), 0 4px 4px -2px oklch(0% 0 0 / 12%);\n  --sp-rw-control-spotlights:\n    inset 0 2px 0 -1px transparent, inset 0 -2px 0 -1px transparent;\n  --sp-rw-control-rim: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(100% 0 0 / 16%);\n  --sp-rw-control-micro:\n    inset 0 0.5px 0 oklch(100% 0 0 / 16%), inset 0 -0.5px 0 oklch(100% 0 0 / 4%);\n  --sp-rw-control-interaction: oklch(100% 0 0);\n  --sp-rw-control-dome: linear-gradient(\n    in srgb,\n    oklch(100% 0 0 / 4%),\n    transparent\n  );\n  --sp-rw-field-depth:\n    inset 0 1px 0 oklch(100% 0 0 / 8%), inset 0 -1px 0 oklch(0% 0 0 / 8%),\n    0 1px 0 oklch(0% 0 0 / 12%);\n  --sp-rw-field-shadow: var(--sp-rw-field-depth), var(--sp-rw-control-rim);\n  --sp-rw-field-focus-shadow:\n    inset 0 1px 0 oklch(100% 0 0 / 8%), inset 0 -1px 0 oklch(0% 0 0 / 8%),\n    0 1px 0 oklch(0% 0 0 / 12%),\n    inset 0 0 0 var(--sp-rw-border-default) var(--sp-rw-border-focus);\n  --sp-rw-field-invalid-shadow:\n    inset 0 1px 0 oklch(100% 0 0 / 8%), inset 0 -1px 0 oklch(0% 0 0 / 8%),\n    0 1px 0 oklch(0% 0 0 / 12%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(76.15% 0.1564 26.56 / 40%);\n  --sp-rw-surface-xs-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 52%),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(0% 0 0 / 16%),\n    0 2px 0 oklch(0% 0 0 / 8%), 0 2px 1px -0.5px oklch(0% 0 0 / 8%);\n  --sp-rw-surface-sm-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 52%),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(0% 0 0 / 16%),\n    0 2px 0 oklch(0% 0 0 / 8%), 0 2px 1px -0.5px oklch(0% 0 0 / 8%),\n    0 4px 3px -1.5px oklch(0% 0 0 / 8%);\n  --sp-rw-surface-xl-shadow:\n    var(--sp-rw-surface-sm-shadow), 0 7px 6px -3px oklch(0% 0 0 / 8%),\n    0 13px 12px -6px oklch(0% 0 0 / 8%), 0 25px 24px -12px oklch(0% 0 0 / 8%);\n  --sp-rw-surface-elevated-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 52%),\n    0 1px 3px oklch(0% 0 0 / 24%), 0 2px 6px -1px oklch(0% 0 0 / 20%);\n  --sp-rw-floating-md-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 36%),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(0% 0 0 / 28%),\n    0 0 24px 6px oklch(0% 0 0 / 12%), 0 2px 1px oklch(0% 0 0 / 12%),\n    0 2px 1px -0.5px oklch(0% 0 0 / 12%), 0 4px 3px -1.5px oklch(0% 0 0 / 12%),\n    0 7px 6px -3px oklch(0% 0 0 / 12%), 0 13px 12px -6px oklch(0% 0 0 / 12%),\n    0 25px 24px -12px oklch(0% 0 0 / 12%), 0 49px 48px -24px oklch(0% 0 0 / 12%);\n  --sp-rw-floating-lg-shadow:\n    0 0 0 var(--sp-rw-border-default) oklch(55% 0.002 107 / 36%),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-bg-secondary),\n    0 0 0 calc(var(--sp-rw-border-default) * 2) oklch(0% 0 0 / 28%),\n    0 0 32px 8px oklch(0% 0 0 / 12%), 0 2px 1px oklch(0% 0 0 / 12%),\n    0 2px 1px -0.5px oklch(0% 0 0 / 12%), 0 4px 3px -1.5px oklch(0% 0 0 / 12%),\n    0 7px 6px -3px oklch(0% 0 0 / 12%), 0 13px 12px -6px oklch(0% 0 0 / 12%),\n    0 25px 24px -12px oklch(0% 0 0 / 12%), 0 49px 48px -24px oklch(0% 0 0 / 12%);\n}\n\n@media (-webkit-device-pixel-ratio >= 2), (resolution >= 2x) {\n  :root {\n    --sp-rw-border-default: 0.5px;\n    --sp-rw-border-translucent: oklch(55% 0.002 107 / 40%);\n  }\n}\n\n/* Component base (rulebook P6). Zero-specificity defaults that every other\n * kit rule refines and every utility overrides. They come from the\n * components' class lists, where a profile or variant rule below replaced\n * them for some elements; here the kit owns the property for all of them. */\n\n/* Actions: Button, Badge, Toggle. */\n:where([data-chassis]) {\n  position: relative;\n  display: inline-flex;\n  flex-shrink: 0;\n  align-items: center;\n  justify-content: center;\n  white-space: nowrap;\n  cursor: pointer;\n}\n\n/* A disabled control keeps its pointer events, so the pointer shows\n * not-allowed over it; its hover, press and open states are off instead. */\n:where([data-chassis]:disabled),\n:where([data-chassis][aria-disabled=\"true\"]) {\n  cursor: not-allowed;\n}\n\n/* A loading Button is busy rather than unavailable, so the pointer shows\n * progress over it. */\n:where([data-chassis][data-loading]),\n:where([data-chassis][aria-busy=\"true\"]) {\n  cursor: progress;\n}\n\n/* What acts as a button without the chassis shows the chassis cursors: a part\n * whose `render` replaces its Button, a click trigger or a close part that\n * renders its own plain button, and a Sidebar group label rendered as a\n * button. A Toggle rendered in such a part owns its cursors. */\n:where(.sp-accordion-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-alert-dialog-close:not([data-chassis], .sp-base-toggle)),\n:where(.sp-alert-dialog-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-autocomplete-clear:not([data-chassis], .sp-base-toggle)),\n:where(.sp-autocomplete-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-collapsible-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-combobox-chip-remove:not([data-chassis], .sp-base-toggle)),\n:where(.sp-combobox-clear:not([data-chassis], .sp-base-toggle)),\n:where(.sp-combobox-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-dialog-close:not([data-chassis], .sp-base-toggle)),\n:where(.sp-dialog-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-drawer-close:not([data-chassis], .sp-base-toggle)),\n:where(.sp-drawer-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-menu-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-number-field-decrement:not([data-chassis], .sp-base-toggle)),\n:where(.sp-number-field-increment:not([data-chassis], .sp-base-toggle)),\n:where(.sp-popover-close:not([data-chassis], .sp-base-toggle)),\n:where(.sp-popover-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-select-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-sheet-close:not([data-chassis], .sp-base-toggle)),\n:where(.sp-sheet-trigger:not([data-chassis], .sp-base-toggle)),\n:where(.sp-tabs-tab:not([data-chassis], .sp-base-toggle)),\n:where(.sp-toolbar-button:not([data-chassis], .sp-base-toggle)),\n:where(\n  [data-sidebar=\"group-label\"]:is(button, [role=\"button\"]):not(\n      [data-chassis],\n      .sp-base-toggle\n    )\n) {\n  cursor: pointer;\n}\n\n:where(\n  .sp-accordion-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-alert-dialog-close:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-alert-dialog-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-autocomplete-clear:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-autocomplete-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-collapsible-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-combobox-chip-remove:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-combobox-clear:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-combobox-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-dialog-close:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-dialog-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-drawer-close:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-drawer-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-menu-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-number-field-decrement:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-number-field-increment:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-popover-close:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-popover-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-select-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-sheet-close:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-sheet-trigger:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-tabs-tab:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  .sp-toolbar-button:not([data-chassis], .sp-base-toggle):is(\n      :disabled,\n      [aria-disabled=\"true\"]\n    )\n),\n:where(\n  [data-sidebar=\"group-label\"]:is(button, [role=\"button\"]):not(\n      [data-chassis],\n      .sp-base-toggle\n    ):is(:disabled, [aria-disabled=\"true\"])\n) {\n  cursor: not-allowed;\n}\n\n/* A tab whose render replaces its Button keeps the chassis' place in its list:\n * positioned, at its own width and on one line. */\n:where(.sp-tabs-tab:not([data-chassis])) {\n  position: relative;\n  flex-shrink: 0;\n  white-space: nowrap;\n}\n\n:where([data-chassis])::before {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  content: \"\";\n  pointer-events: none;\n}\n\n:where([data-chassis][data-align=\"start\"]) {\n  justify-content: flex-start;\n  text-align: left;\n}\n\n:where([data-chassis][data-align=\"between\"]) {\n  justify-content: space-between;\n  text-align: left;\n}\n\n:where([data-chassis][data-multiline]) {\n  white-space: normal;\n}\n\n:where([data-chassis][data-width=\"full\"]) {\n  width: 100%;\n}\n\n:where([data-chassis] svg) {\n  margin-inline: calc(var(--sp-rw-space-0-5) * -1);\n}\n\n:where([data-chassis] svg:not([class*=\"size-\"])) {\n  width: var(--sp-rw-button-icon, var(--sp-rw-control-icon));\n  height: var(--sp-rw-button-icon, var(--sp-rw-control-icon));\n}\n\n:where([data-chassis] svg:not([class*=\"opacity-\"])) {\n  opacity: 0.8;\n}\n\n/* A badge is never wider than the space it sits in; its label ends with an\n * ellipsis there (rulebook K5). */\n:where(.sp-base-badge) {\n  position: relative;\n  display: inline-flex;\n  max-width: 100%;\n  transition-property: box-shadow;\n  transition-timing-function: var(--motion-ease-state);\n  transition-duration: var(--motion-duration-fast);\n}\n\n/* The text label of a badge (badge.tsx gathers the text children in one), and\n * of a Combobox chip, which follows the outline badge's grammar, stays on one\n * line and ends with an ellipsis where the badge meets the edge of its space\n * (rulebook K5). It wraps the way the text around the badge does.\n * In a context that wraps, such as a card, a list or a Tag Group, it may break\n * between any two characters, so a long label never makes its container wider\n * than the space it has, and the clamp shows its first line, filled to the\n * edge, with the ellipsis. In a context that keeps text on one line, such as a\n * table cell, a tab or a button, the label keeps its full width like the text\n * beside it. The label's line box is taller than its glyphs, so the clip keeps\n * the descenders of g, j, p, q and y whole; the badge centres the label, so\n * its text sits where it did. */\n:where(.sp-badge-label),\n:where(.sp-combobox-chip-label) {\n  display: -webkit-box;\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  word-break: break-all;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: 1;\n  line-height: 1.5;\n}\n\n:where(.sp-base-badge svg:not([class*=\"size-\"])) {\n  width: calc(var(--sp-rw-space) * 3.5);\n  height: calc(var(--sp-rw-space) * 3.5);\n}\n\n@media (width >= 40rem) {\n  :where(.sp-base-badge svg:not([class*=\"size-\"])) {\n    width: var(--sp-rw-space-3);\n    height: var(--sp-rw-space-3);\n  }\n}\n\n:where(.sp-base-badge svg:not([class*=\"opacity-\"])) {\n  opacity: 0.8;\n}\n\n:where(.sp-base-toggle) {\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  cursor: pointer;\n  transition-property: box-shadow;\n  transition-timing-function: var(--motion-ease-state);\n  transition-duration: var(--motion-duration-fast);\n}\n\n/* A disabled Toggle shows the cursor and the fade of a disabled action. It is\n * disabled by the `disabled` attribute, its own or that of a fieldset around\n * it, or by aria-disabled where a Toolbar keeps it in the tab order. Its\n * hover and press rules exclude the same two. */\n:where(.sp-base-toggle:is(:disabled, [aria-disabled=\"true\"])) {\n  cursor: not-allowed;\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n:where(.sp-base-toggle)::before {\n  inset: 0;\n  border-radius: inherit;\n}\n\n:where(.sp-base-toggle svg) {\n  pointer-events: none;\n}\n\n:where(.sp-base-toggle svg:not([class*=\"size-\"])) {\n  width: var(--sp-rw-control-icon);\n  height: var(--sp-rw-control-icon);\n}\n\n:where(.sp-base-toggle[data-size=\"xl\"] svg:not([class*=\"size-\"])) {\n  width: var(--sp-rw-control-icon-xl);\n  height: var(--sp-rw-control-icon-xl);\n}\n\n/* Forms. */\n/* The cursor of a label: a pointer over a label that a click passes to its\n * control, the default over a Select's label, and the resize cursor of a\n * Number Field's scrub area over the label in it. A disabled label takes the\n * not-allowed cursor of the labels' shared disabled rule. */\n:where(.sp-base-label),\n:where(.sp-field-label) {\n  cursor: pointer;\n}\n\n:where(.sp-select-label) {\n  cursor: default;\n}\n\n:where(.sp-number-field-scrub-area > .sp-base-label) {\n  cursor: inherit;\n}\n\n/* The field text sizes of surface.ts (formControlTextClassNames). */\n:where(.sp-field-text-base) {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--text-base--line-height);\n}\n\n:where(.sp-field-text-sm) {\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--text-sm--line-height);\n}\n\n/* The native control of an Input fills its field. */\n:where(.sp-input-control > *) {\n  height: 100%;\n  border-radius: inherit;\n  padding-inline: calc(var(--sp-rw-space-3) - 1px);\n  line-height: var(--sp-rw-space-7);\n}\n\n:where(.sp-input-control[data-size=\"sm\"] > *) {\n  padding-inline: calc(var(--sp-rw-space-2-5) - 1px);\n  line-height: var(--sp-rw-space-6);\n}\n\n:where(.sp-input-control[data-size=\"xl\"] > *) {\n  padding-inline: calc(var(--sp-rw-space-4) - 1px);\n}\n\n:where(.sp-input-control > *)::placeholder {\n  color: color-mix(\n    in oklab,\n    var(--sp-semantic-color-muted-foreground) 72%,\n    transparent\n  );\n}\n\n:where(.sp-date-input) {\n  cursor: text;\n}\n\n:where(.sp-input-control > :disabled) {\n  cursor: not-allowed;\n}\n\n:where(.sp-textarea) {\n  border-radius: inherit;\n  resize: vertical;\n}\n\n/* Every kit textarea, plain or in an InputGroup, grows with its content where\n * the browser sizes a field to its content, so it shows no resize grip there:\n * a dragged height would only stop the growth. Where the browser cannot, the\n * textarea keeps its height and the vertical grip above. */\n@supports (field-sizing: content) {\n  :where(.sp-textarea) {\n    resize: none;\n  }\n}\n\n/* An InputGroup with an addon above or below its control grows with it. */\n:where(\n  .sp-base-input-group:has([data-align=\"block-start\"], [data-align=\"block-end\"])\n) {\n  height: auto;\n}\n\n/* InputGroup addons: the text cursor, a mark centred in a tight line box,\n * their place by alignment, and the inline and block padding of an addon above\n * or below the control. */\n:where(.sp-input-group-addon) {\n  align-items: center;\n  cursor: text;\n  line-height: 1;\n}\n\n/* Beside a textarea, an inline addon centres its mark on the first text line\n * (I5), not on the middle of the growing field: it starts at the textarea's\n * top padding, which follows the textarea's size (textarea.css and the\n * Textarea rules below), and takes the field's text line. */\n:where(\n  .sp-base-input-group:has(> .sp-textarea-control)\n    > .sp-input-group-addon:is(\n      [data-align=\"inline-start\"],\n      [data-align=\"inline-end\"]\n    )\n) {\n  align-items: flex-start;\n  padding-block-start: calc(var(--spacing) * 2 - 1px);\n  line-height: inherit;\n}\n\n:where(\n  .sp-base-input-group:has(> .sp-textarea-control[data-size=\"sm\"])\n    > .sp-input-group-addon:is(\n      [data-align=\"inline-start\"],\n      [data-align=\"inline-end\"]\n    )\n) {\n  padding-block-start: var(--sp-rw-space-1);\n}\n\n:where(\n  .sp-base-input-group:has(> .sp-textarea-control[data-size=\"lg\"])\n    > .sp-input-group-addon:is(\n      [data-align=\"inline-start\"],\n      [data-align=\"inline-end\"]\n    )\n) {\n  padding-block-start: var(--sp-rw-space-2-5);\n}\n\n:where(\n  .sp-base-input-group:has(> .sp-textarea-control[data-size=\"xl\"])\n    > .sp-input-group-addon:is(\n      [data-align=\"inline-start\"],\n      [data-align=\"inline-end\"]\n    )\n) {\n  padding-block-start: var(--sp-rw-space-3);\n}\n\n/* A Kbd or KbdGroup in an addon; the Kbd rule still rounds a single key. */\n:where(.sp-input-group-addon > kbd) {\n  border-radius: calc(var(--radius) - 5px);\n}\n\n:where(\n  .sp-input-group-addon:is(\n    [data-align=\"inline-start\"],\n    [data-align=\"block-start\"]\n  )\n) {\n  order: -9999;\n}\n\n:where(\n  .sp-input-group-addon:is([data-align=\"inline-end\"], [data-align=\"block-end\"])\n) {\n  order: 9999;\n}\n\n:where(\n  .sp-input-group-addon:is([data-align=\"block-start\"], [data-align=\"block-end\"])\n) {\n  padding-inline: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:where(.sp-input-group-addon[data-align=\"block-start\"]) {\n  padding-top: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:where(.sp-input-group-addon[data-align=\"block-start\"].border-b) {\n  padding-bottom: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:where(.sp-input-group-addon[data-align=\"block-end\"]) {\n  padding-bottom: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:where(.sp-input-group-addon[data-align=\"block-end\"].border-t) {\n  padding-top: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:where(\n  [data-size=\"sm\"]\n    + .sp-input-group-addon:is(\n      [data-align=\"block-start\"],\n      [data-align=\"block-end\"]\n    )\n) {\n  padding-inline: calc(var(--sp-rw-space-2-5) - 1px);\n}\n\n/* FieldLabel. A label of text alone lays out as a block, so a required\n * asterisk follows its last word when it wraps. Its colour and disabled state\n * are the shared label rules (Input, Textarea and Label section). */\n:where(.sp-field-label) {\n  display: inline-flex;\n  font-size: var(--sp-rw-font-16);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-18);\n}\n\n@media (width >= 40rem) {\n  :where(.sp-field-label) {\n    font-size: var(--sp-rw-font-14);\n    line-height: var(--sp-rw-line-16);\n  }\n}\n\n:where(.sp-field-label:not(:has(> *))) {\n  display: block;\n}\n\n/* Labels and descriptions wrap a long unbroken value, such as a URL or a\n * German compound, within the field's width (rulebook K5). */\n:where(.sp-base-label),\n:where(.sp-field-label),\n:where(.sp-field-description) {\n  overflow-wrap: anywhere;\n}\n\n:where(.sp-select-value[data-placeholder]) {\n  color: var(--sp-semantic-color-muted-foreground);\n}\n\n/* The trigger and clear action of a Combobox input and the trigger of an\n * Autocomplete input; the Autocomplete clear takes end-1 in its class merge. */\n:where(\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group)\n    > .sp-combobox-trigger\n),\n:where(\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group)\n    > .sp-combobox-clear\n),\n:where(\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group)\n    > .sp-autocomplete-trigger\n) {\n  inset-inline-end: var(--sp-rw-space-1);\n}\n\n:where(.sp-combobox-chips) {\n  gap: var(--sp-rw-space-1);\n  color: var(--sp-semantic-color-foreground);\n}\n\n/* Standalone chips, a row without an input, fade as a whole while a remove\n * action in them is disabled; chips in an input fade with that input (the\n * field rules below). */\n:where(.sp-combobox-chips[data-layout=\"standalone\"]:has(:disabled)) {\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n/* The dot of a Radio. */\n:where(.sp-base-radio[data-profile=\"control\"] > .sp-radio-indicator)::before {\n  content: \"\";\n}\n\n/* Overlays, menus and lists. */\n\n/* The opaque surface of a floating panel (surface.ts): the popover fill, a\n * 1 px edge, a soft shadow and a hairline highlight on the ::before layer.\n * Popover, Menu, Select, Drawer, Sheet and the picker popovers draw their own\n * surface over it, and Toast and Notification wear the borderless floating\n * surface instead; Combobox and PreviewCard keep it. The shadow lists\n * are written out as the shadow utilities computed them, empty layers\n * included. */\n:where(.sp-floating-surface) {\n  border-width: 1px;\n  border-style: solid;\n  background-color: var(--sp-semantic-color-popover);\n  box-shadow:\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 10px 15px -3px oklab(0% 0 0 / 0.05),\n    0 4px 6px -4px oklab(0% 0 0 / 0.05);\n  color: var(--sp-semantic-color-popover-foreground);\n}\n\n:where(\n  .sp-floating-surface:not(\n    :where(.dark, .dark *, [data-theme=\"dark\"], [data-theme=\"dark\"] *)\n  )\n) {\n  background-clip: padding-box;\n}\n\n:where(.sp-floating-surface)::before {\n  box-shadow:\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 1px oklab(0% 0 0 / 0.04);\n}\n\n:where(\n  .sp-floating-surface:is(\n    .dark,\n    .dark *,\n    [data-theme=\"dark\"],\n    [data-theme=\"dark\"] *\n  )\n)::before {\n  box-shadow:\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 0 #0000,\n    0 -1px color-mix(in oklab, var(--color-white) 6%, transparent);\n}\n\n/* A Popover popup takes the width Base UI measures; the Calendar and\n * TimeField popovers set their own. */\n:where(.sp-base-popover) {\n  width: var(--popup-width, auto);\n}\n\n/* The current and the previous content of a Popover or Tooltip viewport\n * (Base UI) cross-fade at the popup's inner width over the overlay time:\n * its width less the viewport's inline padding and the popup's edge on each\n * side, the 1px border of a Popover; a Tooltip draws its edge as a shadow and\n * sets --viewport-edge-width to 0. Content with a width of its own, such as\n * the Calendar picker, keeps it. The reduced-motion rules below make the swap\n * instant (M5). */\n:where(:is(.sp-popover-viewport, .sp-tooltip-viewport) [data-current]),\n:where(:is(.sp-popover-viewport, .sp-tooltip-viewport) [data-previous]) {\n  width: calc(\n    var(--popup-width) - 2 * var(--viewport-inline-padding) - 2 *\n      var(--viewport-edge-width, 1px)\n  );\n  opacity: 1;\n  transition-property: opacity;\n  transition-timing-function: var(--motion-ease-standard);\n  transition-duration: var(--motion-duration-overlay);\n}\n\n:where(\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n    [data-current]:is([data-starting-style], [data-ending-style])\n),\n:where(\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n    [data-previous]:is([data-starting-style], [data-ending-style])\n) {\n  opacity: 0;\n}\n\n/* Reduced motion: the content swaps at once. The document mode, the provider\n * preset (the popup's portal scope carries it) and the system setting each\n * switch it (M5). */\nhtml[data-motion-reduce=\"true\"]\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n  [data-current],\nhtml[data-motion-reduce=\"true\"]\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n  [data-previous],\n[data-sp-motion=\"reduced\"]\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n  [data-current],\n[data-sp-motion=\"reduced\"]\n  :is(.sp-popover-viewport, .sp-tooltip-viewport)\n  [data-previous] {\n  transition-duration: 1ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root :is(.sp-popover-viewport, .sp-tooltip-viewport) [data-current],\n  :root :is(.sp-popover-viewport, .sp-tooltip-viewport) [data-previous] {\n    transition-duration: 1ms;\n  }\n}\n\n/* Dialog and AlertDialog: the viewport is a grid until the wide-screen rule\n * centres the popup with flexbox. A popup behind a nested dialog stays\n * visible and steps back, 10% smaller per nested dialog on a wide screen and\n * 10% less opaque (surface.ts), and it scales in from 98% on a wide screen;\n * at the bottom edge of a small screen it slides up 16 px. The popup fades,\n * scales and slides over the modal time, and their backdrop fades over the\n * overlay time. These rules are the one owner of that motion (surface.ts\n * names the classes), so Dialog, AlertDialog and the dialogs built on them\n * move alike; the reduced-motion rules below make it instant (M5). */\n:where(.sp-dialog-viewport),\n:where(.sp-alert-dialog-viewport) {\n  display: grid;\n}\n\n/* The popup widths of Dialog and AlertDialog on the modal scale (S5): their\n * `size` prop writes data-width, xs 400, sm 440 (the default), md 600 and lg\n * 880 px; ModalBlock passes its `width` on as that size. Not data-size, which\n * the rules of grouped controls read on an ancestor. The widths apply from the\n * sm breakpoint up, and a smaller screen shows the full-width bottom sheet at\n * every size. Without specificity, so a max-width utility in a popup's\n * className wins (P6). */\n@media (width >= 40rem) {\n  :where(:is(.sp-dialog-popup, .sp-alert-dialog-popup)[data-width=\"xs\"]) {\n    max-width: 400px;\n  }\n\n  :where(:is(.sp-dialog-popup, .sp-alert-dialog-popup)[data-width=\"sm\"]) {\n    max-width: 440px;\n  }\n\n  :where(:is(.sp-dialog-popup, .sp-alert-dialog-popup)[data-width=\"md\"]) {\n    max-width: 600px;\n  }\n\n  :where(:is(.sp-dialog-popup, .sp-alert-dialog-popup)[data-width=\"lg\"]) {\n    max-width: 880px;\n  }\n}\n\n:where(.sp-dialog-motion) {\n  transition-property: scale, opacity, translate;\n  transition-duration: var(--motion-duration-modal);\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:where(.sp-overlay-backdrop-motion) {\n  transition-property: opacity;\n  transition-duration: var(--motion-duration-overlay);\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:where(\n  .sp-overlay-backdrop-motion:is([data-starting-style], [data-ending-style])\n) {\n  opacity: 0;\n}\n\n@media (width >= 40rem) {\n  :where(.sp-dialog-motion) {\n    scale: calc(1 - 0.1 * var(--nested-dialogs));\n  }\n\n  :where(.sp-dialog-motion:is([data-starting-style], [data-ending-style])) {\n    scale: 98% 98%;\n  }\n}\n\n@media (width < 40rem) {\n  :where(\n    .sp-dialog-mobile-stick:is([data-starting-style], [data-ending-style])\n  ) {\n    translate: 0 var(--sp-rw-space-4);\n  }\n}\n\n/* Reduced motion: the popup and the backdrop appear and leave at once, at the\n * popup's final size and place. The document mode, the provider preset (a\n * portal scope carries it) and the system setting each switch it (M5). */\nhtml[data-motion-reduce=\"true\"] .sp-dialog-motion,\nhtml[data-motion-reduce=\"true\"] .sp-overlay-backdrop-motion,\n[data-sp-motion=\"reduced\"] .sp-dialog-motion,\n[data-sp-motion=\"reduced\"] .sp-overlay-backdrop-motion {\n  transition-duration: 1ms;\n}\n\nhtml[data-motion-reduce=\"true\"]\n  .sp-dialog-motion:is([data-starting-style], [data-ending-style]),\n[data-sp-motion=\"reduced\"]\n  .sp-dialog-motion:is([data-starting-style], [data-ending-style]) {\n  scale: 1;\n  translate: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-dialog-motion,\n  :root .sp-overlay-backdrop-motion {\n    transition-duration: 1ms;\n  }\n\n  :root .sp-dialog-motion:is([data-starting-style], [data-ending-style]) {\n    scale: 1;\n    translate: none;\n  }\n}\n\n/* Group labels of Menu, ContextMenu and DrawerMenu. The inset rule and the\n * DrawerMenu touch size refine them. */\n:where(.sp-menu-label),\n:where(.sp-context-menu-label),\n:where(.sp-drawer-menu-group-label) {\n  padding: var(--sp-rw-space-2);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--text-sm--line-height);\n}\n\n/* A check, radio or switch row takes the accent fill while it is hovered,\n * focused or highlighted, with the primary text of a highlighted menu row. The\n * menu-row state rules below replace it while a Menu or ContextMenu row is\n * highlighted and while a DrawerMenu row is hovered. */\n@media (hover: hover) {\n  :where(\n    .sp-choice-menu-item:not(\n        [data-disabled],\n        :disabled,\n        [aria-disabled=\"true\"]\n      ):hover\n  ) {\n    background-color: var(--sp-semantic-color-accent);\n    color: var(--sp-rw-fg-primary);\n  }\n}\n\n:where(\n  .sp-choice-menu-item:not(\n      [data-disabled],\n      :disabled,\n      [aria-disabled=\"true\"]\n    ):is(:focus-visible, [data-highlighted])\n) {\n  background-color: var(--sp-semantic-color-accent);\n  color: var(--sp-rw-fg-primary);\n}\n\n/* ListBox: the options stack 4 px apart across its width, and the options\n * show the focus. TimeField lays out its columns itself (time-field.css). */\n:where(.sp-list-box) {\n  display: grid;\n  width: 100%;\n  gap: var(--sp-rw-space-1);\n  outline-style: none;\n}\n\n/* Toast: the stack glides and a loading icon spins. The reduced-motion rules\n * of toast.css stop both (M5). */\n:where(.sp-toast-motion) {\n  transition:\n    transform var(--motion-duration-linger) var(--motion-ease-standard),\n    opacity var(--motion-duration-linger) var(--motion-ease-navigation),\n    height var(--motion-duration-fast) var(--motion-ease-navigation),\n    background-color var(--motion-duration-linger) var(--motion-ease-navigation);\n}\n\n:where(.sp-toast-viewport[data-stacked]) {\n  transition-property: transform, translate, scale, rotate;\n  transition-timing-function: var(--motion-ease-standard);\n  transition-duration: var(--motion-duration-linger);\n}\n\n/* Display and navigation. */\n\n/* Alert: one text column, which wraps a long value (K5); the Alert rules add\n * the icon column and, from 640 px, the action column. */\n:where(.sp-alert) {\n  grid-template-columns: minmax(0, 1fr);\n}\n\n/* Header Navigation: the page background. Its own bg-background utility beat\n * the glass class a consumer adds. */\n:where(.sp-header-navigation) {\n  background-color: var(--color-background);\n}\n\n/* Card: the radius of a Card that renders as another part and takes that\n * part's slot (a ContextMenu trigger); the surface rule rounds every Card that\n * keeps its own slot. Both read the surface radius, as do the corners that a\n * CardFrame clips its joined cards to. */\n:where(.sp-base-card) {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* Card header: its parts start at the top of their row, and a media\n * (CardMedia) and an action (CardAction) centre on theirs. The Card section\n * refines both by the number of text parts the header holds. */\n:where(.sp-card-header) {\n  align-items: flex-start;\n}\n\n:where(.sp-card-media),\n:where(.sp-card-action) {\n  align-self: center;\n}\n\n/* Stepper: a marker and its title ease their colours between steps; the\n * marker's ring fades in on hover and for the current step. */\n:where(.sp-stepper-indicator) {\n  transition-property: color, background-color, border-color;\n  transition-timing-function: var(--motion-ease-standard);\n  transition-duration: var(--motion-duration-base);\n}\n\n:where(.sp-stepper-indicator)::after {\n  border-color: transparent;\n  box-shadow: 0 0 0 1px transparent;\n  transition-property: opacity, transform, border-color;\n}\n\n/* A marker's check or lock is 14 px; in a step trigger the Button sizes it. */\n:where(.sp-stepper-indicator:not([data-chassis] *) svg) {\n  width: calc(var(--sp-rw-space) * 3.5);\n  height: calc(var(--sp-rw-space) * 3.5);\n}\n\n:where(.sp-stepper-title) {\n  transition-property:\n    color, background-color, border-color, outline-color, text-decoration-color,\n    fill, stroke;\n  transition-timing-function: var(--motion-ease-standard);\n  transition-duration: var(--motion-duration-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :where(.sp-stepper-indicator),\n  :where(.sp-stepper-title) {\n    transition-property: none;\n  }\n}\n\n/* Pagination: the active indicator glides to the current page. */\n:where(.sp-pagination-content)::before {\n  transition-property: width, height, translate, opacity;\n  transition-timing-function: var(--motion-ease-soft-in-out);\n  transition-duration: var(--motion-duration-base);\n}\n\n:where([data-animate=\"false\"] .sp-pagination-content)::before {\n  transition-property: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :where(.sp-pagination-content)::before {\n    transition-property: none;\n  }\n}\n\n/* Tabs: a tab shows the focus outline of its Button, not the browser's. */\n:where(.sp-tabs-tab) {\n  outline-style: none;\n}\n\n/* Table and NativeTable: a row ends with a 1 px edge; table.css thins it\n * between body rows and drops it after the last one. */\n:where(.sp-table-row) {\n  border-bottom-width: 1px;\n}\n\n/* ResourceList: a row and the header keep 10 px above and below their\n * content, and metadata is muted text at the rows' size. A compact list\n * refines both for its own rows; a selected row lifts the metadata (see\n * the ResourceList block). */\n:where(.sp-resource-list-item),\n:where(.sp-resource-list-header) {\n  padding-block: var(--sp-rw-space-2-5);\n}\n\n:where(.sp-resource-list-meta) {\n  color: var(--sp-semantic-color-muted-foreground);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--text-sm--line-height);\n}\n\n/* Separator: a 1 px line in the border colour, across its container when\n * horizontal and, without a height class of its own, along it when vertical.\n * The separator, Toolbar and OTP Field rules further down refine it; a\n * separator in a slot no rule names (the Select, Autocomplete and Sidebar\n * ones) keeps this line. */\n:where(.sp-base-separator) {\n  background-color: var(--sp-semantic-color-border);\n}\n\n:where(.sp-base-separator[data-orientation=\"horizontal\"]) {\n  width: 100%;\n  height: 1px;\n}\n\n:where(\n  .sp-base-separator[data-orientation=\"vertical\"]:not(\n      [class^=\"h-\"],\n      [class*=\" h-\"]\n    )\n) {\n  align-self: stretch;\n}\n\n/* A Group separator after a focused field slides under the field's focus\n * ring; beside group text the Group rules keep it in place. */\n:where(\n  :is(.sp-input-control:focus-within, .sp-input-group:focus-within)\n    + .sp-base-separator[data-profile=\"group\"]\n),\n:where(\n  .sp-select-trigger:focus-visible\n    + *\n    + .sp-base-separator[data-profile=\"group\"]\n),\n:where(\n  .sp-number-field:focus-within\n    + input\n    + .sp-base-separator[data-profile=\"group\"]\n) {\n  translate: -1px;\n}\n\n/* Group: joined children drop their edge and corners where they meet; the\n * Group rules further down give the outer corners back. A part that another\n * part follows is :nth-last-child(n + 2 of [data-slot]), not\n * :has(~ [data-slot]): the browser tries the rule on every part, and the\n * sibling walk of :has() made it the costliest kit rule (F-08). */\n:where(\n  .sp-group:not([data-orientation=\"vertical\"])\n    > [data-slot]:nth-last-child(n + 2 of [data-slot])\n) {\n  border-inline-end-width: 0;\n  border-start-end-radius: 0;\n  border-end-end-radius: 0;\n}\n\n:where(\n  .sp-group:not([data-orientation=\"vertical\"]) > [data-slot] ~ [data-slot]\n) {\n  border-inline-start-width: 0;\n  border-start-start-radius: 0;\n  border-end-start-radius: 0;\n}\n\n:where(\n  .sp-group[data-orientation=\"vertical\"]\n    > [data-slot]:nth-last-child(n + 2 of [data-slot])\n) {\n  border-bottom-width: 0;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n:where(.sp-group[data-orientation=\"vertical\"] > [data-slot] ~ [data-slot]) {\n  border-top-width: 0;\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n}\n\n/* ScrollArea: the root fills its container; a size utility or a rule of its\n * consumer (the Time Field's) replaces either dimension. */\n:where(.sp-base-scroll-area) {\n  width: 100%;\n  height: 100%;\n  min-height: 0;\n}\n\n/* Base UI hides the native scrollbar of a ScrollArea viewport, and of a Select\n * popup aligned with its trigger, through this class. SiteplaneUIProvider turns\n * off the \\3c style> element Base UI would add for it (CSPProvider\n * disableStyleElements), so a Content Security Policy needs no nonce for it;\n * these are Base UI's two rules. */\n.base-ui-disable-scrollbar {\n  scrollbar-width: none;\n}\n\n.base-ui-disable-scrollbar::-webkit-scrollbar {\n  display: none;\n}\n\n/* Avatar: a 32 px circle. In an avatar Button it fills the control instead\n * (button.css). */\n:where(.sp-avatar) {\n  width: var(--sp-rw-space-8);\n  height: var(--sp-rw-space-8);\n  border-radius: calc(infinity * 1px);\n}\n\n/* AvatarGroup: a row of overlapping avatars. The group's size (sm 24, md 32,\n * lg 40 and xl 48 px) sizes every avatar and the overflow count through the\n * ring rule of the Badge section, and each item after the first, the count\n * included, overlaps the one before it by a quarter of that size. Later items\n * sit on top of earlier ones. */\n:where(.sp-avatar-group) {\n  --avatar-group-size: var(--sp-rw-space-8);\n\n  display: flex;\n  align-items: center;\n}\n\n:where(.sp-avatar-group[data-size=\"sm\"]) {\n  --avatar-group-size: var(--sp-rw-space-6);\n}\n\n:where(.sp-avatar-group[data-size=\"lg\"]) {\n  --avatar-group-size: var(--sp-rw-space-10);\n}\n\n:where(.sp-avatar-group[data-size=\"xl\"]) {\n  --avatar-group-size: var(--sp-rw-space-12);\n}\n\n:where(.sp-avatar-group > * + *) {\n  margin-inline-start: calc(var(--avatar-group-size) / -4);\n}\n\n/* Sidebar: the widths of the shell, on the wrapper SidebarProvider renders: the\n * expanded and icon widths and the bounds of a resize. The push layout keeps\n * the icon width as the gap beside the content. A stylesheet holds them, not a\n * style attribute, which a Content Security Policy may block; the provider's\n * style or className overrides them. The icon rail is a row's 8 px inset, a\n * 36 px square and the 8 px inset again, so the toggle, the workspace mark,\n * every icon and the active pill share the rail's centre axis; a narrower\n * rail pushes the squares off that axis. */\n:where(.sp-sidebar-wrapper) {\n  --sidebar-width: 16rem;\n  --sidebar-width-min: 12rem;\n  --sidebar-width-max: 24rem;\n  --sidebar-width-icon: 3.25rem;\n}\n\n:where(.sp-sidebar-wrapper[data-layout=\"push\"]) {\n  --sidebar-gap-width: var(--sidebar-width-icon);\n}\n\n/* Dashboard Shell: the height of its bars and a narrower sidebar. */\n:where(.sp-sidebar-wrapper[data-dashboard-shell=\"root\"]) {\n  --dashboard-shell-header-height: 3rem;\n  --sidebar-width: 15rem;\n}\n\n/* Dashboard Shell sticky header (stickyHeader): the header stays in view while\n * the content scrolls, switching where the Sidebar does (its mobileBreakpoint,\n * md or lg). From the breakpoint up the shell is the viewport's height and only\n * the main area scrolls (DashboardShellMain), as in Linear and the Vercel\n * dashboard. Below it the page scrolls, so Safari can still collapse its\n * toolbars and a tap on the iOS status bar still scrolls to the top, and the\n * header sticks to the top of the viewport, as shadcn's sticky site header\n * does. There it sits on the page background, which the inset shows below the\n * breakpoint, with that fill, so the content scrolls under it unseen, and above\n * the content's own layers (z-index 10), below overlays. The inset clips its\n * content without becoming the header's scroll container (DashboardShellInset).\n * A utility on the shell or the header still wins. */\n@media (width >= 48rem) {\n  :where(.sp-sidebar-wrapper[data-sticky-header][data-mobile-breakpoint=\"md\"]) {\n    height: 100svh;\n  }\n}\n\n@media (width >= 64rem) {\n  :where(.sp-sidebar-wrapper[data-sticky-header][data-mobile-breakpoint=\"lg\"]) {\n    height: 100svh;\n  }\n}\n\n@media (width < 48rem) {\n  :root\n    .sp-sidebar-wrapper[data-sticky-header][data-mobile-breakpoint=\"md\"]\n    .sp-dashboard-shell-header {\n    position: sticky;\n    z-index: 20;\n    top: 0;\n    background: var(--sp-rw-bg-base);\n  }\n}\n\n@media (width < 64rem) {\n  :root\n    .sp-sidebar-wrapper[data-sticky-header][data-mobile-breakpoint=\"lg\"]\n    .sp-dashboard-shell-header {\n    position: sticky;\n    z-index: 20;\n    top: 0;\n    background: var(--sp-rw-bg-base);\n  }\n}\n\n/* Dashboard Shell header: a leading sidebar trigger keeps its target and puts\n * its glyph on the start line of the header and the main area, 16 px in, so\n * the glyph, a title without a trigger and the content start on one line. The\n * trigger reaches into the inset by half of what its square adds to the\n * glyph: 10 px for the 36 px trigger with its 16 px glyph. */\n:root .sp-dashboard-shell-header > .sp-sidebar-trigger:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-control-lg);\n  margin-inline-start: calc(\n    (var(--sp-rw-button-icon) - var(--dashboard-shell-trigger-size)) / 2\n  );\n}\n\n:root\n  .sp-dashboard-shell-header\n  > .sp-sidebar-trigger[data-size=\"icon-2xs\"]:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-space-5);\n}\n\n:root\n  .sp-dashboard-shell-header\n  > .sp-sidebar-trigger[data-size=\"icon-xs\"]:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-control-xs);\n}\n\n:root\n  .sp-dashboard-shell-header\n  > .sp-sidebar-trigger[data-size=\"icon-sm\"]:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-control-sm);\n}\n\n:root\n  .sp-dashboard-shell-header\n  > .sp-sidebar-trigger[data-size=\"icon-md\"]:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-control-md);\n}\n\n:root\n  .sp-dashboard-shell-header\n  > .sp-sidebar-trigger[data-size=\"icon-xl\"]:first-child {\n  --dashboard-shell-trigger-size: var(--sp-rw-control-xl);\n}\n\n/* The header is one line (K5). A Breadcrumb in it keeps one row: it shrinks\n * to the room the bar leaves, as every trail can (its own min-width: 0, the\n * Breadcrumb rule below), and its links end with an ellipsis like the current\n * page, so a long trail never runs past the bar. */\n:root .sp-dashboard-shell-header .sp-breadcrumb-list {\n  flex-wrap: nowrap;\n}\n\n:root .sp-dashboard-shell-header .sp-breadcrumb-link {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n/* Sidebar separator (SidebarSeparator): the kit line inside the sidebar's\n * 8 px inset (its mx-2). The separator's own width of 100 % plus that inset\n * ran 8 px past the sidebar. It stretches across the band it sits in, also\n * where the icon rail centres the band's items. */\n:root .sp-separator[data-sidebar=\"separator\"][data-orientation=\"horizontal\"] {\n  align-self: stretch;\n  width: auto;\n}\n\n/* A two-line menu row (SidebarMenuButtonText): the title in the row's own\n * text over a quieter description one kit step smaller, 12 px on 18 under the\n * 14 px rows and 14 px on 21 under the 16 px xl row, in the tertiary text.\n * Both stay on one line and end with an ellipsis (K5). The text takes the\n * row's free width, so a trailing chevron stays at the row's end. Two lines\n * fill the 48 px xl row. When the row changes its size, the description\n * follows at the row's pace (see the navigation rows' transition). */\n:root .sp-sidebar-menu-button-text {\n  display: grid;\n  flex: 1 1 0%;\n  min-width: 0;\n  text-align: start;\n}\n\n:root .sp-sidebar-menu-button-title,\n:root .sp-sidebar-menu-button-description {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n:root .sp-sidebar-menu-button-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-18);\n  transition-property: font-size, line-height;\n  transition-duration: var(--motion-duration-base);\n  transition-timing-function: var(--motion-ease-linear);\n}\n\n:root [data-chassis][data-size=\"xl\"] .sp-sidebar-menu-button-description {\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* Sidebar brand (SidebarBrand): the app's logo at the top of the sidebar, its\n * mark first, then its name or wordmark. It starts where the menu rows start\n * their icons, a row's 8 px inline inset, with a row's gap between the mark and\n * the name, so a 20 px mark puts the name on the labels' line. It shrinks in a\n * narrow sidebar, where a name in a span ends with an ellipsis (K5), and clips\n * what does not fit, such as a wordmark, at its own edge, but never below the\n * rail's 36 px square, so its mark is never cut. It is as tall as that square\n * and reaches 2 px into the 8 px above and below it, so the header band stays\n * 48 px, the top bar's height, as with the 32 px trigger beside it (S2). A\n * link takes the kit's focus outline (focusRingRecipe) and, as a logo rather\n * than a row, no hover fill. */\n:root .sp-sidebar-brand {\n  display: flex;\n  overflow: hidden;\n  inline-size: fit-content;\n  max-inline-size: 100%;\n  min-inline-size: var(--sp-rw-control-lg);\n  block-size: var(--sp-rw-control-lg);\n  margin-block: calc((var(--sp-rw-control-md) - var(--sp-rw-control-lg)) / 2);\n  align-items: center;\n  gap: var(--sp-rw-button-content-gap);\n  padding-inline: var(--sp-rw-space-2);\n  border-radius: var(--sp-rw-radius-control);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-strong);\n  line-height: var(--sp-rw-line-21);\n  text-decoration: none;\n  white-space: nowrap;\n}\n\n/* The mark keeps its size wherever the brand narrows; the name gives way. */\n:root .sp-sidebar-brand > :first-child {\n  flex-shrink: 0;\n}\n\n/* A mark without a size takes a row icon's 20 px. */\n:root .sp-sidebar-brand > svg:first-child:not([class*=\"size-\"]) {\n  inline-size: var(--sp-rw-space-5);\n  block-size: var(--sp-rw-space-5);\n}\n\n:root .sp-sidebar-brand > span:not(:first-child) {\n  min-inline-size: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n/* On the collapsed icon rail the brand shows only its mark, in the rail's\n * square on the rail's axis, like the row icons. Like a row, it keeps its\n * inset, so the mark stays where it is while the width animates (FB1-3,\n * Lukas, 16 October 2026): the band narrows the brand to the square, and the\n * name fades out in its place at the rail's pace and gives way, as a row's\n * label does. It stays in the layout, so it is still the link's accessible\n * name, and expanding fades it back in. */\n:root .sp-sidebar-brand > :not(:first-child) {\n  transition: opacity var(--motion-duration-base) var(--motion-ease-linear);\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  .sp-sidebar-brand\n  > :not(:first-child) {\n  opacity: 0;\n}\n\n/* In a SidebarMenuItem with an expand trigger, the item reaches into the band\n * in the brand's place, so the trigger over the item covers the brand's\n * square. */\n:root .sp-sidebar-menu-item:has(> .sp-sidebar-brand) {\n  margin-block: calc((var(--sp-rw-control-md) - var(--sp-rw-control-lg)) / 2);\n}\n\n:root .sp-sidebar-menu-item > .sp-sidebar-brand {\n  margin-block: 0;\n}\n\n/* In the inset layout the top bar sits on the framed surface, 8 px down. The\n * band with the brand moves down with it, so the logo and the collapse button\n * stay level with the top bar beside them (S2). */\n:root\n  .sp-sidebar-container[data-layout=\"inset\"]\n  .sp-sidebar-header:has(.sp-sidebar-brand) {\n  margin-block-start: var(--sp-rw-space-2);\n}\n\n/* Sidebar: a menu action is the Button's muted inline action, which owns its\n * colours in every state and draws no fill (rulebook T2). These rules only\n * reveal one that shows on hover: it appears with its row's hover or focus and\n * stays while its menu is open. The Button's pressed and disabled rules\n * outrank them. */\n:where([data-sidebar=\"menu-action\"][data-show-on-hover]) {\n  pointer-events: none;\n  opacity: 0;\n}\n\n:where(\n  [data-sidebar=\"menu-item\"]:focus-within\n    [data-sidebar=\"menu-action\"][data-show-on-hover]\n) {\n  pointer-events: auto;\n  opacity: 1;\n}\n\n@media (hover: hover) {\n  :where(\n    [data-sidebar=\"menu-item\"]:hover\n      [data-sidebar=\"menu-action\"][data-show-on-hover]\n  ) {\n    pointer-events: auto;\n    opacity: 1;\n  }\n}\n\n:where([data-sidebar=\"menu-action\"][data-show-on-hover][data-popup-open]) {\n  pointer-events: auto;\n  opacity: 1;\n}\n\n/* Sidebar expand trigger (SidebarMenuItem `expandTrigger`): the kit's\n * SidebarTrigger lies over the item's row. On the collapsed icon rail, hovering\n * or focusing the item swaps them: the row's content shrinks, blurs and fades\n * out while the trigger's glyph grows in, and back. Elsewhere the trigger stays\n * hidden and lets the pointer through. Only the contents move, so the Button\n * keeps its own fill, focus and transitions. A SidebarBrand in the item, which\n * has no fill, moves as a whole. */\n:where(\n  [data-sidebar=\"menu-item\"][data-expand-trigger]\n    > :is([data-sidebar=\"menu-button\"], [data-sidebar=\"expand-trigger\"])\n    > .sp-button-content\n),\n:where([data-sidebar=\"menu-item\"][data-expand-trigger] > .sp-sidebar-brand) {\n  transition-property: opacity, scale, filter;\n  transition-duration: var(--motion-duration-slow);\n  transition-timing-function: var(--motion-ease-soft-in-out);\n}\n\n:where([data-sidebar=\"expand-trigger\"]) {\n  pointer-events: none;\n}\n\n:where([data-sidebar=\"expand-trigger\"] > .sp-button-content),\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]:focus-within\n  > [data-sidebar=\"menu-button\"]\n  > .sp-button-content,\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]:focus-within\n  > .sp-sidebar-brand {\n  opacity: 0;\n  filter: blur(4px);\n  scale: 0.25;\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]\n  > [data-sidebar=\"menu-button\"],\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]\n  > .sp-sidebar-brand {\n  pointer-events: none;\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]\n  > [data-sidebar=\"expand-trigger\"] {\n  pointer-events: auto;\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-sidebar=\"menu-item\"][data-expand-trigger]:focus-within\n  > [data-sidebar=\"expand-trigger\"]\n  > .sp-button-content {\n  opacity: 1;\n  filter: none;\n  scale: 1;\n}\n\n@media (hover: hover) {\n  :root\n    .sp-sidebar[data-collapsible=\"icon\"]\n    [data-sidebar=\"menu-item\"][data-expand-trigger]:hover\n    > [data-sidebar=\"menu-button\"]\n    > .sp-button-content,\n  :root\n    .sp-sidebar[data-collapsible=\"icon\"]\n    [data-sidebar=\"menu-item\"][data-expand-trigger]:hover\n    > .sp-sidebar-brand {\n    opacity: 0;\n    filter: blur(4px);\n    scale: 0.25;\n  }\n\n  :root\n    .sp-sidebar[data-collapsible=\"icon\"]\n    [data-sidebar=\"menu-item\"][data-expand-trigger]:hover\n    > [data-sidebar=\"expand-trigger\"]\n    > .sp-button-content {\n    opacity: 1;\n    filter: none;\n    scale: 1;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :where(\n    [data-sidebar=\"menu-item\"][data-expand-trigger]\n      > :is([data-sidebar=\"menu-button\"], [data-sidebar=\"expand-trigger\"])\n      > .sp-button-content\n  ),\n  :where([data-sidebar=\"menu-item\"][data-expand-trigger] > .sp-sidebar-brand) {\n    transition: none;\n  }\n}\n\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  :where(\n    [data-sidebar=\"menu-item\"][data-expand-trigger]\n      > :is([data-sidebar=\"menu-button\"], [data-sidebar=\"expand-trigger\"])\n      > .sp-button-content\n  ),\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  :where([data-sidebar=\"menu-item\"][data-expand-trigger] > .sp-sidebar-brand) {\n  transition: none;\n}\n\n/* Decorative icon wells have no interactive state; controls use their own owner. */\n:root .sp-icon-well {\n  border: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* Code and Pagination consume the same optical rim and radius foundations as\n   the rest of the kit. Component source owns only structure and behavior. */\n:root :where(.sp-code),\n:root :where(.sp-code-block) {\n  border: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n}\n\n:root .sp-pagination-content::before {\n  border: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n  border-radius: var(--sp-rw-radius-control);\n  background: var(--sp-rw-bg-secondary);\n  box-shadow: var(--sp-rw-control-depth);\n  content: \"\";\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-pagination-content::before {\n  transition: none;\n}\n\n:root .sp-pagination[data-variant=\"ghost\"] .sp-pagination-content::before {\n  border-color: transparent;\n  background: var(--sp-rw-bg-muted);\n  box-shadow: none;\n}\n\n/* A page link is at least as wide as it is high, the square of its size, and\n * a longer number, such as 1,248, widens it with 4 px on each side instead of\n * filling the square edge to edge. A number that fits with that room keeps\n * the square, as three digits do at the lg and xl sizes. */\n:root .sp-pagination-link[data-profile=\"default\"][data-size^=\"icon-\"] {\n  width: auto;\n  aspect-ratio: 1;\n  padding-inline: var(--sp-rw-space-1);\n}\n\n:root [data-dashboard-shell=\"header\"][data-divider=\"true\"] {\n  border-bottom: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root [data-pattern=\"detail-drawer-header\"] {\n  padding-bottom: var(--sp-rw-space-4);\n  border-bottom: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n/* A divided footer is a band: 16 px above and below its actions (S4), plus the\n * safe area. The extra slot outranks the Drawer footer's own 24 px below. */\n:root .sp-drawer-footer[data-pattern=\"detail-drawer-footer\"] {\n  border-top: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n  padding-bottom: calc(var(--sp-rw-space-4) + env(safe-area-inset-bottom, 0px));\n}\n\n:root body {\n  color: var(--sp-rw-fg-primary);\n  background: var(--sp-semantic-color-background);\n  font-family: var(--sp-rw-font-family-default);\n  font-size: var(--sp-rw-font-size-default);\n  font-weight: var(--sp-rw-font-weight-default);\n  letter-spacing: var(--sp-rw-letter-spacing-default);\n  line-height: var(--sp-rw-line-height-default);\n}\n\n:root body * {\n  letter-spacing: var(--sp-rw-letter-spacing-default);\n}\n\n/* AlertDialog */\n:root .sp-alert-action {\n  gap: var(--sp-rw-action-gap);\n}\n\n:root .sp-alert-dialog-backdrop {\n  background: var(--sp-rw-bg-scrim);\n  backdrop-filter: none;\n}\n\n:root .sp-alert-dialog-viewport {\n  grid-template-rows: minmax(0, 1fr) auto;\n  padding: var(--sp-rw-space-12) 0 0;\n}\n\n:root .sp-alert-dialog-popup {\n  display: grid;\n  width: 100%;\n  max-height: calc(100dvh - var(--sp-rw-space-8));\n  overflow-y: auto;\n  gap: var(--sp-rw-space-4);\n  border: 0;\n  border-radius: calc(var(--sp-rw-radius-dialog) * 1.5);\n  padding: var(--sp-rw-space-5) var(--sp-rw-space-6) var(--sp-rw-space-6);\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-lg-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-alert-dialog-popup::before {\n  display: none;\n}\n\n/* A header that is the popup's last part keeps as much space below its text\n * as above it, 20 px, as a Dialog header alone does (rulebook S2); the 24 px\n * below are for the actions (FB-10). */\n:root .sp-alert-dialog-popup:has(> .sp-alert-dialog-header:last-child) {\n  padding-bottom: var(--sp-rw-space-5);\n}\n\n:root .sp-alert-dialog-header {\n  display: grid;\n  gap: var(--sp-rw-space-1-5);\n  padding: 0;\n  text-align: left;\n}\n\n:root .sp-alert-dialog-title {\n  color: var(--sp-rw-fg-primary);\n  font-family: var(--sp-rw-font-family-default);\n  font-size: 1.125rem;\n  font-weight: var(--sp-rw-font-weight-title);\n  line-height: 1.6875rem;\n}\n\n:root .sp-alert-dialog-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n}\n\n:root [data-alert-dialog-item] {\n  min-height: 2.0625rem;\n  margin-top: var(--sp-rw-space-0-5);\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--sp-rw-space-1-5) var(--sp-rw-space-3);\n  background: var(--sp-rw-bg-muted);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n}\n\n:root [data-alert-dialog-confirmation-label] {\n  margin-top: var(--sp-rw-space-1-5);\n  font-size: var(--sp-rw-font-14);\n}\n\n:root .sp-alert-dialog-footer {\n  flex-direction: row;\n  justify-content: flex-end;\n  gap: var(--sp-rw-action-gap);\n  border: 0;\n  border-radius: 0;\n  padding: 0;\n  background: transparent;\n}\n\n:root .sp-dialog-footer {\n  flex-direction: row;\n  justify-content: flex-end;\n  gap: var(--sp-rw-action-gap);\n}\n\n/* Inline command results are an Autocomplete-owned collection profile. */\n:root .sp-base-autocomplete-item[data-profile=\"command\"] {\n  min-height: 2.8125rem;\n  gap: var(--sp-rw-space-3);\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--sp-rw-space-3) var(--sp-rw-space-4) var(--sp-rw-space-3)\n    var(--sp-rw-space-3);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n}\n\n/* The rows follow the control size, 37, 41, 45 and 56 px from sm to xl: they\n * step as the controls do, and lg keeps the palette's 45 px (Lukas,\n * 13 October 2026). */\n:root .sp-base-autocomplete-item[data-profile=\"command\"][data-size=\"sm\"] {\n  min-height: 2.3125rem;\n  padding-block: var(--sp-rw-space-2);\n}\n\n:root .sp-base-autocomplete-item[data-profile=\"command\"][data-size=\"md\"] {\n  min-height: 2.5625rem;\n  padding-block: var(--sp-rw-space-2-5);\n}\n\n:root .sp-base-autocomplete-item[data-profile=\"command\"][data-size=\"xl\"] {\n  min-height: 3.5rem;\n  padding-block: var(--sp-rw-space-4);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* A label that wraps keeps the shortcut on its first line (I5), as the\n * AutocompleteItem keeps a leading icon there. One line fills the content box,\n * so a one-line result looks the same. */\n:root\n  .sp-base-autocomplete-item[data-profile=\"command\"]\n  > [data-command-shortcut] {\n  align-self: flex-start;\n  translate: 0 calc((var(--sp-rw-line-21) - 100%) / 2);\n}\n\n/* The shortcut of an xl row sits on its 24 px first line. */\n:root\n  .sp-base-autocomplete-item[data-profile=\"command\"][data-size=\"xl\"]\n  > [data-command-shortcut] {\n  translate: 0 calc((var(--sp-rw-line-24) - 100%) / 2);\n}\n\n/* Match the neutral outline Button hover in both themes. */\n:root .sp-base-autocomplete-item[data-profile=\"command\"][data-highlighted] {\n  background: light-dark(\n    color-mix(in srgb, var(--sp-rw-control-interaction) 8%, transparent),\n    color-mix(in srgb, var(--sp-rw-control-interaction) 12%, transparent)\n  );\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-command-panel > .sp-scroll-area {\n  min-height: 0;\n  flex: 1;\n  overflow: hidden;\n}\n\n/* Dialog, Sheet, Drawer and Popover\n * AlertDialog is the measured large-modal reference. The other overlay\n * surfaces keep their existing behavior and derive their chrome from the\n * same surface, type and spacing tokens. */\n:root :where(.sp-dialog-backdrop),\n:root :where(.sp-drawer-backdrop),\n:root :where(.sp-sheet-backdrop) {\n  background: var(--sp-rw-bg-scrim);\n  backdrop-filter: none;\n}\n\n:root .sp-dialog-viewport {\n  grid-template-rows: minmax(0, 1fr) auto;\n  padding: var(--sp-rw-space-12) 0 0;\n}\n\n:root .sp-dialog-popup {\n  border: 0;\n  border-radius: var(--sp-rw-radius-dialog);\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-lg-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n/* The command palette's popup, a dialog popup with a slot of its own, fills\n * with the dialog's colour. It publishes the colour like the other surfaces,\n * so a glass or theme override reaches it (RW-14). */\n:root .sp-base-dialog[data-profile=\"command\"] {\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n}\n\n:root .sp-dialog-popup::before,\n:root .sp-sheet-popup::before,\n:root .sp-drawer-popup::before,\n:root .sp-popover-popup::before {\n  display: none;\n}\n\n:root :where(.sp-dialog-header),\n:root :where(.sp-sheet-header),\n:root :where(.sp-drawer-header) {\n  gap: var(--sp-rw-space-1-5);\n  padding: var(--sp-rw-space-5) var(--sp-rw-space-6) 0;\n  text-align: left;\n}\n\n/* A header beside the corner close button keeps its text 8 px clear of it. The\n * popup renders that button as the 24 px overlay-dismiss Button, 8 px inside\n * its top-end corner (B5), so the header ends 40 px from the edge. */\n:root\n  :where(.sp-dialog-popup, .sp-sheet-popup, .sp-drawer-popup):has(\n    > [data-profile=\"overlay-dismiss\"]\n  )\n  :where(.sp-dialog-header),\n:root\n  :where(.sp-dialog-popup, .sp-sheet-popup, .sp-drawer-popup):has(\n    > [data-profile=\"overlay-dismiss\"]\n  )\n  :where(.sp-sheet-header),\n:root\n  :where(.sp-dialog-popup, .sp-sheet-popup, .sp-drawer-popup):has(\n    > [data-profile=\"overlay-dismiss\"]\n  )\n  :where(.sp-drawer-header) {\n  padding-inline-end: calc(var(--sp-rw-space-2) * 2 + var(--sp-rw-space-6));\n}\n\n/* A header that is the popup's last part, such as a title alone, keeps as much\n * space below its text as above it, 20 px, as a lone Card header keeps its\n * inset all round (rulebook S2, S3). Only the corner close button and the\n * Drawer's grab bar, which sit outside the flow, may follow it (FB-10). */\n:root\n  :where(.sp-dialog-header):not(\n    :has(~ :not([data-profile=\"overlay-dismiss\"], .sp-drawer-bar))\n  ),\n:root\n  :where(.sp-sheet-header):not(\n    :has(~ :not([data-profile=\"overlay-dismiss\"], .sp-drawer-bar))\n  ),\n:root\n  :where(.sp-drawer-header):not(\n    :has(~ :not([data-profile=\"overlay-dismiss\"], .sp-drawer-bar))\n  ) {\n  padding-bottom: var(--sp-rw-space-5);\n}\n\n/* Card titles share the title of the overlays: 18 px on 27 px, in the title\n * weight (--sp-rw-font-weight-title, 450). The tinted Card band keeps its\n * compact 14 px semibold title (see the Card rules). */\n:root :where(.sp-dialog-title),\n:root :where(.sp-sheet-title),\n:root :where(.sp-drawer-title),\n:root :where(.sp-card-title) {\n  color: var(--sp-rw-fg-primary);\n  font-family: var(--sp-rw-font-family-default);\n  font-size: 1.125rem;\n  font-weight: var(--sp-rw-font-weight-title);\n  line-height: 1.6875rem;\n}\n\n:root :where(.sp-dialog-description),\n:root :where(.sp-sheet-description),\n:root :where(.sp-drawer-description) {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n}\n\n:root :where(.sp-dialog-panel),\n:root :where(.sp-sheet-panel),\n:root :where(.sp-drawer-panel) {\n  padding: var(--sp-rw-space-4) var(--sp-rw-space-6);\n}\n\n:root :where(.sp-dialog-footer),\n:root :where(.sp-sheet-footer),\n:root :where(.sp-drawer-footer) {\n  flex-direction: row;\n  justify-content: flex-end;\n  gap: var(--sp-rw-action-gap);\n  border: 0;\n  border-radius: 0;\n  padding: var(--sp-rw-space-4) var(--sp-rw-space-6) var(--sp-rw-space-6);\n  background: transparent;\n}\n\n:root .sp-sheet-popup {\n  border: 0;\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-lg-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n/* Side panels use a borderless secondary surface with 24px corners (at the md\n * radius: one and a half dialog radii) and the floating-md contour/depth\n * stack. */\n:root .sp-drawer-popup {\n  border: 0;\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-drawer-popup[data-position=\"bottom\"]:not([data-variant=\"straight\"]) {\n  border-radius: calc(var(--sp-rw-radius-dialog) * 1.5)\n    calc(var(--sp-rw-radius-dialog) * 1.5) 0 0;\n}\n\n:root .sp-drawer-popup[data-position=\"top\"]:not([data-variant=\"straight\"]) {\n  border-radius: 0 0 calc(var(--sp-rw-radius-dialog) * 1.5)\n    calc(var(--sp-rw-radius-dialog) * 1.5);\n}\n\n:root .sp-drawer-popup[data-position=\"left\"]:not([data-variant=\"straight\"]) {\n  border-radius: 0 calc(var(--sp-rw-radius-dialog) * 1.5)\n    calc(var(--sp-rw-radius-dialog) * 1.5) 0;\n}\n\n:root .sp-drawer-popup[data-position=\"right\"]:not([data-variant=\"straight\"]) {\n  border-radius: calc(var(--sp-rw-radius-dialog) * 1.5) 0 0\n    calc(var(--sp-rw-radius-dialog) * 1.5);\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"] {\n  border-radius: 0;\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"]::before {\n  box-shadow: none;\n}\n\n:root .sp-drawer-popup[data-elevation=\"none\"] {\n  box-shadow: none;\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"][data-position=\"left\"] {\n  border-right: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"][data-position=\"right\"] {\n  border-left: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"][data-position=\"top\"] {\n  border-bottom: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root .sp-drawer-popup[data-variant=\"straight\"][data-position=\"bottom\"] {\n  border-top: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n}\n\n@media (min-width: 640px) {\n  :root .sp-drawer-popup[data-position][data-variant=\"inset\"] {\n    border-radius: var(--sp-rw-radius-dialog);\n  }\n\n  :root .sp-drawer-popup[data-position][data-variant=\"inset\"]::after {\n    background: transparent;\n  }\n}\n\n:root .sp-drawer-footer {\n  padding-bottom: calc(var(--sp-rw-space-6) + env(safe-area-inset-bottom, 0px));\n}\n\n:root .sp-drawer-popup::after {\n  background: var(--sp-rw-bg-secondary);\n}\n\n:root .sp-drawer-popup:is([data-starting-style], [data-ending-style]) {\n  box-shadow: none;\n}\n\n:root .sp-drawer-bar::before {\n  background: var(--sp-rw-bg-muted);\n}\n\n:root .sp-popover-popup,\n:root .sp-navigation-menu-popup {\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n/* Toast and Notification wear the same borderless floating surface: one edge\n * from the floating-md shadow (B7). Their fill is the stack fill of\n * toast-surface.ts, from the surface colour that toast.css publishes, their\n * radius stays the control radius and their text keeps the popover text\n * colour. */\n:root .sp-toast-surface {\n  border: 0;\n  box-shadow: var(--sp-rw-floating-md-shadow);\n  color: var(--sp-semantic-color-popover-foreground);\n}\n\n/* A raised Notification is set into content like a Card: the Card's one edge\n * in place of the floating shadow (B7; Lukas, 12 October 2026). Fill, radius\n * and text colour stay the toast surface's. */\n:root .sp-notification[data-surface=\"raised\"] {\n  box-shadow: var(--sp-rw-surface-xs-shadow);\n}\n\n/* ColorPicker and the date and time pickers use React Aria-owned popovers, and\n * DateField names its Popover popup with a slot of its own, so the rule above\n * misses it. All of them share the canonical borderless floating-md surface\n * with the public Popover component. A component's own stylesheet adds only\n * placement, width and padding: no border, radius or shadow. */\n:root .sp-color-picker-popover,\n:root .sp-date-field-popover,\n:root .sp-date-picker-popover,\n:root .sp-date-range-picker-popover,\n:root .sp-date-time-picker-calendar-popover,\n:root .sp-date-time-picker-time-popover {\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-color-picker-popover::before,\n:root .sp-date-field-popover::before,\n:root .sp-date-picker-popover::before,\n:root .sp-date-range-picker-popover::before,\n:root .sp-date-time-picker-calendar-popover::before,\n:root .sp-date-time-picker-time-popover::before {\n  box-shadow: none;\n}\n\n/* Menu, Context Menu and their consumers share one global floating/menu-row\n * recipe. Compositions may choose placement and width, but never restyle the\n * popup surface or interaction states locally. */\n:root :where(.sp-menu-popup),\n:root :where(.sp-context-menu-popup),\n:root :where(.sp-menu-sub-content),\n:root :where(.sp-context-menu-sub-content) {\n  overflow: hidden;\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  background: var(--sp-rw-bg-secondary);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root :where(.sp-menu-popup)::before,\n:root :where(.sp-context-menu-popup)::before,\n:root :where(.sp-menu-sub-content)::before,\n:root :where(.sp-context-menu-sub-content)::before {\n  box-shadow: none;\n}\n\n:root :where(.sp-menu-item),\n:root :where(.sp-menu-link-item),\n:root :where(.sp-menu-checkbox-item),\n:root :where(.sp-menu-radio-item),\n:root :where(.sp-menu-sub-trigger),\n:root :where(.sp-context-menu-item),\n:root :where(.sp-context-menu-link-item),\n:root :where(.sp-context-menu-checkbox-item),\n:root :where(.sp-context-menu-radio-item),\n:root :where(.sp-context-menu-sub-trigger),\n:root :where(.sp-drawer-menu-item),\n:root :where(.sp-drawer-menu-checkbox-item),\n:root :where(.sp-drawer-menu-radio-item),\n:root :where(.sp-drawer-menu-trigger) {\n  min-height: var(--sp-rw-control-md);\n  gap: var(--sp-rw-menu-row-gap);\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--sp-rw-space-1) var(--sp-rw-menu-row-padding-inline);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* An inset row has no icon but keeps the slot of one, so its label starts where\n * the labels of icon rows start (rulebook I8). A group label takes the same\n * inset. The parts carry no inset padding of their own; these rules refine\n * the label padding of the Component base block. */\n:root\n  :where(\n    .sp-menu-item,\n    .sp-menu-link-item,\n    .sp-menu-sub-trigger,\n    .sp-menu-label,\n    .sp-context-menu-item,\n    .sp-context-menu-link-item,\n    .sp-context-menu-sub-trigger,\n    .sp-context-menu-label\n  )[data-inset] {\n  padding-inline-start: calc(\n    var(--sp-rw-menu-row-padding-inline) + var(--sp-rw-menu-row-icon) +\n      var(--sp-rw-menu-row-gap)\n  );\n}\n\n:root\n  :where(.sp-menu-item):where([data-highlighted], [data-popup-open]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-link-item):where([data-highlighted], [data-popup-open]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-checkbox-item):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-menu-radio-item):where([data-highlighted], [data-popup-open]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-sub-trigger):where([data-highlighted], [data-popup-open]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-item):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-context-menu-link-item):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-context-menu-checkbox-item):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-context-menu-radio-item):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-context-menu-sub-trigger):where(\n    [data-highlighted],\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.sp-drawer-menu-item):hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-checkbox-item):hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-radio-item):hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-trigger):hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  :where(.sp-menu-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-link-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-checkbox-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-radio-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-menu-sub-trigger):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-link-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-checkbox-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-radio-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-context-menu-sub-trigger):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-checkbox-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-radio-item):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-drawer-menu-trigger):active:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root\n  :where(\n    .sp-menu-item,\n    .sp-menu-link-item,\n    .sp-context-menu-item,\n    .sp-context-menu-link-item\n  )[data-variant=\"destructive\"] {\n  color: var(--sp-rw-fg-destructive);\n}\n\n:root\n  :where(\n    .sp-menu-item,\n    .sp-menu-link-item,\n    .sp-menu-checkbox-item,\n    .sp-menu-radio-item,\n    .sp-menu-sub-trigger,\n    .sp-context-menu-item,\n    .sp-context-menu-link-item,\n    .sp-context-menu-checkbox-item,\n    .sp-context-menu-radio-item,\n    .sp-context-menu-sub-trigger,\n    .sp-drawer-menu-item,\n    .sp-drawer-menu-checkbox-item,\n    .sp-drawer-menu-radio-item,\n    .sp-drawer-menu-trigger\n  )\n  svg {\n  width: var(--sp-rw-menu-row-icon);\n  height: var(--sp-rw-menu-row-icon);\n  margin-inline: 0;\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n}\n\n/* The reset above aligns leading icons. The trailing submenu chevron still\n * ends the row, like a shortcut. */\n:root\n  :where(.sp-menu-sub-trigger, .sp-context-menu-sub-trigger)\n  > svg:last-child,\n:root\n  .sp-drawer-menu-trigger[data-chassis]\n  > .sp-button-content\n  > svg:last-child {\n  margin-inline-start: auto;\n}\n\n:root\n  :where(\n    .sp-menu-item,\n    .sp-menu-link-item,\n    .sp-menu-checkbox-item,\n    .sp-menu-radio-item,\n    .sp-menu-sub-trigger,\n    .sp-context-menu-item,\n    .sp-context-menu-link-item,\n    .sp-context-menu-checkbox-item,\n    .sp-context-menu-radio-item,\n    .sp-context-menu-sub-trigger\n  ):where([data-highlighted], [data-popup-open]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  )\n  svg {\n  color: var(--sp-rw-fg-primary);\n}\n\n/* A destructive row keeps its icon in the colour of its label, at rest and\n * while highlighted, instead of the neutral icon colours above: the trash can is\n * as red as \"Delete\". The icon follows the label rule rather than naming the\n * colour again, and this rule has to come after the two above, which it ties in\n * specificity. The DrawerMenu row is a Button, so its destructive state is the\n * ghost-destructive variant, named through its profile like its label below: a\n * row that a DrawerClose renders loses the slot but keeps the profile. */\n:root\n  :where(\n    .sp-menu-item,\n    .sp-menu-link-item,\n    .sp-context-menu-item,\n    .sp-context-menu-link-item\n  )[data-variant=\"destructive\"]\n  svg,\n:root\n  [data-chassis][data-profile=\"menu-row\"][data-variant=\"ghost-destructive\"]\n  svg {\n  color: inherit;\n}\n\n:root :where(.sp-menu-label),\n:root :where(.sp-context-menu-label),\n:root :where(.sp-drawer-menu-group-label) {\n  color: var(--sp-rw-fg-tertiary);\n}\n\n@media (pointer: coarse) {\n  :root :where(.sp-menu-item),\n  :root :where(.sp-menu-link-item),\n  :root :where(.sp-menu-checkbox-item),\n  :root :where(.sp-menu-radio-item),\n  :root :where(.sp-menu-sub-trigger),\n  :root :where(.sp-context-menu-item),\n  :root :where(.sp-context-menu-link-item),\n  :root :where(.sp-context-menu-checkbox-item),\n  :root :where(.sp-context-menu-radio-item),\n  :root :where(.sp-context-menu-sub-trigger),\n  :root :where(.sp-drawer-menu-item),\n  :root :where(.sp-drawer-menu-checkbox-item),\n  :root :where(.sp-drawer-menu-radio-item),\n  :root :where(.sp-drawer-menu-trigger) {\n    min-height: var(--sp-rw-control-xl);\n    padding-block: var(--sp-rw-space-3);\n  }\n}\n\n:root .sp-popover-popup.bg-background\\/80 {\n  background: color-mix(in srgb, var(--sp-rw-bg-secondary) 80%, transparent);\n}\n\n:root:where(.dark) .sp-popover-popup.bg-background\\/80,\n:root:where([data-theme=\"dark\"]) .sp-popover-popup.bg-background\\/80,\n:root :where(.dark) .sp-popover-popup.bg-background\\/80,\n:root :where([data-theme=\"dark\"]) .sp-popover-popup.bg-background\\/80 {\n  background: color-mix(in srgb, var(--sp-rw-bg-secondary) 70%, transparent);\n}\n\n:root .sp-popover-viewport {\n  --viewport-inline-padding: var(--sp-rw-space-4);\n  padding-block: var(--sp-rw-space-4);\n}\n\n:root .sp-popover-viewport[data-padding=\"compact\"] {\n  --viewport-inline-padding: var(--sp-rw-space-2);\n  padding-block: var(--sp-rw-space-2);\n}\n\n:root .sp-popover-title {\n  color: var(--sp-rw-fg-primary);\n  font-family: var(--sp-rw-font-family-default);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-popover-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n}\n\n/* A tooltip-style popover is as wide as its text, not the width Base UI\n * measures. */\n:root .sp-popover-popup[data-tooltip-style] {\n  width: fit-content;\n  border-radius: var(--sp-rw-radius-sm);\n  box-shadow: var(--sp-rw-surface-sm-shadow);\n  font-size: var(--sp-rw-font-12);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-16);\n}\n\n:root .sp-popover-popup[data-tooltip-style] .sp-popover-viewport {\n  --viewport-inline-padding: var(--sp-rw-space-1-5);\n  padding-block: var(--sp-rw-space-1);\n}\n\n/* A column keeps the centered popup's clamped height definite, so a\n * DialogPanel scrolls between the header and footer. */\n@media (min-width: 40rem) {\n  :root .sp-alert-dialog-viewport,\n  :root .sp-dialog-viewport {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    padding: var(--sp-rw-space-4) var(--sp-rw-space-12);\n  }\n}\n\n/* Button and IconButton */\n:root [data-chassis] {\n  --sp-rw-button-fill: transparent;\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  --sp-rw-button-depth: none;\n  --sp-rw-button-interaction: transparent;\n  --sp-rw-button-interaction-rest: 0;\n  --sp-rw-button-interaction-hover: 0;\n  --sp-rw-button-interaction-pressed: 0;\n  --sp-rw-button-spotlights: none;\n  --sp-rw-button-dome: none;\n  --sp-rw-button-rim: none;\n  --sp-rw-button-micro: none;\n  /* The icon of the control size; the size rules below set the others. */\n  --sp-rw-button-icon: var(--sp-rw-control-icon);\n\n  min-height: 0;\n  isolation: isolate;\n  -webkit-user-select: none;\n  user-select: none;\n  gap: var(--sp-rw-button-content-gap);\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--sp-rw-space-1-5) var(--sp-rw-space-2-5);\n  background: var(--sp-rw-button-fill);\n  box-shadow: none;\n  color: var(--sp-rw-button-text);\n  font-family: var(\n    --sp-rw-button-font-family,\n    var(--sp-rw-font-family-default)\n  );\n  font-size: var(--sp-rw-button-font-size, var(--sp-rw-font-size-default));\n  font-weight: var(\n    --sp-rw-button-font-weight,\n    var(--sp-rw-font-weight-default)\n  );\n  letter-spacing: var(\n    --sp-rw-button-letter-spacing,\n    var(--sp-rw-letter-spacing-default)\n  );\n  line-height: var(\n    --sp-rw-button-line-height,\n    var(--sp-rw-line-height-default)\n  );\n  transform: none;\n  transition: none;\n}\n\n:root [data-chassis] > .sp-button-content > svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-button-icon);\n  height: var(--sp-rw-button-icon);\n  margin-inline: 0;\n}\n\n:root [data-chassis]::before {\n  background: none;\n  box-shadow: none;\n}\n\n:root\n  [data-chassis]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  box-shadow: none !important;\n  transform: none;\n}\n\n/* One focus outline for every control. Its colour is the `--sp-rw-border-focus`\n * token, fully opaque at every pixel density so the indicator keeps its non-text\n * contrast; the field and table rims read the same token. */\n:root\n  .sp-focus-ring:not(.sp-focus-ring--ring, .sp-focus-ring--inset):focus-visible,\n:root .sp-focus-ring--visible:not(.sp-focus-ring--ring, .sp-focus-ring--inset),\n:root [data-chassis]:focus-visible,\n:root .sp-base-toggle:focus-visible,\n:root .sp-checkbox:focus-visible,\n:root .sp-table-selection-checkbox[data-focus-visible],\n:root .sp-radio:focus-visible,\n:root .sp-switch:focus-visible,\n:root .sp-slider-thumb:focus-visible,\n:root .sp-slider-thumb:has(:focus-visible),\n:root .sp-fill-sweep-button-root:has(:focus-visible),\n:root .sp-tabs-content:focus-visible,\n:root .sp-base-scroll-area:has(> .sp-scroll-area-viewport:focus-visible),\n:root :is(a, button).sp-base-badge:focus-visible {\n  outline: var(--sp-semantic-state-focus-width) solid var(--sp-rw-border-focus);\n  outline-offset: var(--sp-semantic-state-focus-offset);\n}\n\n/* Disabled. A disabled control fades once, on its outermost element, by the\n * action token (0.64) (T1). Parts inside a field or a label that fades already\n * keep their full opacity (see \"Disabled parts\" below). */\n:root\n  [data-chassis][data-size][data-variant]:is(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n/* Public Button composition profiles keep reusable action chassis and states\n * in the Button boundary while parents retain placement and unique anatomy. */\n/* These profiles paint state on the root. A variant's interaction layer must\n * not tint that same state a second time. Surface/elevation layers are retained. */\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"accordion-trigger\"],\n    [data-profile=\"full-cell-action\"],\n    [data-profile=\"task-pill\"],\n    [data-profile=\"chip-remove\"],\n    [data-profile=\"field-action\"],\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ) {\n  --sp-rw-button-interaction: transparent;\n}\n\n/* Field action: the icon-only action inside a field (show or hide a secret,\n * copy, clear, open a picker). It is the bare glyph, never a Button box. The\n * variant's interaction layer is neutral and silent (see the group above), so\n * nothing tints the field at hover, press or open; those states only darken\n * the glyph from the muted to the primary text colour. The 24px box stays as\n * the hit area. Keyboard focus draws the kit focus outline tight around the\n * glyph, not around that box; a pointer focus shows nothing. */\n:root [data-chassis][data-size][data-profile=\"field-action\"] {\n  /* A glyph in a field is 16 px (I3), whatever box the field's size gives it. */\n  --sp-rw-button-icon: var(--sp-rw-control-icon);\n  --sp-rw-button-text: var(--sp-rw-fg-tertiary);\n  --sp-rw-button-interaction-rest: 0;\n  --sp-rw-button-interaction-hover: 0;\n  --sp-rw-button-interaction-pressed: 0;\n  border-radius: var(--sp-rw-radius-xs);\n  background: transparent;\n}\n\n:root\n  [data-chassis][data-size][data-profile=\"field-action\"]:where(\n    :hover,\n    :active,\n    :focus-visible,\n    [data-pressed],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n}\n\n/* An editable collection field is already active while its list is open, so the\n * open list alone must not darken the trailing actions into a second persistent\n * control; hover, press and focus still do. Base UI sets data-pressed on them\n * for as long as the list is open, so only :active counts as a press here. */\n:root\n  [data-chassis][data-size]:where(\n    .sp-combobox-trigger,\n    .sp-combobox-clear,\n    .sp-autocomplete-trigger,\n    .sp-autocomplete-clear\n  )[data-profile=\"field-action\"][data-popup-open]:not(\n    :hover,\n    :active,\n    :focus-visible,\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  --sp-rw-button-text: var(--sp-rw-fg-tertiary);\n}\n\n/* The content box shrinks to the glyph, without the glyph's negative inline\n * margin, so the focus outline can hug it. */\n:root\n  [data-chassis][data-size][data-profile=\"field-action\"]\n  > .sp-button-content {\n  width: auto;\n  border-radius: var(--sp-rw-radius-xs);\n}\n\n/* A square inline action (see the inline profile below) is a bare glyph in a\n * larger target as well. It shares the glyph's margin and the two focus rules,\n * so both draw one outline around the glyph. */\n:root\n  [data-chassis][data-size][data-profile=\"inline\"][data-shape=\"square\"]\n  > .sp-button-content\n  > svg,\n:root\n  [data-chassis][data-size][data-profile=\"field-action\"]\n  > .sp-button-content\n  > svg {\n  margin-inline: 0;\n}\n\n:root\n  [data-chassis][data-size][data-profile=\"inline\"][data-shape=\"square\"]:focus-visible,\n:root [data-chassis][data-size][data-profile=\"field-action\"]:focus-visible {\n  outline: none;\n}\n\n:root\n  [data-chassis][data-size][data-profile=\"inline\"][data-shape=\"square\"]:focus-visible\n  > .sp-button-content,\n:root\n  [data-chassis][data-size][data-profile=\"field-action\"]:focus-visible\n  > .sp-button-content {\n  outline: var(--sp-semantic-state-focus-width) solid var(--sp-rw-border-focus);\n  outline-offset: var(--sp-semantic-state-focus-offset);\n}\n\n:root [data-chassis][data-size][data-profile=\"accordion-trigger\"] {\n  width: 100%;\n  height: auto;\n  min-height: var(--sp-rw-space-10);\n  align-items: center;\n  gap: var(--sp-rw-space-2);\n  border-radius: var(--sp-rw-radius-sm);\n  padding: var(--sp-rw-space-2);\n  background: transparent;\n  color: var(--sp-rw-fg-primary);\n  opacity: 1;\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  transition:\n    background-color var(--motion-duration-fast) var(--motion-ease-state),\n    color var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root\n  [data-chassis][data-profile=\"accordion-trigger\"]:hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root\n  [data-chassis][data-profile=\"accordion-trigger\"]:where(\n    :active,\n    [data-pressed]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]) {\n  background: var(--sp-rw-glow-pressed);\n  opacity: 1;\n  transform: none;\n}\n\n:root [data-chassis][data-profile=\"collapsible-trigger\"][data-variant=\"text\"] {\n  opacity: 1;\n}\n\n:root\n  [data-chassis][data-profile=\"collapsible-trigger\"][data-variant=\"text\"]:hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root\n  [data-chassis][data-profile=\"collapsible-trigger\"][data-variant=\"text\"]:where(\n    :active,\n    [data-pressed]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]) {\n  background: var(--sp-rw-glow-pressed);\n  opacity: 1;\n  transform: none;\n}\n\n/* Step trigger: the header of a Stepper step, a text button around a marker\n * and its text. The box reaches one bleed past that content on every side, so\n * the focus outline clears the marker's halo, and an equal negative margin keeps\n * the content where a read-only step puts it: the marker stays on the connector.\n * The height follows the content and the text wraps, as in a read-only step. */\n:root [data-chassis][data-size][data-profile=\"step-trigger\"] {\n  --sp-rw-step-trigger-bleed: var(--sp-rw-space-1);\n\n  display: flex;\n  justify-content: flex-start;\n  gap: var(--sp-rw-space-3);\n  width: calc(100% + var(--sp-rw-step-trigger-bleed) * 2);\n  height: auto;\n  min-height: 0;\n  margin: calc(var(--sp-rw-step-trigger-bleed) * -1);\n  padding: var(--sp-rw-step-trigger-bleed);\n  text-align: start;\n  white-space: normal;\n}\n\n/* The marker stays at the top, level with the first line of the title. */\n:root\n  [data-chassis][data-size][data-profile=\"step-trigger\"]\n  > .sp-button-content {\n  align-items: flex-start;\n}\n\n:root [data-chassis][data-size][data-profile=\"compact-role-control\"] {\n  justify-content: flex-start;\n  background: transparent;\n  box-shadow: none;\n}\n\n:root\n  [data-chassis][data-profile=\"compact-role-control\"]:where(\n    :hover,\n    :active,\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  background: transparent;\n}\n\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"menu-row\"],\n    [data-profile=\"submenu-trigger\"]\n  ) {\n  /* The icon of the menu row slot (I8), at every control size. */\n  --sp-rw-button-icon: var(--sp-rw-menu-row-icon);\n\n  width: 100%;\n  min-width: 0;\n  /* The row height of the shared menu-row recipe: the Button base rule would\n   * otherwise leave these rows a pixel taller than the check and radio rows. */\n  min-height: var(--sp-rw-control-md);\n  justify-content: flex-start;\n  padding-block: var(--sp-rw-space-1);\n  padding-inline: var(--sp-rw-space-2);\n  text-align: start;\n  white-space: normal;\n}\n\n/* The shared recipe makes every menu row 48 px high for a coarse pointer, but\n * the Button base rule outranks it for these rows, which stayed at 32 px next to\n * the 48 px check and radio rows. They take the touch height here. */\n@media (pointer: coarse) {\n  :root\n    [data-chassis][data-size]:is(\n      [data-profile=\"menu-row\"],\n      [data-profile=\"submenu-trigger\"]\n    ) {\n    min-height: var(--sp-rw-control-xl);\n    padding-block: var(--sp-rw-space-3);\n  }\n}\n\n/* Rulebook I8: a menu keeps the leading slot only while one of its rows leads\n * with a mark: an icon, an avatar, a check or a radio. In a menu of text and\n * switch rows, every label starts at the row padding.\n * Switch rows and radio rows with the indicator at the end read the variable\n * (choice-menu-item.tsx) and count when they lead with an element; check rows\n * and the other radio rows always lead with their indicator. */\n:root\n  .sp-menu-popup:not(\n    :has(\n      [data-slot$=\"radio-item\"]:not([data-indicator-position=\"end\"]),\n      [data-slot$=\"radio-item\"][data-indicator-position=\"end\"]\n        > span:first-child\n        > :not(.sp-menu-row-label):first-child,\n      [data-slot$=\"checkbox-item\"]:not(\n        [data-variant=\"switch\"],\n        .sp-base-switch\n      ),\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > :is(svg, .sp-avatar):first-child,\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > .sp-button-content\n        > :is(svg, .sp-avatar):first-child,\n      [data-slot$=\"checkbox-item\"] > span:first-child > svg:first-child\n    )\n  ),\n:root\n  .sp-menu-sub-content:not(\n    :has(\n      [data-slot$=\"radio-item\"]:not([data-indicator-position=\"end\"]),\n      [data-slot$=\"radio-item\"][data-indicator-position=\"end\"]\n        > span:first-child\n        > :not(.sp-menu-row-label):first-child,\n      [data-slot$=\"checkbox-item\"]:not(\n        [data-variant=\"switch\"],\n        .sp-base-switch\n      ),\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > :is(svg, .sp-avatar):first-child,\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > .sp-button-content\n        > :is(svg, .sp-avatar):first-child,\n      [data-slot$=\"checkbox-item\"] > span:first-child > svg:first-child\n    )\n  ),\n:root\n  .sp-context-menu-popup:not(\n    :has(\n      [data-slot$=\"radio-item\"]:not([data-indicator-position=\"end\"]),\n      [data-slot$=\"radio-item\"][data-indicator-position=\"end\"]\n        > span:first-child\n        > :not(.sp-menu-row-label):first-child,\n      [data-slot$=\"checkbox-item\"]:not(\n        [data-variant=\"switch\"],\n        .sp-base-switch\n      ),\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > :is(svg, .sp-avatar):first-child,\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > .sp-button-content\n        > :is(svg, .sp-avatar):first-child,\n      [data-slot$=\"checkbox-item\"] > span:first-child > svg:first-child\n    )\n  ),\n:root\n  .sp-context-menu-sub-content:not(\n    :has(\n      [data-slot$=\"radio-item\"]:not([data-indicator-position=\"end\"]),\n      [data-slot$=\"radio-item\"][data-indicator-position=\"end\"]\n        > span:first-child\n        > :not(.sp-menu-row-label):first-child,\n      [data-slot$=\"checkbox-item\"]:not(\n        [data-variant=\"switch\"],\n        .sp-base-switch\n      ),\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > :is(svg, .sp-avatar):first-child,\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > .sp-button-content\n        > :is(svg, .sp-avatar):first-child,\n      [data-slot$=\"checkbox-item\"] > span:first-child > svg:first-child\n    )\n  ),\n:root\n  .sp-drawer-menu:not(\n    :has(\n      [data-slot$=\"radio-item\"]:not([data-indicator-position=\"end\"]),\n      [data-slot$=\"radio-item\"][data-indicator-position=\"end\"]\n        > span:first-child\n        > :not(.sp-menu-row-label):first-child,\n      [data-slot$=\"checkbox-item\"]:not(\n        [data-variant=\"switch\"],\n        .sp-base-switch\n      ),\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > :is(svg, .sp-avatar):first-child,\n      :is(\n          [data-slot$=\"-item\"],\n          [data-slot$=\"sub-trigger\"],\n          .sp-drawer-menu-trigger\n        )\n        > .sp-button-content\n        > :is(svg, .sp-avatar):first-child,\n      [data-slot$=\"checkbox-item\"] > span:first-child > svg:first-child\n    )\n  ) {\n  --sp-rw-menu-row-lead: 0;\n}\n\n/* The check, radio and switch rows of a DrawerMenu take the slot of the Button\n * rows above: 8 px of inline padding and the Button content gap. A check or\n * radio indicator then sits where the icons of the other rows sit, a switch\n * row keeps that slot before its label, and every label keeps its x (rulebook\n * I8). */\n:root .sp-drawer-menu-checkbox-item,\n:root .sp-drawer-menu-radio-item {\n  --sp-rw-menu-row-padding-inline: var(--sp-rw-space-2);\n  --sp-rw-menu-row-gap: var(--sp-rw-button-content-gap);\n}\n\n:root\n  [data-chassis]:is(\n    [data-profile=\"menu-row\"],\n    [data-profile=\"submenu-trigger\"]\n  )[data-variant=\"text\"]:where(\n    :hover,\n    [data-highlighted],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  background: var(--accent);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  [data-chassis]:is(\n    [data-profile=\"menu-row\"],\n    [data-profile=\"submenu-trigger\"]\n  )[data-variant=\"text\"]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: color-mix(in oklab, var(--accent) 95%, var(--foreground) 5%);\n}\n\n/* Destructive Drawer rows use the red Button layer, not the neutral Menu fill. */\n:root\n  [data-chassis][data-profile=\"menu-row\"][data-variant=\"ghost-destructive\"] {\n  background: transparent;\n  color: var(--sp-rw-fg-destructive);\n}\n\n:root [data-chassis][data-size][data-profile=\"full-cell-action\"] {\n  border: 0;\n  border-radius: 0;\n  background: transparent;\n  box-shadow: none;\n}\n\n:root\n  [data-chassis][data-profile=\"full-cell-action\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: color-mix(in oklab, var(--foreground) 4%, transparent);\n}\n\n:root\n  [data-chassis][data-profile=\"full-cell-action\"]:where(\n    :active,\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  background: color-mix(in oklab, var(--foreground) 5%, transparent);\n}\n\n:root [data-chassis][data-profile=\"full-cell-action\"]:focus-visible {\n  outline: 0;\n  box-shadow: inset 0 0 0 var(--sp-semantic-state-focus-width)\n    var(--sp-rw-border-focus);\n}\n\n:root [data-chassis][data-size][data-profile=\"tabs-tab\"] {\n  height: var(--sp-rw-control-md);\n  min-height: var(--sp-rw-control-md);\n  gap: var(--sp-rw-space-1);\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  padding-inline: var(--sp-rw-space-3);\n  background: transparent;\n  box-shadow: none;\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  transition: color var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root [data-chassis][data-profile=\"tabs-tab\"][data-profile-mode=\"pill\"] {\n  border-radius: var(--sp-rw-radius-full);\n  padding-block: 0;\n  font-weight: 400;\n}\n\n:root\n  [data-chassis][data-profile=\"tabs-tab\"]:hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root [data-chassis][data-profile=\"tabs-tab\"][data-active] {\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  [data-chassis][data-profile=\"tabs-tab\"]:where(:active, [data-pressed]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: transparent;\n  opacity: 1;\n  transform: none;\n}\n\n:root [data-chassis][data-profile=\"tabs-tab\"]:focus-visible {\n  outline-offset: calc(var(--sp-semantic-state-focus-width) * -1);\n}\n\n:root [data-chassis][data-profile=\"tabs-tab\"][data-size=\"sm\"] {\n  height: var(--sp-rw-control-sm);\n  min-height: var(--sp-rw-control-sm);\n  padding-inline: var(--sp-rw-space-2);\n}\n\n:root [data-chassis][data-profile=\"tabs-tab\"][data-size=\"lg\"] {\n  height: var(--sp-rw-control-lg);\n  min-height: var(--sp-rw-control-lg);\n}\n\n/* The tab icons take the Button icon of their size: 16 px, 20 px at xl. */\n:root [data-chassis][data-profile=\"tabs-tab\"][data-size=\"xl\"] {\n  height: var(--sp-rw-control-xl);\n  min-height: var(--sp-rw-control-xl);\n  padding-inline: var(--sp-rw-space-4);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root [data-chassis][data-size][data-profile=\"task-pill\"] {\n  width: 100%;\n  min-width: 0;\n  height: var(--sp-rw-space-6);\n  justify-content: flex-start;\n  overflow: hidden;\n  border-width: var(--sp-rw-border-default);\n  border-radius: var(--radius-md);\n  padding-inline: var(--sp-rw-space-1-5);\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-16);\n  text-align: start;\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-1\"] {\n  border-color: color-mix(in oklab, var(--chart-1) 24%, transparent);\n  background: color-mix(in oklab, var(--chart-1) 8%, transparent);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-2\"] {\n  border-color: color-mix(in oklab, var(--chart-2) 24%, transparent);\n  background: color-mix(in oklab, var(--chart-2) 8%, transparent);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-3\"] {\n  border-color: color-mix(in oklab, var(--chart-3) 24%, transparent);\n  background: color-mix(in oklab, var(--chart-3) 8%, transparent);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-4\"] {\n  border-color: color-mix(in oklab, var(--chart-4) 24%, transparent);\n  background: color-mix(in oklab, var(--chart-4) 8%, transparent);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-5\"] {\n  border-color: color-mix(in oklab, var(--chart-5) 24%, transparent);\n  background: color-mix(in oklab, var(--chart-5) 8%, transparent);\n}\n\n/* The muted pill takes the kit's muted fill and muted text, and the active\n * pill its border colour: the tokens behind the bg-muted,\n * text-muted-foreground and border-border utilities the pill was built from. */\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"muted\"] {\n  border-color: var(--input);\n  background: color-mix(\n    in oklab,\n    var(--sp-semantic-color-muted) 64%,\n    transparent\n  );\n  color: var(--sp-rw-fg-tertiary);\n}\n\n:root\n  [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"muted\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-semantic-color-muted);\n}\n\n:root\n  [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"muted\"][data-pressed]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-semantic-color-muted);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-active] {\n  border-color: var(--sp-semantic-color-border);\n  background: color-mix(\n    in oklab,\n    var(--sp-semantic-color-muted) 72%,\n    transparent\n  );\n  color: var(--foreground);\n}\n\n:root\n  [data-chassis][data-profile=\"task-pill\"]:where(:hover, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  --sp-task-pill-state-mix: 12%;\n  background: color-mix(\n    in oklab,\n    var(--sp-task-pill-tone, var(--sp-semantic-color-muted))\n      var(--sp-task-pill-state-mix),\n    transparent\n  );\n}\n\n:root\n  [data-chassis][data-profile=\"task-pill\"][data-pressed]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  --sp-task-pill-state-mix: 16%;\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-1\"] {\n  --sp-task-pill-tone: var(--chart-1);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-2\"] {\n  --sp-task-pill-tone: var(--chart-2);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-3\"] {\n  --sp-task-pill-tone: var(--chart-3);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-4\"] {\n  --sp-task-pill-tone: var(--chart-4);\n}\n\n:root [data-chassis][data-profile=\"task-pill\"][data-profile-tone=\"chart-5\"] {\n  --sp-task-pill-tone: var(--chart-5);\n}\n\n/* One navigation treatment (rulebook T5, P3): the Sidebar rows and the desktop\n * navigation items, the ghost links and triggers of a header bar and the links\n * in navigation menu content, rest in the secondary text, and hover and press\n * draw a light fill. The rules below give hover, press, an open disclosure and\n * the current destination the full foreground, and the current destination\n * keeps the persistent fill. */\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  ),\n:root\n  [data-chassis][data-variant=\"ghost\"]:is(\n    [data-profile=\"navigation-link\"],\n    .sp-navigation-menu-link\n  ) {\n  --sp-rw-button-text: var(--sp-rw-fg-secondary);\n  --sp-rw-button-interaction-hover: var(--sp-rw-interaction-hover);\n  --sp-rw-button-interaction-pressed: var(--sp-rw-interaction-pressed);\n}\n\n:root\n  [data-chassis][data-variant=\"ghost\"]:is(\n    [data-profile=\"navigation-link\"],\n    .sp-navigation-menu-link\n  ):is(\n    :hover,\n    :active,\n    [data-pressed],\n    [data-popup-open],\n    [aria-current=\"page\"]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n}\n\n/* Beside a leading icon, a navigation menu link's description starts under the\n * title text (I7): the icon, which the Button pulls 2 px out on each side, and\n * the 8 px gap put that text 4 px past the icon's width. */\n:root\n  .sp-navigation-menu-link-title:has(> svg:first-child)\n  + .sp-navigation-menu-link-description {\n  padding-inline-start: calc(\n    var(--sp-rw-button-icon, var(--sp-rw-control-icon)) + var(--sp-rw-space-1)\n  );\n}\n\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  ) {\n  width: 100%;\n  min-width: 0;\n  justify-content: flex-start;\n  overflow: hidden;\n  /* Insets belong to the control, including rows without a leading icon. A\n   * row with a menu action or badge at its end keeps room for it: the Sidebar\n   * sets --sp-rw-navigation-row-padding-end there. */\n  padding-block: 0;\n  padding-inline: var(--sp-rw-space-2)\n    var(--sp-rw-navigation-row-padding-end, var(--sp-rw-space-2));\n  text-align: start;\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-row\"][data-sidebar-variant=\"workspace\"] {\n  border-radius: calc(\n    (var(--sp-rw-radius-control) + var(--sp-rw-radius-surface)) / 2\n  );\n}\n\n/* Collapsed icon rail. Rows keep the expanded inline inset, so the leading icon\n * stays on one fixed horizontal axis, and end as squares: the row's own height\n * is its minimum width. The row still narrows with the rail's width animation\n * and stops at the square. */\n:root [data-chassis][data-size=\"sm\"][data-profile=\"navigation-row\"] {\n  --sp-rw-navigation-row-size: var(--sp-rw-control-sm);\n}\n\n:root [data-chassis][data-size=\"md\"][data-profile=\"navigation-row\"] {\n  --sp-rw-navigation-row-size: var(--sp-rw-control-md);\n}\n\n:root [data-chassis][data-size=\"lg\"][data-profile=\"navigation-row\"] {\n  --sp-rw-navigation-row-size: var(--sp-rw-control-lg);\n}\n\n:root [data-chassis][data-size=\"xl\"][data-profile=\"navigation-row\"] {\n  --sp-rw-navigation-row-size: var(--sp-rw-control-xl);\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-chassis][data-size][data-profile=\"navigation-row\"] {\n  min-width: var(--sp-rw-navigation-row-size);\n  /* The rail hides the menu action and badge, so it keeps no room for them. */\n  padding-inline-end: var(--sp-rw-space-2);\n}\n\n/* A row follows a change of its size at the sidebar's own pace, as shadcn's\n * menu buttons do (transition-[width,height,padding]): its height, padding and\n * text, and the size of an Avatar in it. A two-line row that steps from xl to\n * the rail's lg square as the sidebar collapses shrinks with the rail's width,\n * and the rows below glide up with it instead of jumping (FB1-4, Lukas,\n * 16 October 2026). Reduced motion makes it instant through the duration\n * token (M5). */\n:root [data-chassis][data-profile=\"navigation-row\"] {\n  transition-property: width, height, padding, font-size, line-height;\n  transition-duration: var(--motion-duration-base);\n  transition-timing-function: var(--motion-ease-linear);\n}\n\n:root [data-chassis][data-profile=\"navigation-row\"] .sp-avatar {\n  transition-property: width, height;\n  transition-duration: var(--motion-duration-base);\n  transition-timing-function: var(--motion-ease-linear);\n}\n\n/* In the collapsed rail a row paints only its leading icon. The label and any\n * trailing content stay mounted for assistive technology and fade with the\n * rail's width animation (as group labels do), so hiding never depends on the\n * row being narrower than icon, gap and label. */\n:root\n  [data-chassis][data-profile=\"navigation-row\"]\n  > .sp-button-content\n  > :not(:first-child) {\n  transition: opacity var(--motion-duration-base) var(--motion-ease-linear);\n}\n\n:root\n  .sp-sidebar[data-collapsible=\"icon\"]\n  [data-chassis][data-profile=\"navigation-row\"]\n  > .sp-button-content\n  > :not(:first-child) {\n  opacity: 0;\n}\n\n/* Explicit icon sizes must not re-enable Button's negative SVG margins. */\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  )\n  > .sp-button-content\n  svg {\n  margin-inline: 0;\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-row\"]\n  > .sp-button-content\n  > svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n}\n\n/* Leading icons use their actual size; the row supplies the surrounding inset. */\n:root\n  [data-chassis][data-profile=\"navigation-row\"]\n  > .sp-button-content\n  > svg:first-child {\n  width: var(--sp-rw-space-5);\n  margin-inline: 0;\n}\n\n/* The leading icon steps through its own ladder, one step behind the label:\n * tertiary at rest, secondary on hover or press, primary on the current row. */\n:root\n  [data-chassis][data-profile=\"navigation-row\"]\n  > .sp-button-content\n  > svg:first-child {\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-row\"]:where(\n    :hover,\n    :active,\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"])\n  > .sp-button-content\n  > svg:first-child {\n  color: var(--sp-rw-fg-secondary);\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-row\"][data-active=\"true\"]\n  > .sp-button-content\n  > svg:first-child {\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-sub-row\"]\n  > .sp-button-content\n  > svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n}\n\n/* A row's text ends with an ellipsis wherever it sits (K5): a span after the\n * row's icon or avatar, also before a trailing chevron, or the row's only\n * span. Only a span that ends the row truncated before, so a label before a\n * chevron pushed the chevron out of the row. Kit parts with a slot of their\n * own, such as an Avatar, a Badge or the two-line text, keep their sizing. */\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  )\n  > .sp-button-content\n  > span:not(:first-child, [data-slot]),\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  )\n  > .sp-button-content\n  > span:only-child:not([data-slot]) {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  ):where(:hover, :active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ),\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  )[data-active=\"true\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  color: var(--sidebar-accent-foreground);\n}\n\n/* One persistent current-destination fill (rulebook T5): the Sidebar rows\n * (data-active), the navigation links (aria-current) of the mobile menu and\n * of the header bar, the links in navigation menu content, and the selected\n * or current rows of a ResourceList. The shared interaction layer draws it at\n * the selected step of the rows ladder at rest, on hover and pressed, in the\n * color of each Button's own interaction layer. */\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-row\"],\n    [data-profile=\"navigation-sub-row\"]\n  )[data-active=\"true\"]:not(:disabled, [aria-disabled=\"true\"]),\n:root\n  [data-chassis]:is(\n    [data-profile=\"navigation-link\"],\n    .sp-navigation-menu-link\n  )[aria-current=\"page\"]:not(:disabled, [aria-disabled=\"true\"]),\n:root\n  [data-chassis][data-resource-list-item]:is(\n    [data-selected],\n    [data-active]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-interaction-rest: var(--sp-rw-interaction-selected);\n  --sp-rw-button-interaction-hover: var(--sp-rw-interaction-selected);\n  --sp-rw-button-interaction-pressed: var(--sp-rw-interaction-selected);\n}\n\n:root [data-chassis][data-profile=\"compact-icon-action\"] {\n  --sp-rw-button-interaction-hover: 0.08;\n  --sp-rw-button-interaction-pressed: 0.1;\n  color: var(--sidebar-foreground);\n}\n\n:root\n  [data-chassis][data-profile=\"compact-icon-action\"]:where(\n    :hover,\n    :active,\n    [data-popup-open],\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  color: var(--sidebar-accent-foreground);\n}\n\n/* Text icon actions have no interaction child; keep their one root fill. */\n:root\n  [data-chassis][data-profile=\"compact-icon-action\"][data-variant=\"text\"]:where(\n    :hover,\n    :active,\n    [data-popup-open],\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  background: var(--sidebar-accent);\n}\n\n/* Every overlay close keeps one 16 px glyph in its 24 px box, whatever size\n * its host names. It rests in the muted text colour and takes the foreground\n * on hover. */\n:root [data-chassis][data-size][data-profile=\"overlay-dismiss\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon);\n\n  width: var(--sp-rw-space-6);\n  height: var(--sp-rw-space-6);\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 0.8;\n  transition: opacity var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root\n  [data-chassis][data-profile=\"overlay-dismiss\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  color: var(--foreground);\n  opacity: 1;\n}\n\n:root [data-chassis][data-size][data-profile=\"chip-remove\"] {\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n  border: 0;\n  border-radius: var(--sp-rw-radius-xs);\n  padding: 0;\n  background: transparent;\n  color: var(--sp-rw-fg-tertiary);\n}\n\n:root\n  [data-chassis][data-profile=\"chip-remove\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root [data-chassis][data-profile=\"chip-remove\"]:focus-visible {\n  box-shadow: none;\n}\n\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ) {\n  padding-inline: calc(var(--spacing) * 3 - 1px);\n  color: var(--sp-rw-fg-secondary);\n  transition: background-color var(--motion-duration-fast)\n    var(--motion-ease-state);\n}\n\n:root [data-chassis][data-size][data-profile=\"joined-start\"] {\n  border-start-start-radius: calc(\n    var(--sp-rw-radius-control) - var(--sp-rw-border-default)\n  );\n  border-end-start-radius: calc(\n    var(--sp-rw-radius-control) - var(--sp-rw-border-default)\n  );\n}\n\n:root [data-chassis][data-size][data-profile=\"joined-end\"] {\n  border-start-end-radius: calc(\n    var(--sp-rw-radius-control) - var(--sp-rw-border-default)\n  );\n  border-end-end-radius: calc(\n    var(--sp-rw-radius-control) - var(--sp-rw-border-default)\n  );\n}\n\n/* An xl InputGroup has the surface radius; its joined buttons follow it with\n * their outer corners, as the step buttons of an xl Number Field do.\n * The group's other buttons keep the control radius. */\n:root\n  .sp-base-input-group:has(.sp-input-control[data-size=\"xl\"])\n  [data-chassis][data-size][data-profile=\"joined-start\"] {\n  border-start-start-radius: calc(\n    var(--sp-rw-radius-surface) - var(--sp-rw-border-default)\n  );\n  border-end-start-radius: calc(\n    var(--sp-rw-radius-surface) - var(--sp-rw-border-default)\n  );\n}\n\n:root\n  .sp-base-input-group:has(.sp-input-control[data-size=\"xl\"])\n  [data-chassis][data-size][data-profile=\"joined-end\"] {\n  border-start-end-radius: calc(\n    var(--sp-rw-radius-surface) - var(--sp-rw-border-default)\n  );\n  border-end-end-radius: calc(\n    var(--sp-rw-radius-surface) - var(--sp-rw-border-default)\n  );\n}\n\n/* The Number Field's size sets the padding of its joined steppers (KIT-15:\n * the field, not any sized ancestor). */\n:root\n  .sp-number-field[data-size=\"sm\"]\n  [data-chassis][data-size]:is(\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ) {\n  padding-inline: calc(var(--spacing) * 2.5 - 1px);\n}\n\n:root\n  .sp-number-field[data-size=\"xl\"]\n  [data-chassis][data-size]:is(\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ) {\n  padding-inline: calc(var(--spacing) * 3.5 - 1px);\n}\n\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ):hover:not([data-disabled], :disabled, [aria-disabled=\"true\"]) {\n  background: var(--sp-rw-glow-hover);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  [data-chassis][data-size]:is(\n    [data-profile=\"joined-start\"],\n    [data-profile=\"joined-end\"]\n  ):where(:active, [data-pressed]):not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root [data-chassis][data-size][data-profile=\"morph-trigger\"] {\n  display: grid;\n  place-items: center;\n  border: 0;\n  border-radius: var(--radius-md);\n  padding: 0;\n  background: transparent;\n  color: var(--foreground);\n}\n\n:root\n  [data-chassis][data-profile=\"morph-trigger\"]:where(\n    :hover,\n    :active,\n    [data-state=\"open\"]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  background: transparent;\n}\n\n:root [data-chassis][data-profile=\"morph-trigger\"]:disabled {\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n@media (max-width: 639px) {\n  :root [data-chassis][data-profile=\"responsive-navigation\"] {\n    aspect-ratio: 1;\n    padding: 0;\n  }\n}\n\n:root\n  [data-chassis][data-profile=\"responsive-navigation\"][data-profile-mode=\"compact\"] {\n  aspect-ratio: 1;\n  padding: 0;\n}\n\n/* Keep the canonical Button finish; the sweep paints above it, below its rim. */\n:root [data-chassis][data-profile=\"animated-fill\"] > .sp-button-chassis-rim {\n  z-index: 25;\n  border-radius: var(--sp-fill-sweep-radius);\n}\n\n:root [data-chassis][data-size][data-profile=\"animated-fill\"] {\n  --sp-fill-sweep-layer: var(--sp-rw-bg-inverse);\n  --sp-fill-sweep-hover-foreground: var(--sp-rw-fg-inverse);\n  overflow: visible;\n  isolation: isolate;\n  /* Composite the rectangular base and sweep before rounding their silhouette. */\n  border-radius: 0;\n  clip-path: inset(0 round var(--sp-fill-sweep-radius));\n  /* Flatten before clip-path antialiasing: filter is needed in Firefox and\n   * the opaque mask in WebKit, otherwise the base darkens the curved rim. */\n  filter: opacity(1);\n  mask-image: linear-gradient(#000, #000);\n  transition-property:\n    color, border-color, background-color, background-position, box-shadow;\n  transition-duration: var(--sp-fill-sweep-duration);\n  transition-timing-function: var(--motion-ease-soft-in-out);\n}\n\n:root .sp-fill-sweep-button-root:has(:focus-visible) {\n  border-radius: var(--sp-fill-sweep-radius);\n}\n\n:root [data-chassis][data-profile=\"animated-fill\"] > .sp-button-content {\n  position: static;\n  z-index: auto;\n}\n\n:root [data-chassis][data-profile=\"animated-fill\"][data-variant=\"inverse\"] {\n  --sp-fill-sweep-layer: var(--sp-rw-bg-secondary);\n  --sp-fill-sweep-hover-foreground: var(--sp-rw-fg-primary);\n}\n\n:root\n  [data-chassis][data-profile=\"animated-fill\"][data-variant=\"inverse\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  --sp-rw-button-rim: var(--sp-rw-control-rim);\n}\n\n:root\n  [data-chassis][data-profile=\"animated-fill\"][data-variant=\"outline-destructive\"] {\n  --sp-fill-sweep-layer: var(--sp-rw-bg-destructive);\n  --sp-fill-sweep-hover-foreground: var(--sp-rw-fg-emphasis);\n}\n\n:root\n  [data-chassis][data-profile=\"animated-fill\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  color: var(--sp-fill-sweep-hover-foreground);\n}\n\n:root [data-chassis][data-size][data-profile=\"animated-arrow\"] {\n  position: relative;\n  display: inline-block;\n  width: max-content;\n  height: auto;\n  border: 0;\n  padding: 0;\n  color: inherit;\n  font: inherit;\n  text-decoration: none;\n  vertical-align: middle;\n}\n\n/* The bare arrow rounds its focus outline like the arrow; a surface keeps\n * its own corners. */\n:root\n  [data-chassis][data-profile=\"animated-arrow\"][data-variant=\"text\"]:focus-visible {\n  border-radius: var(--sp-expanding-arrow-radius);\n}\n\n:root [data-chassis][data-profile=\"animated-arrow\"] > .sp-button-content {\n  display: block;\n  height: 100%;\n}\n\n/* On the default surface the arrow keeps the canonical Button finish, as the\n * fill sweep does: the content gives up its own layer, so the arrow's fill\n * paints above the surface and below the rim, and the label above both. The\n * content hands the surface's corners on to the fill. */\n:root [data-chassis][data-profile=\"animated-arrow\"] > .sp-button-chassis-rim {\n  z-index: 25;\n}\n\n:root\n  [data-chassis][data-profile=\"animated-arrow\"]:not([data-variant=\"text\"])\n  > .sp-button-content {\n  position: static;\n  z-index: auto;\n  border-radius: inherit;\n}\n\n:root [data-chassis][data-size][data-profile=\"animated-expand\"] {\n  position: relative;\n  display: flex;\n  width: var(--sp-expanding-logout-closed-size);\n  height: var(--sp-expanding-logout-closed-size);\n  align-items: center;\n  justify-content: flex-start;\n  overflow: hidden;\n  border: 0;\n  border-radius: var(--sp-expanding-logout-radius);\n  padding: 0;\n  font: inherit;\n  transition-property:\n    width, border-radius, background-color, color, border-color, box-shadow,\n    transform;\n  transition-duration: var(--sp-expanding-logout-duration);\n  transition-timing-function: var(--motion-ease-soft-in-out);\n}\n\n:root [data-chassis][data-profile=\"animated-expand\"]:disabled {\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n:root\n  [data-chassis][data-profile=\"animated-expand\"]:is(:hover, :focus-visible):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  width: var(--sp-expanding-logout-open-width);\n}\n\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  [data-chassis][data-size]:is(\n    [data-profile=\"animated-fill\"],\n    [data-profile=\"animated-arrow\"],\n    [data-profile=\"animated-expand\"]\n  ) {\n  transition-duration: 0ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root\n    [data-chassis][data-size]:is(\n      [data-profile=\"animated-fill\"],\n      [data-profile=\"animated-arrow\"],\n      [data-profile=\"animated-expand\"]\n    ) {\n    transition-duration: 0ms;\n  }\n}\n\n:root [data-chassis][data-size=\"xs\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon-xs);\n\n  height: var(--sp-rw-control-xs);\n  padding: var(--sp-rw-space-1) var(--sp-rw-space-2);\n  border-radius: var(--sp-rw-radius-sm);\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-18);\n}\n\n:root [data-chassis][data-size=\"sm\"] {\n  height: var(--sp-rw-control-sm);\n  padding: var(--sp-rw-space-1) calc(var(--sp-rw-space) * 2.25);\n}\n\n:root [data-chassis][data-size=\"md\"] {\n  height: var(--sp-rw-control-md);\n}\n\n/* Autocomplete keeps Base UI behavior and derives its anchored surface from\n * the measured InputField + floating menu contracts. */\n:root .sp-autocomplete-popup {\n  overflow: hidden;\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  background: var(--sp-rw-bg-secondary);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n}\n\n:root .sp-autocomplete-popup::before {\n  display: none;\n}\n\n:root .sp-autocomplete-item {\n  min-height: var(--sp-rw-control-md);\n  gap: var(--sp-rw-space-2);\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--sp-rw-space-1) var(--sp-rw-space-2);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-autocomplete-item[data-highlighted] {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root .sp-autocomplete-item:active {\n  background: var(--sp-rw-glow-pressed);\n}\n\n/* Timeline uses the shared\n * compact content typography and separator colour without changing its\n * semantic marker API. Every marker is centred in one lead column, so the text\n * of all events and the connector share one x (rulebook I9). The column is as\n * wide as the dot, ring, diamond and bar (10 px) and widens to a custom marker\n * (20 px) for every event of a timeline that has one. */\n:where(.sp-timeline) {\n  --timeline-lead: var(--sp-rw-space-2-5);\n}\n\n:where(.sp-timeline:has(.sp-timeline-item[data-marker])) {\n  --timeline-lead: var(--sp-rw-space-5);\n}\n\n:root .sp-timeline-connector {\n  background: var(--sp-rw-border-translucent);\n}\n\n:root .sp-timeline-title {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-timeline-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-12);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-18);\n}\n\n/* The now marker (TimelineNow) is a row of the timeline with the accent dot\n * of an event. Its label sits on the line of a description, so the dot\n * centres on it (rulebook I5), in the accent at the label weight, and a line\n * in the accent's border colour runs from 8 px after it to the end of the\n * row, at least 16 px long. */\n:root .sp-timeline-now-label {\n  display: flex;\n  align-items: center;\n  gap: var(--sp-rw-space-2);\n  color: var(--sp-rw-fg-accent);\n  font-size: var(--sp-rw-font-12);\n  font-weight: var(--sp-rw-font-weight-label, 500);\n  line-height: var(--sp-rw-line-18);\n  overflow-wrap: anywhere;\n}\n\n:root .sp-timeline-now-label::after {\n  flex: 1 0 var(--sp-rw-space-4);\n  height: var(--sp-rw-border-default);\n  background: var(--sp-rw-border-accent);\n  content: \"\";\n}\n\n/* Data List: label and value rows of a description list. Horizontal by\n * default: every item is a row of the list's grid (subgrid), so the labels\n * share one column, as wide as the widest label up to 40% of the list or as\n * --data-list-label-width sets it, and the values take the rest, 16 px after\n * it. Rows are 12 px apart. The vertical orientation sets each label 4 px\n * above its value and the items 16 px apart. The defaults have no specificity,\n * so the orientation and the dividers refine them (rulebook P6). */\n:where(.sp-data-list) {\n  display: grid;\n  grid-template-columns:\n    var(--data-list-label-width, fit-content(40%))\n    minmax(0, 1fr);\n  gap: var(--sp-rw-space-3) var(--sp-rw-space-4);\n  margin: 0;\n}\n\n:where(.sp-data-list-item) {\n  display: grid;\n  grid-column: 1 / -1;\n  grid-template-columns: subgrid;\n  align-items: baseline;\n}\n\n:root .sp-data-list[data-orientation=\"vertical\"] {\n  grid-template-columns: minmax(0, 1fr);\n  row-gap: var(--sp-rw-space-4);\n}\n\n:root .sp-data-list[data-orientation=\"vertical\"] > .sp-data-list-item {\n  grid-template-columns: minmax(0, 1fr);\n  row-gap: var(--sp-rw-space-1);\n}\n\n/* Labels are the quieter text at the regular weight and values the primary\n * text, as Radix Themes and Chakra set their data lists; both wrap a long\n * unbroken value in their column (rulebook K5). */\n:root .sp-data-list-label {\n  min-width: 0;\n  margin: 0;\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  overflow-wrap: anywhere;\n}\n\n:root .sp-data-list-value {\n  min-width: 0;\n  margin: 0;\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  overflow-wrap: anywhere;\n}\n\n/* Dividers: the kit's hairline (rulebook B7) between two items, 12 px above\n * and below it; the first and the last item keep no space outside their\n * content. */\n:root .sp-data-list[data-dividers] {\n  row-gap: 0;\n}\n\n:root .sp-data-list[data-dividers] > .sp-data-list-item {\n  padding-block: var(--sp-rw-space-3);\n}\n\n:root .sp-data-list[data-dividers] > .sp-data-list-item + .sp-data-list-item {\n  border-top: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n}\n\n:root .sp-data-list[data-dividers] > .sp-data-list-item:first-child {\n  padding-top: 0;\n}\n\n:root .sp-data-list[data-dividers] > .sp-data-list-item:last-child {\n  padding-bottom: 0;\n}\n\n/* Truncate: text that ends in an ellipsis. By default it stays on one line and\n * is cut where its box ends: the box is as wide as the text, up to the width of\n * its container or the width a utility in its className sets, such as\n * max-w-1/2, which beats these rules (rulebook P6). It flows inline at the top\n * of its line, so a Truncate alone in a title leaves the line as tall as the\n * text, and in a flex or grid row it can shrink below the width of its text.\n * data-lines wraps the text and clamps it after that many lines, the count\n * --truncate-lines sets; a long word without spaces breaks inside a line\n * rather than run past it (K5). The defaults have no specificity, so the line\n * count refines them. */\n:where(.sp-truncate) {\n  display: inline-block;\n  min-width: 0;\n  max-width: 100%;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  vertical-align: top;\n  white-space: nowrap;\n}\n\n:root .sp-truncate[data-lines] {\n  display: -webkit-box;\n  overflow-wrap: anywhere;\n  white-space: normal;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: var(--truncate-lines, 2);\n}\n\n/* CopyButton: a Button whose icon is CopyRedrawIcon, a box around the two\n * strokes of the copy glyph rather than a bare svg. With a label, the inline\n * profile's icon rule above does not see that box, so this rule gives the\n * icon and the label the same flex row, spaced by the Button's content gap.\n * A failed copy shows a cross in the destructive text colour, and in the\n * Button's own text colour on a variant with a fill of its own, as the check\n * of a copy does (action-icons.css). */\n:root\n  .sp-copy-button[data-profile=\"inline\"]:has(> .sp-button-content > .sp-copy) {\n  display: inline-flex;\n}\n\n:where(.sp-copy-fail) {\n  color: var(--sp-semantic-color-destructive-foreground);\n}\n\n:root\n  .sp-copy-button:is(\n    [data-variant=\"accent\"],\n    [data-variant=\"destructive\"],\n    [data-variant=\"inverse\"],\n    [data-variant=\"glass\"]\n  )\n  .sp-copy-fail {\n  color: inherit;\n}\n\n:root [data-chassis][data-size=\"lg\"] {\n  height: var(--sp-rw-control-lg);\n}\n\n:root [data-chassis][data-size=\"xl\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon-xl);\n\n  height: var(--sp-rw-control-xl);\n  padding-inline: var(--sp-rw-space-4);\n  border-radius: var(--sp-rw-radius-surface);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* A multiline Button stays on the control scale (S1): one line is as tall as a\n * Button of the same size, and more lines grow from there. In xs, sm and\n * default the padding centres one line in that height; lg and xl keep the\n * chassis padding and take the height as their minimum. A profile with its own\n * row recipe (menu rows, accordion, stepper) outranks these rules. The first\n * rule lets the height follow the lines at every size; it outranks each size\n * rule, the icon sizes below included. */\n:root [data-chassis][data-size][data-multiline] {\n  height: auto;\n}\n\n:root [data-chassis][data-multiline]:where([data-size=\"xs\"]) {\n  padding-block: calc((var(--sp-rw-control-xs) - 1lh) / 2);\n}\n\n:root [data-chassis][data-multiline]:where([data-size=\"sm\"]) {\n  padding-block: calc((var(--sp-rw-control-sm) - 1lh) / 2);\n}\n\n:root [data-chassis][data-multiline]:where([data-size=\"md\"]) {\n  padding-block: calc((var(--sp-rw-control-md) - 1lh) / 2);\n}\n\n:root [data-chassis][data-multiline]:where([data-size=\"lg\"]) {\n  min-height: var(--sp-rw-control-lg);\n}\n\n:root [data-chassis][data-multiline]:where([data-size=\"xl\"]) {\n  min-height: var(--sp-rw-control-xl);\n}\n\n:root [data-chassis][data-size^=\"icon\"] {\n  padding: 0;\n}\n\n:root [data-chassis][data-size=\"icon-2xs\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon-2xs);\n\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n  border-radius: var(--sp-rw-radius-sm);\n}\n\n:root [data-chassis][data-size=\"icon-xs\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon-xs);\n\n  width: var(--sp-rw-control-xs);\n  height: var(--sp-rw-control-xs);\n  border-radius: var(--sp-rw-radius-sm);\n}\n\n:root [data-chassis][data-size=\"icon-sm\"] {\n  width: var(--sp-rw-control-sm);\n  height: var(--sp-rw-control-sm);\n}\n\n:root [data-chassis][data-size=\"icon-md\"] {\n  width: var(--sp-rw-control-md);\n  height: var(--sp-rw-control-md);\n}\n\n:root [data-chassis][data-size=\"icon-lg\"] {\n  width: var(--sp-rw-control-lg);\n  height: var(--sp-rw-control-lg);\n}\n\n:root [data-chassis][data-size=\"icon-xl\"] {\n  --sp-rw-button-icon: var(--sp-rw-control-icon-xl);\n\n  width: var(--sp-rw-control-xl);\n  height: var(--sp-rw-control-xl);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* Pill shape: the one place that rounds a Button fully. Only the radius changes;\n * the size keeps its height and padding, and every variant and state keeps its\n * own styling. The effect layers and ::before inherit this radius and the shared\n * focus outline follows the border box, so the layers and the keyboard focus\n * ring turn pill with the chassis. It ties the size rules above in specificity\n * and wins by following them; profiles and Groups that own their geometry\n * (avatar, field-action, joined edges) stay more specific and keep it. */\n:root [data-chassis][data-shape=\"pill\"] {\n  border-radius: var(--sp-rw-radius-full);\n}\n\n/* The default Button is opaque, as in the established kits (Untitled UI\n * Secondary, shadcn outline, Mantine default): the tertiary surface, white in\n * Light and a slightly raised neutral in Dark, with the control's hairline rim\n * and soft shadow. Nothing behind it shows through, so it reads over an image\n * or a colour as well as on a page, a Card or a Dialog. The Dark fill is the\n * next step of the surface ladder, 27.2 % lightness over the page, primary and\n * secondary surfaces (19.6, 21.7 and 24.3 %): a button never looks darker than\n * the surface it sits on, and never the brightest surface either (Lukas,\n * 13 October 2026; it was 33.3 %). */\n:root [data-chassis][data-variant=\"default\"] {\n  --sp-rw-button-fill: var(--sp-rw-bg-tertiary);\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  --sp-rw-button-depth: var(--sp-rw-control-depth);\n  --sp-rw-button-interaction: var(--sp-rw-control-interaction);\n  --sp-rw-button-interaction-hover: var(--sp-rw-interaction-on-fill-hover);\n  --sp-rw-button-interaction-pressed: var(--sp-rw-interaction-on-fill-pressed);\n  --sp-rw-button-spotlights: var(--sp-rw-control-spotlights);\n  --sp-rw-button-dome: var(--sp-rw-control-dome);\n  --sp-rw-button-rim: var(--sp-rw-control-rim);\n  --sp-rw-button-micro: var(--sp-rw-control-micro);\n}\n\n/* The six layers of the chassis, each named, so the rule is looked up on them\n * alone. */\n:root [data-chassis] > .sp-button-chassis-drop-shadow,\n:root [data-chassis] > .sp-button-chassis-interaction,\n:root [data-chassis] > .sp-button-chassis-spotlights,\n:root [data-chassis] > .sp-button-chassis-dome-overlay,\n:root [data-chassis] > .sp-button-chassis-rim,\n:root [data-chassis] > .sp-button-chassis-secondary-spotlights {\n  position: absolute;\n  inset: 0;\n  display: block;\n  border-radius: inherit;\n  pointer-events: none;\n}\n\n:root [data-chassis] > .sp-button-chassis-drop-shadow {\n  z-index: -1;\n  box-shadow: var(--sp-rw-button-depth);\n}\n\n:root [data-chassis] > .sp-button-chassis-interaction {\n  z-index: 0;\n  background: var(--sp-rw-button-interaction);\n  opacity: var(--sp-rw-button-interaction-rest);\n}\n\n:root [data-chassis] > .sp-button-chassis-spotlights {\n  z-index: 1;\n  box-shadow: var(--sp-rw-button-spotlights);\n}\n\n:root [data-chassis] > .sp-button-chassis-dome-overlay {\n  z-index: 2;\n  background: var(--sp-rw-button-dome);\n}\n\n:root [data-chassis] > .sp-button-chassis-rim {\n  z-index: 3;\n  box-shadow: var(--sp-rw-button-rim);\n}\n\n:root [data-chassis] > .sp-button-chassis-secondary-spotlights {\n  z-index: 4;\n  box-shadow: var(--sp-rw-button-micro);\n}\n\n:root [data-chassis] > .sp-button-content {\n  position: relative;\n  z-index: 30;\n  display: inline-flex;\n  width: 100%;\n  min-width: 0;\n  align-items: center;\n  justify-content: inherit;\n  gap: inherit;\n}\n\n/* A Button of the default profile is never wider than the space it sits in,\n * and its label (button.tsx) stays on one line. In a narrower space the label\n * may break between any two characters, and the clamp shows its first line,\n * filled to the edge, with an ellipsis, while its title carries the full text\n * (rulebook K5). A label that fits shows whole, and a multiline Button wraps.\n * A Button of an icon size holds a glyph or a short figure, such as a page\n * number that widens its square, and never cuts it. The label's line box is\n * the Button's, which holds its descenders. */\n:where([data-chassis][data-profile=\"default\"]) {\n  max-width: 100%;\n}\n\n:where(\n    [data-chassis][data-profile=\"default\"]:not(\n        [data-multiline],\n        [data-size^=\"icon-\"]\n      )\n      > .sp-button-content\n  )\n  > .sp-button-label {\n  display: -webkit-box;\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: normal;\n  word-break: break-all;\n  -webkit-box-orient: vertical;\n  -webkit-line-clamp: 1;\n}\n\n/* In a Button whose text wraps, a leading icon or check (the ListBox check\n * leads its row) centres on the first line (I5); a trailing chevron keeps the\n * row centre. The icon keeps the size its owner gives it and moves by half the\n * difference to the line. */\n:root\n  [data-chassis][data-size][data-multiline]\n  > .sp-button-content\n  > svg:first-child:not(\n    .sp-accordion-indicator,\n    [data-slot=\"collapsible-chevron\"]\n  ) {\n  align-self: flex-start;\n  translate: 0 calc((1lh - 100%) / 2);\n}\n\n:root [data-chassis] > :where(.sp-button-chassis-drop-shadow),\n:root [data-chassis] > :where(.sp-button-chassis-interaction),\n:root [data-chassis] > :where(.sp-button-chassis-spotlights),\n:root [data-chassis] > :where(.sp-button-chassis-secondary-spotlights) {\n  transition: opacity var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root\n  [data-chassis]:hover:not(:active, :disabled, [aria-disabled=\"true\"])\n  > .sp-button-chassis-interaction {\n  opacity: var(--sp-rw-button-interaction-hover);\n}\n\n:root\n  [data-chassis]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  )\n  > .sp-button-chassis-interaction {\n  opacity: var(--sp-rw-button-interaction-pressed);\n}\n\n:root\n  [data-chassis][data-popup-open]:not(:disabled, [aria-disabled=\"true\"])\n  > .sp-button-chassis-interaction {\n  opacity: var(--sp-rw-button-interaction-pressed);\n}\n\n/* Push layout: the Sidebar stays a fixed layer over the content surface, and\n * SidebarInset moves its content clear of it by the expanded width beyond the\n * icon rail (--sidebar-content-offset; 0 when collapsed or below the mobile\n * breakpoint), at the Sidebar's own pace, so the content edge and the sidebar\n * edge stay in step. The surface itself stays put. Reduced motion makes the\n * duration token 1 ms (defaults.css). */\n:root .sp-sidebar-inset[data-layout=\"push\"] {\n  padding-inline-start: var(--sidebar-content-offset);\n  transition: padding-inline-start var(--motion-duration-base)\n    var(--motion-ease-linear);\n}\n\n/* The rail is a hit area along the whole sidebar edge: its classes place it\n * from the top to the bottom of the sidebar as an 8 px strip on the outer side\n * of the edge, clear of the sidebar's scrollbar, so it takes no height or\n * padding from the Button size rules. */\n:root [data-chassis][data-size][data-profile=\"sidebar-rail\"] {\n  height: auto;\n  padding: 0;\n  background: transparent;\n}\n\n:root\n  [data-chassis][data-profile=\"sidebar-rail\"]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  cursor: default;\n}\n\n/* The desktop sidebar (the aside) carries its side, state and collapse mode;\n * the mobile sheet carries none of the state, so its rail keeps the default\n * cursor. A rail beside a collapsed offcanvas sidebar shows a strip on hover\n * in the hover glow of the sidebar's rows and menus, and a resizable rail\n * shows which way the edge moves. */\n:root\n  .sp-sidebar[data-collapsible=\"offcanvas\"]\n  [data-chassis][data-profile=\"sidebar-rail\"]:hover:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root\n  .sp-sidebar[data-side=\"left\"][data-state=\"expanded\"]\n  [data-chassis][data-profile=\"sidebar-rail\"][data-resizable] {\n  cursor: w-resize;\n}\n\n:root\n  .sp-sidebar[data-side=\"right\"][data-state=\"expanded\"]\n  [data-chassis][data-profile=\"sidebar-rail\"][data-resizable] {\n  cursor: e-resize;\n}\n\n:root\n  .sp-sidebar[data-side=\"left\"][data-state=\"collapsed\"]\n  [data-chassis][data-profile=\"sidebar-rail\"][data-resizable] {\n  cursor: e-resize;\n}\n\n:root\n  .sp-sidebar[data-side=\"right\"][data-state=\"collapsed\"]\n  [data-chassis][data-profile=\"sidebar-rail\"][data-resizable] {\n  cursor: w-resize;\n}\n\n/* A right-to-left page mirrors the side, so the edge moves the other way.\n * The dir attribute is named beside :dir(rtl), which a build for older\n * browsers turns into a list of right-to-left languages. */\n:root\n  .sp-sidebar[data-side=\"left\"][data-state=\"expanded\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  )\n  .sp-sidebar-rail[data-resizable],\n:root\n  .sp-sidebar[data-side=\"right\"][data-state=\"collapsed\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  )\n  .sp-sidebar-rail[data-resizable] {\n  cursor: e-resize;\n}\n\n:root\n  .sp-sidebar[data-side=\"right\"][data-state=\"expanded\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  )\n  .sp-sidebar-rail[data-resizable],\n:root\n  .sp-sidebar[data-side=\"left\"][data-state=\"collapsed\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  )\n  .sp-sidebar-rail[data-resizable] {\n  cursor: w-resize;\n}\n\n:root\n  [data-chassis]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  )\n  > :where(.sp-button-chassis-drop-shadow),\n:root\n  [data-chassis]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  )\n  > :where(.sp-button-chassis-spotlights),\n:root\n  [data-chassis]:where(:active, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  )\n  > :where(.sp-button-chassis-secondary-spotlights) {\n  opacity: 0;\n}\n\n:root\n  [data-chassis]:active:not(:disabled, [aria-disabled=\"true\"])\n  > :where(.sp-button-chassis-drop-shadow),\n:root\n  [data-chassis]:active:not(:disabled, [aria-disabled=\"true\"])\n  > :where(.sp-button-chassis-interaction),\n:root\n  [data-chassis]:active:not(:disabled, [aria-disabled=\"true\"])\n  > :where(.sp-button-chassis-spotlights),\n:root\n  [data-chassis]:active:not(:disabled, [aria-disabled=\"true\"])\n  > :where(.sp-button-chassis-secondary-spotlights) {\n  transition-duration: 0s;\n}\n\n@media (hover: hover) {\n  :root\n    [data-chassis]:hover:not(:disabled, [aria-disabled=\"true\"])\n    > :where(.sp-button-chassis-drop-shadow),\n  :root\n    [data-chassis]:hover:not(:disabled, [aria-disabled=\"true\"])\n    > :where(.sp-button-chassis-interaction),\n  :root\n    [data-chassis]:hover:not(:disabled, [aria-disabled=\"true\"])\n    > :where(.sp-button-chassis-spotlights),\n  :root\n    [data-chassis]:hover:not(:disabled, [aria-disabled=\"true\"])\n    > :where(.sp-button-chassis-secondary-spotlights) {\n    transition-duration: 0s;\n  }\n}\n\n@media (hover: none) {\n  :root [data-chassis]:active:not(:disabled, [aria-disabled=\"true\"]) {\n    opacity: 0.8;\n  }\n}\n\n:root\n  [data-chassis]:where([data-variant=\"accent\"], [data-variant=\"destructive\"]) {\n  --sp-rw-button-fill: var(--sp-rw-button-source);\n  --sp-rw-button-text: var(--sp-rw-fg-emphasis);\n  --sp-rw-button-depth:\n    0 2px 2px -1px\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.12)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.24)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      ),\n    0 4px 4px -2px\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.12)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.24)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      );\n  --sp-rw-button-interaction: light-dark(\n    rgb(\n      from oklch(from var(--sp-rw-button-source) calc(l - 0.1) c h / 1)\n        clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n    ),\n    rgb(\n      from\n        oklch(\n          from var(--sp-rw-button-source) calc(l + 0.15) calc(c * 0.9) h / 0.7\n        )\n        clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n    )\n  );\n  --sp-rw-button-interaction-hover: 0.4;\n  --sp-rw-button-interaction-pressed: 0.64;\n  --sp-rw-button-spotlights:\n    inset 0 1px 0 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.16)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      ),\n    inset 0 -1px 0 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.08)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      ),\n    inset 0 0.5px 0 0\n      light-dark(\n        transparent,\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.16)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      ),\n    inset 0 -0.5px 0 0\n      light-dark(\n        transparent,\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.08)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      );\n  --sp-rw-button-dome: linear-gradient(\n    to bottom in srgb,\n    light-dark(transparent, oklch(100% 0 0 / 8%)),\n    light-dark(oklch(0% 0 0 / 8%), transparent)\n  );\n  --sp-rw-button-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(\n      rgb(\n        from\n          oklch(from var(--sp-rw-button-source) calc(l - 0.2) calc(c * 0.75) h)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      ),\n      rgb(\n        from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.32)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      )\n    );\n  --sp-rw-button-micro:\n    inset 0 0.5px 0 light-dark(oklch(100% 0 0 / 8%), transparent),\n    inset 0 -0.5px 0 light-dark(oklch(0% 0 0 / 16%), transparent);\n}\n\n:root [data-chassis][data-variant=\"accent\"] {\n  --sp-rw-button-source: var(--sp-rw-bg-accent);\n  --sp-rw-button-text: var(--sp-rw-fg-on-accent);\n}\n\n:root [data-chassis][data-variant=\"destructive\"] {\n  --sp-rw-button-source: var(--sp-rw-bg-destructive);\n}\n\n:root:where(.dark)\n  [data-chassis]:where([data-variant=\"accent\"], [data-variant=\"destructive\"]),\n:root:where([data-theme=\"dark\"])\n  [data-chassis]:where([data-variant=\"accent\"], [data-variant=\"destructive\"]),\n:root\n  :where(.dark)\n  [data-chassis]:where([data-variant=\"accent\"], [data-variant=\"destructive\"]),\n:root\n  :where([data-theme=\"dark\"])\n  [data-chassis]:where([data-variant=\"accent\"], [data-variant=\"destructive\"]) {\n  --sp-rw-button-interaction-pressed: 0.8;\n}\n\n:root [data-chassis][data-variant=\"inverse\"] {\n  --sp-rw-button-source: var(--sp-rw-bg-inverse);\n  --sp-rw-button-fill: var(--sp-rw-button-source);\n  --sp-rw-button-text: var(--sp-rw-fg-inverse);\n  --sp-rw-button-depth:\n    0 2px 2px -1px\n      rgb(\n        from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.12)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      ),\n    0 4px 4px -2px\n      rgb(\n        from oklch(from var(--sp-rw-button-source) 0.2 calc(c * 1.2) h / 0.12)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      );\n  --sp-rw-button-interaction: light-dark(oklch(100% 0 0), oklch(0% 0 0));\n  --sp-rw-button-interaction-hover: 0.08;\n  --sp-rw-button-interaction-pressed: 0.12;\n  --sp-rw-button-spotlights:\n    inset 0 1px 0 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.24)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      ),\n    inset 0 -1px 0 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 1)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      ),\n    inset 0 0.5px 0 0\n      light-dark(\n        transparent,\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 1)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      ),\n    inset 0 -0.5px 0 0\n      light-dark(\n        transparent,\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 1)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        )\n      );\n  --sp-rw-button-dome: linear-gradient(\n    to bottom in srgb,\n    transparent,\n    light-dark(oklch(0% 0 0 / 88%), oklch(0% 0 0 / 8%))\n  );\n  --sp-rw-button-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(oklch(0% 0 0), oklch(100% 0 0));\n  --sp-rw-button-micro:\n    inset 0 0.5px 0 light-dark(oklch(100% 0 0 / 8%), transparent),\n    inset 0 -0.5px 0 light-dark(oklch(0% 0 0 / 16%), transparent);\n}\n\n:root [data-chassis][data-variant=\"outline\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  --sp-rw-button-interaction: var(--sp-rw-control-interaction);\n  --sp-rw-button-interaction-hover: var(--sp-rw-interaction-on-fill-hover);\n  --sp-rw-button-interaction-pressed: var(--sp-rw-interaction-on-fill-pressed);\n  --sp-rw-button-rim: var(--sp-rw-outline-rim);\n}\n\n:root\n  [data-chassis]:where(\n    [data-variant=\"outline-destructive\"],\n    [data-variant=\"ghost-destructive\"]\n  ) {\n  --sp-rw-button-text: var(--sp-rw-fg-destructive);\n  --sp-rw-button-interaction: var(--sp-rw-bg-destructive);\n  --sp-rw-button-interaction-hover: 0.12;\n  --sp-rw-button-interaction-pressed: 0.16;\n}\n\n:root [data-chassis][data-variant=\"outline-destructive\"] {\n  --sp-rw-button-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(\n      rgb(\n        from oklch(from var(--sp-rw-bg-destructive) calc(l - 0.09) c h)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      ),\n      rgb(\n        from oklch(from var(--sp-rw-bg-destructive) calc(l + 0.1) c h / 0.9)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      )\n    );\n}\n\n:root:where(.dark)\n  [data-chassis]:where(\n    [data-variant=\"outline-destructive\"],\n    [data-variant=\"ghost-destructive\"]\n  ),\n:root:where([data-theme=\"dark\"])\n  [data-chassis]:where(\n    [data-variant=\"outline-destructive\"],\n    [data-variant=\"ghost-destructive\"]\n  ),\n:root\n  :where(.dark)\n  [data-chassis]:where(\n    [data-variant=\"outline-destructive\"],\n    [data-variant=\"ghost-destructive\"]\n  ),\n:root\n  :where([data-theme=\"dark\"])\n  [data-chassis]:where(\n    [data-variant=\"outline-destructive\"],\n    [data-variant=\"ghost-destructive\"]\n  ) {\n  --sp-rw-button-interaction-hover: 0.24;\n  --sp-rw-button-interaction-pressed: 0.32;\n}\n\n:root [data-chassis][data-variant=\"ghost\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  --sp-rw-button-interaction: var(--sp-rw-control-interaction);\n  --sp-rw-button-interaction-hover: var(--sp-rw-interaction-hover);\n  --sp-rw-button-interaction-pressed: var(--sp-rw-interaction-pressed);\n}\n\n/* The quiet brand action: accent text on a tint of the accent, the brand\n * colour. The tint deepens on hover and press. */\n:root [data-chassis][data-variant=\"muted-accent\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-accent);\n  --sp-rw-button-interaction: var(--sp-rw-bg-accent);\n  --sp-rw-button-interaction-rest: 0.16;\n  --sp-rw-button-interaction-hover: 0.2;\n  --sp-rw-button-interaction-pressed: 0.24;\n}\n\n:root:where(.dark) [data-chassis][data-variant=\"muted-accent\"],\n:root:where([data-theme=\"dark\"]) [data-chassis][data-variant=\"muted-accent\"],\n:root :where(.dark) [data-chassis][data-variant=\"muted-accent\"],\n:root :where([data-theme=\"dark\"]) [data-chassis][data-variant=\"muted-accent\"] {\n  --sp-rw-button-interaction-rest: 0.2;\n  --sp-rw-button-interaction-hover: 0.28;\n  --sp-rw-button-interaction-pressed: 0.36;\n}\n\n/* In Dark the pressed tint lightens the surface below, so the accent text\n * takes a step lighter while the Button is pressed or its popup is open: it\n * keeps 4.5:1 on a Card or Dialog (WCAG 1.4.3). It is derived from the accent\n * text, so a brand override reaches it. */\n:root:where(.dark)\n  [data-chassis][data-variant=\"muted-accent\"]:where(\n    :active,\n    [data-pressed],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]),\n:root:where([data-theme=\"dark\"])\n  [data-chassis][data-variant=\"muted-accent\"]:where(\n    :active,\n    [data-pressed],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]),\n:root\n  :where(.dark)\n  [data-chassis][data-variant=\"muted-accent\"]:where(\n    :active,\n    [data-pressed],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]),\n:root\n  :where([data-theme=\"dark\"])\n  [data-chassis][data-variant=\"muted-accent\"]:where(\n    :active,\n    [data-pressed],\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-text: oklch(from var(--sp-rw-fg-accent) calc(l + 0.02) c h);\n}\n\n/* The glass Button is the shared glass surface on the frosted chassis: the\n * glass rule draws its fill, edge, sheen and blur, and its text takes the glass\n * ink. Hover and press tint the interaction layer with that ink, so they read\n * on glass in both themes. */\n:root [data-chassis][data-variant=\"glass\"] {\n  --sp-rw-button-text: var(--sp-rw-glass-foreground);\n  --sp-rw-button-interaction: var(--sp-rw-glass-foreground);\n  --sp-rw-button-interaction-rest: 0;\n  --sp-rw-button-interaction-hover: 0.08;\n  --sp-rw-button-interaction-pressed: 0.14;\n}\n\n:root [data-chassis][data-variant=\"frosted-accent\"] {\n  --sp-rw-button-source: var(--sp-rw-bg-accent);\n  --sp-rw-button-text: var(--sp-rw-fg-accent);\n  --sp-rw-button-depth:\n    0 1px 3px -1px\n      rgb(\n        from oklch(from var(--sp-rw-button-source) 0.08 calc(c * 1) h / 0.1)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      ),\n    0 1px 2px -2px\n      rgb(\n        from oklch(from var(--sp-rw-button-source) 0.08 calc(c * 1) h / 0.06)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      );\n  --sp-rw-button-interaction: var(--sp-rw-button-source);\n  --sp-rw-button-interaction-rest: 0.12;\n  --sp-rw-button-interaction-hover: 0.18;\n  --sp-rw-button-interaction-pressed: 0.26;\n  --sp-rw-button-spotlights:\n    inset 0 1px 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.85)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      ),\n    inset 0 -1px 0\n      light-dark(\n        rgb(\n          from oklch(from var(--sp-rw-button-source) 0.98 0.01 h / 0.85)\n            clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n        ),\n        transparent\n      );\n  --sp-rw-button-dome: linear-gradient(\n    to bottom in srgb,\n    light-dark(transparent, oklch(100% 0 0 / 3%)),\n    light-dark(oklch(0% 0 0 / 3%), transparent)\n  );\n  --sp-rw-button-rim: inset 0 0 0 var(--sp-rw-border-default)\n    light-dark(\n      rgb(\n        from oklch(from var(--sp-rw-button-source) calc(l - 0.04) c h / 0.45)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      ),\n      rgb(\n        from oklch(from var(--sp-rw-button-source) calc(l + 0.1) c h / 0.65)\n          clamp(0, r, 255) clamp(0, g, 255) clamp(0, b, 255)\n      )\n    );\n}\n\n:root [data-chassis]:where([data-variant=\"link\"], [data-variant=\"text\"]) {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n  background: transparent;\n}\n\n/* A link reads as text, so its words can be selected and copied as on any link\n * on the web; a Button that acts keeps user-select: none above, so a double\n * click on it selects nothing. A drag that starts on a link still drags the\n * link, as browsers do; a triple click, or a drag that starts beside the link,\n * selects its text (Lukas, 15 October 2026). */\n:root [data-chassis][data-variant=\"link\"] {\n  -webkit-user-select: text;\n  user-select: text;\n}\n\n/* A link stays underlined in its text colour; hover and press fade it to\n * 70 %, so the pointer is answered without changing its look (Lukas,\n * 17 October 2026, from the link hover review). */\n:root [data-chassis][data-variant=\"link\"] {\n  transition: opacity var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root\n  [data-chassis][data-variant=\"link\"]:where(:hover, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  opacity: 0.7;\n}\n\n/* Inline links retain Button states while fitting surrounding prose. The link\n * flows and wraps with the words around it, so a long name or project link\n * breaks across lines instead of running out of its container. Its content\n * wrapper forms no box of its own; the label is part of the line. */\n:root [data-chassis][data-profile=\"inline\"] {\n  display: inline;\n  min-height: 0;\n  padding: 0;\n  border-width: 0;\n  vertical-align: baseline;\n  white-space: normal;\n  /* A flex or grid parent can stretch the link; its label stays centred there,\n   * as it was when the link was a centring flex box. Within a line of text the\n   * property has no effect. */\n  text-align: center;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n/* The link gives up the control height for the height of its line. A square\n * inline action (below) is no part of a line and keeps the height of its size,\n * so this rule leaves it out. `:where` holds the rule at the specificity of the\n * one above, which carried the declaration before. */\n:root [data-chassis][data-profile=\"inline\"]:where(:not([data-shape=\"square\"])) {\n  height: auto;\n}\n\n:root [data-chassis][data-profile=\"inline\"] > .sp-button-content {\n  display: contents;\n}\n\n/* A link in running text keeps its words together: the whole link moves to\n * the next line when the line runs out, such as Open booking at the end of an\n * Alert's sentence, and only a link longer than a whole line breaks inside it,\n * so a long name never runs past its container (Lukas, 16 October 2026). */\n:root\n  [data-chassis][data-variant=\"link\"][data-profile=\"inline\"]:where(\n    :not([data-shape=\"square\"])\n  ) {\n  display: inline-block;\n  max-width: 100%;\n}\n\n/* A link is underlined at rest (button.tsx); a text Button, inline or on its\n * own, underlines on hover and press. The kit's own text controls (Tabs,\n * Sidebar rows, icon actions) keep their profiles' treatment. */\n:root\n  [data-chassis][data-variant=\"text\"]:is(\n    [data-profile=\"default\"],\n    [data-profile=\"inline\"]\n  ):not([data-shape=\"square\"]):where(:hover, [data-pressed]):not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  text-decoration-line: underline;\n  text-underline-offset: var(--sp-rw-space-1);\n}\n\n/* On touch screens every Button widens its target to 44 px with an absolutely\n * placed ::after. An inline link forms no box, so that layer would start where\n * the label ends and reach past the line, and past the page edge near it. It\n * sits centred on the label instead. */\n@media (pointer: coarse) {\n  :root [data-chassis][data-profile=\"inline\"]::after {\n    top: 50%;\n    left: 50%;\n    translate: -50% -50%;\n  }\n}\n\n/* An icon beside the label keeps the flex layout that spaces and centres them;\n * the label still wraps. */\n:root [data-chassis][data-profile=\"inline\"]:has(> .sp-button-content > svg) {\n  display: inline-flex;\n}\n\n/* A square inline action is the bare glyph in the square of its size, such as\n * the action at the end of a Sidebar row. The square stays as the pointer\n * target with the glyph centred in it, and the content wrapper is a box around\n * the glyph again, in the action's own radius. Keyboard focus draws the kit\n * outline around that box, tight around the glyph (rulebook T3), through the\n * rules it shares with the field action above.\n *\n * The square is the box of every square Button: its width and its height both\n * come from the size rule, because the inline profile leaves a square's height\n * alone (above). So the target is that square wherever it stands, also as a\n * flex or grid item that its container stretches, or positioned absolutely. */\n:root [data-chassis][data-profile=\"inline\"][data-shape=\"square\"] {\n  display: inline-flex;\n}\n\n:root\n  [data-chassis][data-profile=\"inline\"][data-shape=\"square\"]\n  > .sp-button-content {\n  display: inline-flex;\n  width: auto;\n  border-radius: inherit;\n}\n\n/* A muted inline action, the secondary one beside a primary link or a quiet\n * icon action, rests in the tertiary text colour and returns to the primary\n * one on hover, press and keyboard focus, with no fill (rulebook T2). */\n:root [data-chassis][data-profile=\"inline\"][data-profile-tone=\"muted\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-tertiary);\n}\n\n:root\n  [data-chassis][data-profile=\"inline\"][data-profile-tone=\"muted\"]:where(\n    :hover,\n    :active,\n    :focus-visible,\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-text: var(--sp-rw-fg-primary);\n}\n\n/* An accent inline action, such as Edit beside a neutral link, takes the accent\n * text colour of the frosted-accent Button at rest and keeps it on interaction;\n * the link underline marks hover and press. */\n:root [data-chassis][data-profile=\"inline\"][data-profile-tone=\"accent\"] {\n  --sp-rw-button-text: var(--sp-rw-fg-accent);\n}\n\n/* A destructive inline action, such as Remove, rests like any link and takes\n * the destructive text colour of the red Button variants on interaction. */\n:root\n  [data-chassis][data-profile=\"inline\"][data-profile-tone=\"destructive\"]:where(\n    :hover,\n    :active,\n    [data-pressed]\n  ):not(:disabled, [aria-disabled=\"true\"]) {\n  --sp-rw-button-text: var(--sp-rw-fg-destructive);\n}\n\n/* ResourceList owns row geometry; the composed ghost Button owns interactive\n * hover, pressed, focus-visible and disabled visuals. An interactive row is a\n * block-level row: the chassis default (inline-flex, centred) refined here,\n * not repeated as utilities on the row. The row is a multiline Button, so its\n * height follows its lines through the multiline Button rule. */\n:root [data-chassis][data-resource-list-item] {\n  display: flex;\n  min-height: var(--sp-rw-control-md);\n  gap: var(--sp-rw-space-3);\n  padding: var(--sp-rw-space-2-5) var(--sp-rw-space-3);\n}\n\n:root [data-chassis][data-resource-list-item] > .sp-button-content {\n  display: contents;\n}\n\n/* A compact list, by its density prop or by its provider's density, gives\n * its own rows and its header 8 px above and below and its metadata the small\n * text step. The rules start at the list's own data-density and reach its\n * parts through child combinators: a compact element around a list of density\n * \"default\", such as a compact Card, or a compact list it is nested in,\n * leaves it as it is. */\n:root\n  .sp-resource-list[data-density=\"compact\"]\n  > .sp-motion-item\n  > .sp-resource-list-item,\n:root\n  .sp-resource-list[data-density=\"compact\"]\n  > .sp-motion-item\n  > .sp-resource-list-header {\n  padding-block: var(--sp-rw-space-2);\n}\n\n:root\n  .sp-resource-list[data-density=\"compact\"]\n  > .sp-motion-item\n  > .sp-resource-list-item\n  > .sp-resource-list-meta,\n:root\n  .sp-resource-list[data-density=\"compact\"]\n  > .sp-motion-item\n  > .sp-resource-list-item\n  > .sp-button-content\n  > .sp-resource-list-meta {\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--text-xs--line-height);\n}\n\n/* A row whose content has a description runs over more than one line\n * (rulebook I5). Its leading mark centres on the first line, and text at its\n * end sits on that line, as a date does in an inbox. A control at the end,\n * such as a chevron or a button, stays centred on the row, and so does every\n * part of a row of one line. The meta counts as text while it holds nothing\n * but text and the selection check. */\n:root\n  .sp-resource-list-indicator:has(\n    ~ .sp-resource-list-content > .sp-resource-list-description\n  ) {\n  align-self: flex-start;\n  translate: 0 calc((1lh - 100%) / 2);\n}\n\n:root\n  .sp-resource-list-content:has(> .sp-resource-list-description)\n  ~ .sp-resource-list-meta:not(:has(> :not(.sp-resource-list-selection))) {\n  align-self: flex-start;\n  min-height: var(--sp-rw-space-5);\n}\n\n/* A divided list (variant=\"divided\") draws the kit's hairline (rulebook B7)\n * between two rows, in the middle of the space between them, across the row\n * content: it starts and ends where the rows' text does, and the rows' fills\n * stay clear of it. The first and the last row keep no space outside their\n * content: the list reaches past its top and bottom by the row padding, as a\n * flush list reaches past its sides, so the fills keep their inset. A\n * ResourceListHeader is an entry like a row, so the line below it is this\n * divider too, with the same space on both sides. */\n:root .sp-resource-list[data-variant=\"divided\"] {\n  margin-block: calc(var(--sp-rw-space-2-5) * -1);\n}\n\n:root .sp-resource-list[data-variant=\"divided\"][data-density=\"compact\"] {\n  margin-block: calc(var(--sp-rw-space-2) * -1);\n}\n\n:root .sp-resource-list-divider {\n  display: block;\n  height: var(--sp-rw-border-default);\n  margin-inline: var(--sp-rw-space-3);\n  margin-bottom: var(--sp-motion-gap);\n  background: var(--sp-rw-border-translucent);\n}\n\n/* With clickable rows (interactive), the line is as wide as their hover\n * surface, flush or not, the header's line included, so the hover fill never\n * stands out over a line. A list of static rows keeps the line across the\n * row content. */\n:root\n  .sp-resource-list[data-variant=\"divided\"]:has(\n    > .sp-motion-item > .sp-resource-list-item[data-interactive]\n  )\n  .sp-resource-list-divider {\n  margin-inline: 0;\n}\n\n/* The selected row owns its metadata colour: the pressed fill under a selected\n * row lowers the contrast of muted text, so metadata reads at 80% foreground\n * there. Consumers (TaskCalendar) do not recolour it themselves. */\n:root .sp-resource-list-item[data-selected=\"true\"] .sp-resource-list-meta {\n  color: color-mix(in oklab, var(--foreground) 80%, transparent);\n}\n\n/* Allocate marker space continuously so right-aligned metadata moves with it. */\n:root .sp-resource-list-selection {\n  display: inline-flex;\n  width: 0;\n  flex: none;\n  overflow: hidden;\n  margin-inline-start: 0;\n  transition:\n    width var(--motion-duration-base) var(--motion-ease-standard),\n    margin-inline-start var(--motion-duration-base) var(--motion-ease-standard);\n}\n\n:root .sp-resource-list-selection[data-selected=\"true\"] {\n  width: var(--sp-rw-space-4);\n  margin-inline-start: var(--sp-rw-space-2);\n}\n\n:root[data-motion-reduce=\"true\"] .sp-resource-list-selection,\n[data-sp-motion=\"reduced\"] .sp-resource-list-selection {\n  transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-resource-list-selection {\n    transition: none;\n  }\n}\n\n/* Accordion disclosure styling. */\n:root .sp-accordion-item {\n  border-bottom: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root .sp-accordion-item:last-child {\n  border-bottom: 0;\n}\n\n:root .sp-accordion-indicator {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  color: var(--sp-rw-fg-tertiary);\n  transform: rotate(0deg);\n  transition: transform var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root .sp-accordion-trigger[data-panel-open] .sp-accordion-indicator {\n  transform: rotate(180deg);\n}\n\n:root .sp-accordion-panel {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n  transition: height var(--motion-duration-expand) var(--motion-ease-state);\n}\n\n:root .sp-accordion-panel > div {\n  padding: 0 var(--sp-rw-space-2) var(--sp-rw-space-3);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-accordion-indicator,\n  :root .sp-accordion-panel {\n    transition: none;\n  }\n}\n\n/* Collapsible disclosure styling. */\n:root [data-slot=\"collapsible-chevron\"] {\n  color: var(--sp-rw-fg-tertiary);\n  transform: rotate(0deg);\n  transition: transform var(--motion-duration-expand) var(--motion-ease-expand);\n}\n\n:root\n  .sp-collapsible-trigger[data-panel-open]\n  [data-slot=\"collapsible-chevron\"] {\n  transform: rotate(180deg);\n}\n\n/* A tree chevron (data-turn=\"quarter\") points to the end of its row at rest,\n * a ChevronRight mirrored in RTL, and turns a quarter turn to point down while\n * the panel is open, with the same motion. */\n:root [dir=\"rtl\"] [data-slot=\"collapsible-chevron\"][data-turn=\"quarter\"] {\n  transform: rotate(180deg);\n}\n\n:root\n  .sp-collapsible-trigger[data-panel-open]\n  [data-slot=\"collapsible-chevron\"][data-turn=\"quarter\"] {\n  transform: rotate(90deg);\n}\n\n:root .sp-collapsible-panel {\n  transition: height var(--motion-duration-expand) var(--motion-ease-expand);\n}\n\nhtml[data-motion-reduce=\"true\"] [data-slot=\"collapsible-chevron\"],\nhtml[data-motion-reduce=\"true\"] .sp-collapsible-panel,\n:where([data-sp-motion=\"reduced\"]) [data-slot=\"collapsible-chevron\"],\n:where([data-sp-motion=\"reduced\"]) .sp-collapsible-panel {\n  transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root [data-slot=\"collapsible-chevron\"],\n  :root .sp-collapsible-panel {\n    transition: none;\n  }\n}\n\n/* Tabs: default rail and indicator. The rail's corners stay concentric with\n * the tabs inside it, and square with them at the none radius. */\n:root .sp-tabs-list,\n:root .sp-tabs-rail {\n  --sp-rw-tabs-inset: var(--sp-rw-space-1);\n  gap: 0;\n  border: 0;\n  border-radius: calc(\n    var(--sp-rw-radius-control) +\n      min(var(--sp-rw-radius-control), var(--sp-rw-tabs-inset))\n  );\n  padding: var(--sp-rw-tabs-inset);\n  color: var(--sp-rw-fg-tertiary);\n}\n\n/* The rail, and the shadow of the selection on it, which the indicator and an\n * active tab before hydration both draw (below). */\n:root .sp-tabs-list:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root .sp-tabs-rail:is([data-variant=\"default\"], [data-variant=\"pill\"]) {\n  --sp-rw-tabs-selection-shadow:\n    inset 0 0.5px 0 oklch(0% 0 0 / 8%), inset 0 -0.5px 0 oklch(0% 0 0 / 16%),\n    inset 0 0 0 0.5px oklch(0% 0 0 / 16%), inset 0 1px 0 oklch(100% 0 0),\n    inset 0 -1px 0 oklch(100% 0 0 / 80%), 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    0 4px 4px -2px oklch(0% 0 0 / 2%);\n  background: oklch(56.74% 0.009 106.68 / 4%);\n  box-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 4%),\n    inset 0 0 0 0.5px oklch(0% 0 0 / 12%);\n}\n\n/* Dark mode set on <html> itself, or on any element inside it. */\n:root:where(.dark)\n  .sp-tabs-list:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root:where(.dark)\n  .sp-tabs-rail:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root:where([data-theme=\"dark\"])\n  .sp-tabs-list:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root:where([data-theme=\"dark\"])\n  .sp-tabs-rail:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root\n  :where(.dark)\n  .sp-tabs-list:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root\n  :where(.dark)\n  .sp-tabs-rail:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root\n  :where([data-theme=\"dark\"])\n  .sp-tabs-list:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root\n  :where([data-theme=\"dark\"])\n  .sp-tabs-rail:is([data-variant=\"default\"], [data-variant=\"pill\"]) {\n  --sp-rw-tabs-selection-shadow:\n    inset 0 0.5px 0 oklch(100% 0 0 / 16%),\n    inset 0 -0.5px 0 oklch(100% 0 0 / 4%),\n    inset 0 0 0 0.5px oklch(100% 0 0 / 16%), inset 0 0 0 0 oklch(0% 0 0 / 8%),\n    inset 0 0 0 0 transparent, 0 2px 2px -1px oklch(0% 0 0 / 12%),\n    0 4px 4px -2px oklch(0% 0 0 / 12%);\n  box-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 8%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 8%),\n    inset 0 0 0 0.5px oklch(100% 0 0 / 16%);\n}\n\n:root :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"],\n:root .sp-tab-indicator[data-variant=\"pill\"] {\n  border-radius: var(--sp-rw-radius-full);\n}\n\n:root :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"] {\n  box-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 4%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 4%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(0% 0 0 / 12%);\n}\n\n:root:where(.dark) :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"],\n:root:where([data-theme=\"dark\"])\n  :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"],\n:root :where(.dark) :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"],\n:root\n  :where([data-theme=\"dark\"])\n  :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"pill\"] {\n  box-shadow:\n    inset 0 2px 2px -1px oklch(0% 0 0 / 8%),\n    inset 0 4px 4px -2px oklch(0% 0 0 / 8%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(100% 0 0 / 16%);\n}\n\n:root :is(.sp-tabs-list, .sp-tabs-rail)[data-variant=\"underline\"] {\n  border-radius: 0;\n  padding-inline: 0;\n  background: transparent;\n  box-shadow: none;\n}\n\n:root\n  :is(\n    .sp-tabs-list,\n    .sp-tabs-rail\n  )[data-variant=\"underline\"][data-orientation=\"vertical\"] {\n  padding-block: 0;\n  padding-inline: var(--sp-rw-tabs-inset);\n}\n\n/* `divider` draws a passive line under all underline tabs, along their side\n * when vertical: the Separator's hairline, in its weight and colour. The line\n * is an inset edge of the list, so it spans the list, stays in place while the\n * tabs scroll and lies under the active underline, which sits on it at the\n * same edge. */\n:root .sp-tabs-list[data-variant=\"underline\"][data-divider] {\n  box-shadow: inset 0 calc(var(--sp-rw-border-default) * -1) 0\n    var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-tabs-list[data-variant=\"underline\"][data-divider][data-orientation=\"vertical\"] {\n  box-shadow: inset var(--sp-rw-border-default) 0 0\n    var(--sp-rw-border-translucent);\n}\n\n:root .sp-tabs-list[data-embedded=\"true\"] {\n  --sp-rw-tabs-inset: 0px;\n  padding: 0;\n  background: transparent;\n  box-shadow: none;\n}\n\n/* A rail whose tabs scroll inside a ScrollArea fades the edges where more tabs\n * wait. The tab that keyboard focus brings into view stops clear of that fade. */\n:root .sp-tabs-rail .sp-scroll-area-viewport {\n  scroll-padding-inline: var(--fade-size);\n}\n\n/* A tab list whose tabs do not fit scrolls (S-13). Its frame adds no box\n * while the tabs fit. When they overflow, it lays a scroll button on either\n * side of the list: each holds its place while the list overflows and shows\n * while more tabs wait in its direction. A press\n * scrolls a page of tabs; the selected tab and the focused one scroll into\n * view (tabs.tsx). The buttons fade in and out over the state time. */\n:where(.sp-tabs-list-frame) {\n  display: contents;\n}\n\n/* The tab that keyboard focus brings into view keeps the list's inset beside\n * it, so the list scrolls to its very start and end. */\n:root .sp-tabs-list {\n  scroll-padding-inline: var(--sp-rw-tabs-inset);\n}\n\n:where(.sp-tabs-list-frame[data-overflow]) {\n  display: grid;\n  min-width: 0;\n  grid-template-columns: auto minmax(0, 1fr) auto;\n  align-items: center;\n  column-gap: var(--sp-rw-space-1);\n}\n\n:root .sp-tabs-scroll-button {\n  display: flex;\n  opacity: 0;\n  pointer-events: none;\n  transition: opacity var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root .sp-tabs-scroll-button[hidden] {\n  display: none;\n}\n\n:root .sp-tabs-scroll-button[data-visible] {\n  opacity: 1;\n  pointer-events: auto;\n}\n\n/* On a touch screen the list scrolls by swiping, so the frame lays out no\n * scroll buttons and keeps no room for them. The selected tab and the focused\n * one still scroll into view (tabs-overflow.ts). */\n@media (pointer: coarse) {\n  :where(.sp-tabs-list-frame[data-overflow]) {\n    display: contents;\n  }\n\n  :root .sp-tabs-scroll-button {\n    display: none;\n  }\n}\n\n:root .sp-tabs-tab {\n  min-width: var(--sp-rw-control-xl);\n}\n\n/* Only a horizontal tab fits its content. A vertical tab keeps its own\n * full-width, start-aligned utilities, so every label in the list starts at\n * one x. */\n:root .sp-tabs-tab:not([data-orientation=\"vertical\"]) {\n  width: auto;\n}\n\n:root .sp-tab-indicator {\n  top: var(--sp-rw-tabs-inset);\n  left: 0;\n  width: var(--active-tab-width);\n  height: var(--active-tab-height);\n  border-radius: var(--sp-rw-radius-control);\n  transform: translateX(var(--active-tab-left));\n  transition:\n    width var(--motion-duration-fast) var(--motion-ease-state),\n    height var(--motion-duration-fast) var(--motion-ease-state),\n    transform var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root .sp-tab-indicator:is([data-variant=\"default\"], [data-variant=\"pill\"]) {\n  overflow: hidden;\n}\n\n/* The selection: a translucent pill on the rail, with a dome over its edges.\n * The indicator draws it once it knows where the active tab is. Until then it\n * stays hidden (Base UI's in a list, the rail's own while the rail is\n * \"pending\"), and the active tab draws the same pill over its own box, so the\n * server HTML shows the selection before the page hydrates, with no inline\n * script. The indicator then appears over the same box and the tab stops\n * drawing it, in one frame. */\n:root .sp-tab-indicator:is([data-variant=\"default\"], [data-variant=\"pill\"]),\n:root\n  .sp-tab-indicator[hidden]:is([data-variant=\"default\"], [data-variant=\"pill\"])\n  ~ [data-chassis][data-profile=\"tabs-tab\"][data-active],\n:root\n  .sp-tabs-rail[data-indicator=\"pending\"]:is(\n    [data-variant=\"default\"],\n    [data-variant=\"pill\"]\n  )\n  [data-chassis][data-profile=\"tabs-tab\"][data-active] {\n  background: var(--sp-rw-bg-translucent);\n  box-shadow: var(--sp-rw-tabs-selection-shadow);\n}\n\n:root\n  .sp-tab-indicator:is([data-variant=\"default\"], [data-variant=\"pill\"])::before,\n:root\n  .sp-tab-indicator[hidden]:is([data-variant=\"default\"], [data-variant=\"pill\"])\n  ~ [data-chassis][data-profile=\"tabs-tab\"][data-active]::before,\n:root\n  .sp-tabs-rail[data-indicator=\"pending\"]:is(\n    [data-variant=\"default\"],\n    [data-variant=\"pill\"]\n  )\n  [data-chassis][data-profile=\"tabs-tab\"][data-active]::before {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  background: var(--sp-rw-control-dome);\n  content: \"\";\n}\n\n/* The underline is 2 px and lies inside the list, flush with its edge. It sat\n * 1 px outside, where the overflow clip of a horizontal list cut it to 1 px. */\n:root .sp-tab-indicator[data-variant=\"underline\"] {\n  top: auto;\n  bottom: 0;\n  height: 2px;\n  background: var(--sp-rw-fg-primary);\n}\n\n/* Until the underline indicator shows, the active tab draws the same line at\n * the edge of the list: below it, or beside it when vertical, across the\n * list's inset. */\n:root\n  .sp-tab-indicator[hidden][data-variant=\"underline\"]\n  ~ [data-chassis][data-profile=\"tabs-tab\"][data-active]::before {\n  inset: auto 0 calc(var(--sp-rw-tabs-inset) * -1);\n  height: 2px;\n  background: var(--sp-rw-fg-primary);\n}\n\n:root\n  .sp-tab-indicator[hidden][data-variant=\"underline\"][data-orientation=\"vertical\"]\n  ~ [data-chassis][data-profile=\"tabs-tab\"][data-active]::before {\n  inset: 0 auto 0 calc(var(--sp-rw-tabs-inset) * -1);\n  width: 2px;\n  height: auto;\n}\n\n:root .sp-tabs-list[data-orientation=\"vertical\"] {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root .sp-tab-indicator[data-orientation=\"vertical\"] {\n  top: 0;\n  left: var(--sp-rw-tabs-inset);\n  width: calc(100% - var(--sp-rw-space-2));\n  height: var(--active-tab-height);\n  transform: translateY(var(--active-tab-top));\n}\n\n:root .sp-tab-indicator[data-variant=\"underline\"][data-orientation=\"vertical\"] {\n  top: 0;\n  left: 0;\n  bottom: auto;\n  width: 2px;\n  height: var(--active-tab-height);\n}\n\nhtml[data-motion-reduce=\"true\"] .sp-tab-indicator,\n:where([data-sp-motion=\"reduced\"]) .sp-tab-indicator {\n  transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-tab-indicator {\n    transition: none;\n  }\n}\n\n/* Toolbar: a transparent, wrapping composition. */\n:root .sp-toolbar {\n  border: 0;\n  border-radius: 0;\n  padding: 0;\n  background: transparent;\n  box-shadow: none;\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-toolbar-separator[data-orientation=\"vertical\"] {\n  width: var(--sp-rw-border-default);\n  height: var(--sp-rw-space-4);\n  align-self: center;\n  margin-block: 0;\n  background: var(--sp-rw-border-translucent);\n}\n\n:root .sp-toolbar-separator[data-orientation=\"horizontal\"] {\n  width: 100%;\n  height: var(--sp-rw-border-default);\n  margin-inline: 0;\n  background: var(--sp-rw-border-translucent);\n}\n\n:root .sp-file-upload-status {\n  border: 0;\n  background: transparent;\n  box-shadow: inset var(--sp-rw-border-default) 0 0\n    var(--sp-rw-border-translucent);\n}\n\n/* Input, Textarea and Label. A Group that pairs GroupText with an input is a\n * field too: it draws one frame around the text and the input, with the same\n * surface, focus, invalid and disabled states as an InputGroup. */\n:root :where(.sp-input-control:not([data-unstyled])),\n:root :where(.sp-textarea-control),\n:root :where(.sp-base-input-group),\n:root\n  :where(\n    .sp-group:has(> .sp-group-text):has(\n        > .sp-input-control:not([data-unstyled])\n      )\n  ) {\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  background: linear-gradient(oklch(0% 0 0 / 2%), transparent);\n  box-shadow: var(--sp-rw-field-shadow);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  transition:\n    color var(--motion-duration-fast) var(--motion-ease-state),\n    background-color var(--motion-duration-fast) var(--motion-ease-state),\n    box-shadow var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n/* A multi-line field keeps the colour of its surface. The fill gradient of a\n * one-line field would tint its whole height; the inset top shadow and rim\n * still mark the field. */\n:root :where(.sp-textarea-control),\n:root :where(.sp-base-input-group:has(textarea)) {\n  background: transparent;\n}\n\n/* Inside an InputGroup the unstyled control takes the group's field text:\n * 14 px, or 16 px with `textSize=\"base\"`. Its own size class set 16 px, so a\n * grouped field read larger than a plain Input beside it. */\n:root\n  .sp-base-input-group\n  :is(.sp-input-control, .sp-textarea-control)[data-unstyled] {\n  font-size: inherit;\n  line-height: inherit;\n}\n\n/* An explicit InputGroup textSize replaces the 14px field text. */\n:root .sp-base-input-group[data-text-size=\"base\"] {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* An explicit Input or Textarea textSize does the same at every size. */\n:root :is(.sp-input-control, .sp-textarea-control)[data-text-size=\"base\"] {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root .sp-input-control:not([data-unstyled]) {\n  height: var(--sp-rw-control-md);\n}\n\n:root .sp-base-input-group:has(.sp-base-input) {\n  height: var(--sp-rw-control-md);\n}\n\n:root .sp-base-input-group:has(.sp-input-control[data-size=\"sm\"]) {\n  height: var(--sp-rw-control-sm);\n}\n\n:root .sp-base-input-group:has(.sp-input-control[data-size=\"lg\"]) {\n  height: var(--sp-rw-control-lg);\n}\n\n:root .sp-base-input-group:has(.sp-input-control[data-size=\"xl\"]) {\n  height: var(--sp-rw-control-xl);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root .sp-input-control:not([data-unstyled])[data-size=\"sm\"] {\n  height: var(--sp-rw-control-sm);\n}\n\n:root .sp-input-control:not([data-unstyled])[data-size=\"lg\"] {\n  height: var(--sp-rw-control-lg);\n}\n\n:root .sp-input-control:not([data-unstyled])[data-size=\"xl\"] {\n  height: var(--sp-rw-control-xl);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* An xl Textarea takes the xl field radius of the Input above. */\n:root .sp-textarea-control:not([data-unstyled])[data-size=\"xl\"] {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* Combobox multi-select reuses the measured InputField contour. Its selected\n * values follow the accepted outline Badge grammar instead of the Legacy\n * accent-pill treatment. */\n:root .sp-combobox-chips:not([data-layout=\"standalone\"]) {\n  min-height: var(--combobox-chips-height, var(--sp-rw-control-md));\n  gap: var(--sp-rw-space-1);\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  padding: var(--combobox-chips-padding, var(--sp-rw-space-1));\n  background: linear-gradient(oklch(0% 0 0 / 2%), transparent);\n  box-shadow: var(--sp-rw-field-shadow);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n  transition: box-shadow var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root .sp-combobox-chips:not([data-layout=\"standalone\"]):focus-within {\n  box-shadow: var(--sp-rw-field-focus-shadow);\n}\n\n:root\n  .sp-combobox-chips:not([data-layout=\"standalone\"]):has(\n    [aria-invalid=\"true\"]\n  ) {\n  box-shadow: var(--sp-rw-field-invalid-shadow);\n}\n\n:root\n  .sp-combobox-chips:not([data-layout=\"standalone\"]):has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled\n  ) {\n  cursor: not-allowed;\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n:root .sp-combobox-chip {\n  height: var(--combobox-chip-height, var(--sp-rw-space-6));\n  gap: var(--sp-rw-space-0-5);\n  border: 0;\n  border-radius: var(--sp-rw-radius-xs);\n  padding-inline-start: var(--sp-rw-space-2);\n  background: transparent;\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-border-translucent);\n  color: var(--sp-rw-fg-secondary);\n  font-size: var(--sp-rw-font-13);\n  font-weight: 450;\n  line-height: var(--sp-rw-line-18);\n}\n\n/* A chip that leads with an avatar or an icon starts closer to its edge: the\n * 16 px mark sits as far from the border as from the top and the bottom. */\n:root .sp-combobox-chip:has(> .sp-combobox-chip-start-addon) {\n  padding-inline-start: calc(\n    (var(--combobox-chip-height, var(--sp-rw-space-6)) - var(--sp-rw-space-4)) /\n      2\n  );\n}\n\n/* The field is as tall as an Input of its input's size, and the chips grow\n * with it: 20 px at sm, 24 px at md and lg, 32 px at xl (Lukas, 13 October\n * 2026; the field was 32 px at every size). */\n:root .sp-combobox-chips:has(.sp-combobox-chips-input[data-size=\"sm\"]) {\n  --combobox-chips-height: var(--sp-rw-control-sm);\n  --combobox-chip-height: var(--sp-rw-space-5);\n}\n\n:root .sp-combobox-chips:has(.sp-combobox-chips-input[data-size=\"lg\"]) {\n  --combobox-chips-height: var(--sp-rw-control-lg);\n  --combobox-chips-padding: var(--sp-rw-space-1-5);\n}\n\n:root .sp-combobox-chips:has(.sp-combobox-chips-input[data-size=\"xl\"]) {\n  --combobox-chips-height: var(--sp-rw-control-xl);\n  --combobox-chips-padding: var(--sp-rw-space-2);\n  --combobox-chip-height: var(--sp-rw-space-8);\n}\n\n:root\n  .sp-combobox-chips:has(.sp-combobox-chips-input[data-size=\"xl\"])\n  .sp-combobox-chip {\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* A Combobox item is as tall as a one-line field of its Combobox, as a Select\n * item is as tall as its trigger: the height of the field its popup anchors\n * to. A chips field grows a row for each line of chips, so a popup anchored to\n * it names the control size of its input (data-item-size), and its items take\n * that size's control height (FB-9). */\n:root .sp-combobox-item {\n  min-height: var(--sp-rw-combobox-item-height, var(--anchor-height));\n}\n\n:root .sp-combobox-popup[data-item-size=\"sm\"] {\n  --sp-rw-combobox-item-height: var(--sp-rw-control-sm);\n}\n\n:root .sp-combobox-popup[data-item-size=\"md\"] {\n  --sp-rw-combobox-item-height: var(--sp-rw-control-md);\n}\n\n:root .sp-combobox-popup[data-item-size=\"lg\"] {\n  --sp-rw-combobox-item-height: var(--sp-rw-control-lg);\n}\n\n:root .sp-combobox-popup[data-item-size=\"xl\"] {\n  --sp-rw-combobox-item-height: var(--sp-rw-control-xl);\n}\n\n/* Focus ring of a field. A joined OTP Field is an InputGroup too, but its slots\n * draw the ring, as separate slots do: the field around them does not take it\n * (otp-field.css). */\n:root :where(.sp-input-control:not([data-unstyled])):focus-within,\n:root :where(.sp-textarea-control):focus-within,\n:root :where(.sp-base-input-group:not(.sp-otp-field)):focus-within,\n:root\n  :where(\n    .sp-group:has(> .sp-group-text):has(\n        > .sp-input-control:not([data-unstyled])\n      )\n  ):focus-within {\n  box-shadow: var(--sp-rw-field-focus-shadow);\n}\n\n:root :where(.sp-input-control:not([data-unstyled])):has([aria-invalid=\"true\"]),\n:root :where(.sp-textarea-control):has([aria-invalid=\"true\"]),\n:root :where(.sp-base-input-group):has([aria-invalid=\"true\"]),\n:root\n  :where(\n    .sp-group:has(> .sp-group-text):has(\n        > .sp-input-control:not([data-unstyled])\n      )\n  ):has([aria-invalid=\"true\"]) {\n  box-shadow: var(--sp-rw-field-invalid-shadow);\n}\n\n/* A field is disabled when its own control is: the input, the textarea or the\n * segments of a date or time field. A disabled action inside it, such as a\n * submit, copy or picker Button, a read-only field's picker button or a step\n * button at its bound, leaves the field as it is. */\n:root\n  :where(.sp-input-control:not([data-unstyled])):has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled,\n    [role=\"spinbutton\"][aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-textarea-control):has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled,\n    [role=\"spinbutton\"][aria-disabled=\"true\"]\n  ),\n:root\n  :where(.sp-base-input-group):has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled,\n    [role=\"spinbutton\"][aria-disabled=\"true\"]\n  ),\n:root\n  :where(\n    .sp-group:has(> .sp-group-text):has(\n        > .sp-input-control:not([data-unstyled])\n      )\n  ):has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled,\n    [role=\"spinbutton\"][aria-disabled=\"true\"]\n  ) {\n  cursor: not-allowed;\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n:root\n  .sp-base-input-group:has(\n    :is(input:not([type=\"hidden\"]), textarea):disabled,\n    [role=\"spinbutton\"][aria-disabled=\"true\"]\n  )\n  .sp-input-group-addon {\n  cursor: not-allowed;\n}\n\n/* Number Field and OTP Field reuse the measured InputField surface. */\n:root :where(.sp-number-field-group),\n:root :where(.sp-otp-field-input) {\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  outline: 0;\n  background: linear-gradient(oklch(0% 0 0 / 2%), transparent);\n  box-shadow: var(--sp-rw-field-shadow);\n  color: var(--sp-rw-fg-primary);\n  transition: box-shadow var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n/* The elevated field. Every field that takes the elevated surface carries the\n * `shadow-form-control` marker from formControlSurfaceClassNames, and this one\n * rule draws its contour: a change here reaches Input, Textarea, InputGroup,\n * Number Field and OTP Field alike. It replaces only the rest shadow. It sits\n * after the base rule of every field and before the Number Field and OTP Field\n * states, so focus, invalid and the other states stay with the shared rules, as\n * for the default field. The contour is the one of an elevated Card, so the\n * surface token owns it and the field keeps no copy. */\n:root :where(.shadow-form-control) {\n  box-shadow: var(--sp-rw-surface-elevated-shadow);\n}\n\n:root .sp-number-field-group {\n  /* The step buttons keep their icon size; center them at every control size. */\n  align-items: center;\n  height: var(--sp-rw-control-md);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* The value takes the 16 px text of the field scale at xl, or of an explicit\n * textSize=\"base\", like Input. */\n:root .sp-number-field[data-text-size=\"base\"] .sp-number-field-group {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* An emphasized value is the content of its stepper, such as a quantity or a\n * seat count: semibold and one step above the field text, 16 px at sm, 18 px at\n * default and lg and 20 px at xl. It comes after the textSize rule, so it sets\n * the size whatever the textSize. The value inherits it from the group, so a\n * text utility on NumberFieldInput still overrides it. */\n:root .sp-number-field[data-emphasized] .sp-number-field-group {\n  font-size: var(--sp-rw-font-18);\n  font-weight: var(--sp-rw-font-weight-strong);\n  line-height: var(--sp-rw-line-27);\n}\n\n:root .sp-number-field[data-emphasized][data-size=\"sm\"] .sp-number-field-group {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root .sp-number-field[data-emphasized][data-size=\"xl\"] .sp-number-field-group {\n  font-size: var(--sp-rw-font-20);\n  line-height: var(--sp-rw-line-30);\n}\n\n:root .sp-number-field[data-size=\"sm\"] .sp-number-field-group {\n  height: var(--sp-rw-control-sm);\n}\n\n:root .sp-number-field[data-size=\"lg\"] .sp-number-field-group {\n  height: var(--sp-rw-control-lg);\n}\n\n:root .sp-number-field[data-size=\"xl\"] .sp-number-field-group {\n  /* The xl field radius is the control radius of this field, so the step\n   * buttons' joined outer corners follow the group's corners, as they do at\n   * the smaller sizes. */\n  --sp-rw-radius-control: var(--sp-rw-radius-surface);\n\n  height: var(--sp-rw-control-xl);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* A value longer than the field ends with an ellipsis at rest (S-07). While\n * the field has focus, the browser drops the ellipsis and the value scrolls to\n * the caret, as in any input. */\n:root .sp-number-field-input {\n  height: 100%;\n  text-overflow: ellipsis;\n}\n\n:root .sp-otp-field[data-size=\"xl\"] .sp-otp-field-input {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root .sp-otp-field[data-size=\"sm\"] .sp-otp-field-input {\n  width: var(--sp-rw-control-sm);\n  height: var(--sp-rw-control-sm);\n}\n\n:root .sp-otp-field[data-size=\"md\"] .sp-otp-field-input {\n  width: var(--sp-rw-control-md);\n  height: var(--sp-rw-control-md);\n}\n\n:root .sp-otp-field[data-size=\"lg\"] .sp-otp-field-input {\n  width: var(--sp-rw-control-lg);\n  height: var(--sp-rw-control-lg);\n}\n\n:root .sp-otp-field[data-size=\"xl\"] .sp-otp-field-input {\n  width: var(--sp-rw-control-xl);\n  height: var(--sp-rw-control-xl);\n}\n\n:root :where(.sp-number-field-group:focus-within),\n:root :where(.sp-otp-field-input:focus-visible) {\n  outline: 0;\n  box-shadow: var(--sp-rw-field-focus-shadow);\n}\n\n:root :where(.sp-number-field-group[aria-invalid=\"true\"]),\n:root :where(.sp-number-field-group:has([aria-invalid=\"true\"])),\n:root :where(.sp-otp-field-input[aria-invalid=\"true\"]) {\n  border: 0;\n  box-shadow: var(--sp-rw-field-invalid-shadow);\n}\n\n/* Number Field: the group is disabled with its field (Base UI's data-disabled),\n * not when a step button stops at the minimum or the maximum. */\n:root :where(.sp-number-field-group[data-disabled]),\n:root :where(.sp-otp-field-input:disabled) {\n  background: linear-gradient(oklch(0% 0 0 / 2%), transparent);\n  box-shadow: var(--sp-rw-field-shadow);\n}\n\n:root .sp-number-field-group[data-disabled],\n:root .sp-otp-field:has(.sp-otp-field-input:disabled) {\n  cursor: not-allowed;\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n/* The 20 px between steps is the item's own pb-5, which the last step drops\n * (last:pb-0, stepper.tsx); the padding has that one owner (rulebook P6). */\n:root .sp-stepper-item::before {\n  width: var(--sp-rw-border-default);\n  background: var(--sp-rw-border-translucent);\n}\n\n/* The gap ring around a marker is drawn here, not by a utility class. It\n * takes the colour of the surface the marker sits on, like every separation\n * ring, and falls back to the page background. The primary surface it used to\n * take showed as a grey halo on a white Card. */\n:root .sp-stepper-indicator {\n  width: var(--sp-rw-control-xs);\n  height: var(--sp-rw-control-xs);\n  border-width: var(--sp-rw-border-default);\n  border-color: var(--sp-rw-border-translucent);\n  background: var(--sp-rw-bg-primary);\n  box-shadow: 0 0 0 var(--sp-rw-space-1)\n    var(--sp-rw-surface-color, var(--sp-rw-bg-base));\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-16);\n}\n\n:root [data-status=\"done\"] .sp-stepper-indicator {\n  border-color: var(--sp-rw-bg-positive);\n  background: var(--sp-rw-bg-positive);\n  color: var(--sp-rw-fg-emphasis);\n}\n\n:root [data-status=\"current\"] .sp-stepper-indicator {\n  border-color: var(--sp-rw-border-accent);\n  color: var(--sp-rw-fg-accent);\n}\n\n:root [data-status=\"blocked\"] .sp-stepper-indicator {\n  border-color: var(--sp-rw-border-attention);\n  color: var(--sp-rw-fg-attention);\n}\n\n:root .sp-stepper-indicator::after {\n  border-width: var(--sp-rw-border-default);\n  transition-duration: var(--motion-duration-fast);\n  transition-timing-function: var(--motion-ease-state);\n}\n\n:root\n  .sp-stepper-button-item:not(:disabled):not([aria-current=\"step\"]):hover\n  .sp-stepper-indicator::after {\n  border-color: var(--sp-rw-border-translucent);\n  box-shadow: 0 0 0 var(--sp-rw-border-default) var(--sp-rw-glow-hover);\n}\n\n:root\n  .sp-stepper-button-item[aria-current=\"step\"]\n  .sp-stepper-indicator::after {\n  border-color: var(--sp-rw-border-accent);\n  box-shadow: 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-bg-accent-translucent);\n}\n\n/* The marker and the title ease their colours between steps and the ring\n * fades (Component base); a forced reduced mode switches them off like the\n * rest of the kit's motion. */\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-stepper-indicator,\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-stepper-title,\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-stepper-indicator::after {\n  transition: none;\n}\n\n:root .sp-stepper-title {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root [data-status=\"open\"] .sp-stepper-title {\n  color: var(--sp-rw-fg-tertiary);\n}\n\n:root .sp-stepper-button-item[aria-current=\"step\"] .sp-stepper-title {\n  color: var(--sp-rw-fg-primary);\n}\n\n:root .sp-stepper-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-12);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-18);\n}\n\n:root :where(.sp-input),\n:root :where(.sp-textarea) {\n  height: 100%;\n  border: 0;\n  background: transparent;\n  color: var(--sp-rw-fg-primary);\n  font: inherit;\n}\n\n:root .sp-input {\n  --sp-input-padding-inline: var(--sp-rw-space-3);\n\n  padding-inline: var(--sp-input-padding-inline);\n}\n\n/* Public Input owns Combobox integration insets and disabled treatment. The\n * Combobox wrapper supplies only stable role signals and sibling placement. A\n * leading icon sits 8 px from the edge and the text 6 px after it, at 30 px,\n * at every size (rulebook I3). */\n:root .sp-base-input[data-profile=\"combobox\"][data-inset-start] .sp-input {\n  padding-inline-start: calc(var(--sp-rw-space-8) - var(--sp-rw-space-0-5));\n}\n\n:root .sp-base-input[data-profile=\"combobox\"][data-inset-end] .sp-input {\n  padding-inline-end: calc(var(--sp-rw-space) * 7);\n}\n\n:root\n  .sp-base-input[data-profile=\"combobox\"][data-size=\"sm\"][data-inset-end]\n  .sp-input {\n  padding-inline-end: calc(var(--sp-rw-space) * 6.5);\n}\n\n:root\n  .sp-base-input[data-profile=\"combobox\"][data-size=\"xl\"][data-inset-end]\n  .sp-input {\n  padding-inline-end: calc(var(--sp-rw-space) * 8);\n}\n\n/* An unstyled Combobox or Autocomplete input fades like a styled one; the\n * input embedded in chips fades with the chips instead. */\n:root\n  .sp-base-input[data-profile=\"combobox\"]:not(\n    [data-profile-mode=\"embedded\"]\n  ):has(:disabled) {\n  cursor: not-allowed;\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n:root .sp-input[type=\"number\"] {\n  appearance: textfield;\n}\n\n:root .sp-input[type=\"number\"]::-webkit-inner-spin-button,\n:root .sp-input[type=\"number\"]::-webkit-outer-spin-button {\n  margin: 0;\n  appearance: none;\n}\n\n:root .sp-autocomplete-input-group:has(.sp-autocomplete-start-addon) .sp-input {\n  padding-inline-start: calc(var(--sp-rw-space-8) - var(--sp-rw-space-0-5));\n}\n\n:root\n  :where(.sp-combobox-input-group, .sp-autocomplete-input-group)\n  > :where(.sp-combobox-start-addon),\n:root\n  :where(.sp-combobox-input-group, .sp-autocomplete-input-group)\n  > :where(.sp-autocomplete-start-addon) {\n  padding-inline-start: calc(var(--sp-rw-space-2) + 1px);\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n}\n\n:root\n  :where(.sp-combobox-start-addon, .sp-autocomplete-start-addon)\n  svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n}\n\n/* Icons in an InputGroup around a field control. In an InputGroupText an icon\n * is 18 px, and 16 px from 640 px, beside an input or a textarea. In an addon\n * it is 16 px, beside date segments too; the addon rule follows the text rules,\n * so an InputGroupText in an addon takes it. The icon of a Button in an addon\n * keeps the Button's own size, and an icon with a size class keeps that. */\n:root\n  .sp-base-input-group:has(.sp-input-control, .sp-textarea-control)\n  .sp-input-group-text\n  svg:not([class*=\"size-\"]) {\n  width: calc(var(--sp-rw-space) * 4.5);\n  height: calc(var(--sp-rw-space) * 4.5);\n}\n\n@media (width >= 40rem) {\n  :root\n    .sp-base-input-group:has(.sp-input-control, .sp-textarea-control)\n    .sp-input-group-text\n    svg:not([class*=\"size-\"]) {\n    width: var(--sp-rw-space-4);\n    height: var(--sp-rw-space-4);\n  }\n}\n\n:root\n  .sp-base-input-group:has(\n    .sp-input-control,\n    .sp-textarea-control,\n    [role=\"spinbutton\"]\n  )\n  .sp-input-group-addon\n  svg:not([class*=\"size-\"]):where(\n    :not([data-chassis] > .sp-button-content > svg)\n  ) {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n}\n\n:root .sp-input-group-addon[data-align=\"inline-start\"] {\n  padding-inline-start: var(--sp-rw-space-2);\n}\n\n:root .sp-input-group-addon[data-align=\"inline-end\"] {\n  padding-inline-end: var(--sp-rw-space-2);\n}\n\n/* Optical alignment: field text is centred on its cap height, but lowercase\n * values such as email addresses read about 1.75 px lower. A plain icon beside\n * the text therefore sits 1 px below the geometric centre, between the two.\n * Buttons in addons keep their own centring. */\n:root\n  .sp-input-group-addon:is(\n    [data-align=\"inline-start\"],\n    [data-align=\"inline-end\"]\n  )\n  > svg {\n  translate: 0 1px;\n}\n\n/* Beside a textarea the icon centres on the first text line, with the same\n * optical 1 px. */\n:root\n  .sp-base-input-group:has(> .sp-textarea-control)\n  > .sp-input-group-addon:is(\n    [data-align=\"inline-start\"],\n    [data-align=\"inline-end\"]\n  )\n  > svg {\n  translate: 0 calc((1lh - 100%) / 2 + 1px);\n}\n\n:root .sp-input-group-addon {\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n}\n\n/* One colour for the dividers inside a field: the divider of an InputGroup\n * addon and the line between a file input's button and its file name. It is\n * declared on the field, so a nested theme scope recolours it. */\n:root .sp-input-group-addon[data-divider],\n:root .sp-input[type=\"file\"] {\n  --sp-field-divider-color: color-mix(\n    in oklab,\n    var(--color-muted-foreground) 16%,\n    transparent\n  );\n}\n\n:root .sp-input-group-addon[data-divider] {\n  border-color: var(--sp-field-divider-color);\n}\n\n:root .sp-input-group-addon[data-divider][data-align=\"inline-start\"] {\n  align-self: stretch;\n  border-inline-end-width: var(--sp-rw-border-default);\n  padding-inline-end: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:root .sp-input-group-addon[data-divider][data-align=\"inline-end\"] {\n  align-self: stretch;\n  border-inline-start-width: var(--sp-rw-border-default);\n  padding-inline-start: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:root .sp-input-group-addon[data-divider][data-align=\"block-start\"] {\n  border-bottom-width: var(--sp-rw-border-default);\n  padding-block: calc(var(--sp-rw-space-2) - 1px);\n}\n\n:root .sp-input-group-addon[data-divider][data-align=\"block-end\"] {\n  border-top-width: var(--sp-rw-border-default);\n  padding-block: calc(var(--sp-rw-space-2) - 1px);\n}\n\n:root .sp-input-group-addon[data-padding=\"none\"] {\n  padding: 0;\n}\n\n:root .sp-input-group-addon[data-padding=\"compact\"][data-align=\"inline-start\"] {\n  padding-inline-start: var(--sp-rw-space-1);\n}\n\n:root .sp-input-group-addon[data-padding=\"compact\"][data-align=\"inline-end\"] {\n  padding-inline-end: var(--sp-rw-space-1);\n}\n\n:root .sp-base-input[data-profile=\"default\"][data-inset-start] > input {\n  padding-inline-start: var(--sp-rw-space-8);\n}\n\n:root .sp-base-input[data-profile=\"default\"][data-inset-end] > input {\n  padding-inline-end: var(--sp-rw-space-8);\n}\n\n/* The native file control lays its button and file name out as one line of\n * inline content at the top of the box. A text input centres its value; this\n * centres the file line in the same way, so at every size the button and the\n * name sit on the field's centre, with the start padding of any other input.\n * The box itself keeps the height of the field. A short divider of the addon\n * divider's colour and weight separates the button from the file name. */\n:root .sp-input[type=\"file\"] {\n  align-content: center;\n  color: var(--sp-rw-fg-tertiary);\n  cursor: pointer;\n}\n\n:root .sp-input[type=\"file\"]::file-selector-button {\n  border: 0;\n  border-inline-end: var(--sp-rw-border-default) solid\n    var(--sp-field-divider-color);\n  background: transparent;\n  color: var(--sp-rw-fg-primary);\n  cursor: pointer;\n  font: inherit;\n  margin-inline-end: var(--sp-rw-space-3);\n  padding: 0;\n  padding-inline-end: calc(var(--sp-rw-space-3) - 1px);\n}\n\n:root .sp-input-control[data-size=\"sm\"] .sp-input {\n  --sp-input-padding-inline: var(--sp-rw-space-2-5);\n}\n\n:root .sp-input-control[data-size=\"xl\"] .sp-input {\n  --sp-input-padding-inline: var(--sp-rw-space-4);\n}\n\n/* Without an explicit textSize, xl fields use 16px text. */\n:root .sp-input-control[data-size=\"xl\"]:not([data-text-size]) .sp-input {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root :where(.sp-input, .sp-textarea)::placeholder {\n  color: var(--sp-rw-fg-tertiary);\n  opacity: 1;\n}\n\n/* Masked text. A browser draws every character of a password field as the\n * font's bullet: about 3 px at 14 px text in the kit font, and with the tight\n * default tracking the dots almost touch. The masked value takes a face with a\n * larger, rounder bullet (Verdana, or DejaVu Sans on Linux) and a little\n * tracking: dots of about 5 px, 3 px apart. The size stays the field's own, and\n * the face carries the kit font's ascent and descent, so the line, the baseline\n * and the caret match every other field (a larger font size would grow the\n * caret). Where neither font is installed the face is skipped: the kit font\n * stays and only the tracking applies. The placeholder (:placeholder-shown) and\n * the revealed value (type=\"text\") keep the normal text style. Every password\n * field is an Input, so InputGroup and ModalBlock follow. */\n@font-face {\n  font-family: \"Siteplane Masked\";\n  src: local(\"Verdana\"), local(\"DejaVu Sans\");\n  ascent-override: 101%;\n  descent-override: 25%;\n}\n\n:root .sp-input[type=\"password\"]:not(:placeholder-shown) {\n  font-family: \"Siteplane Masked\", var(--sp-rw-font-family-default);\n  letter-spacing: 0.04em;\n}\n\n:root .sp-textarea {\n  min-height: calc(var(--sp-rw-space) * 20);\n  padding: var(--sp-rw-space-2) var(--sp-rw-space-3);\n}\n\n:root .sp-textarea-control[data-size=\"sm\"] .sp-textarea {\n  min-height: calc(var(--sp-rw-space) * 16);\n  padding: var(--sp-rw-space-1) var(--sp-rw-space-2-5);\n}\n\n:root .sp-textarea-control[data-size=\"lg\"] .sp-textarea {\n  min-height: calc(var(--sp-rw-space) * 24);\n  padding-block: var(--sp-rw-space-2-5);\n}\n\n:root .sp-textarea-control[data-size=\"xl\"] .sp-textarea {\n  min-height: calc(var(--sp-rw-space) * 28);\n  padding: var(--sp-rw-space-3) var(--sp-rw-space-4);\n}\n\n:root .sp-textarea-control[data-size=\"xl\"]:not([data-text-size]) .sp-textarea {\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* A textarea in an InputGroup with insetEnd keeps its end clear for the\n * action over its corner, at every size. The group's end signal names the\n * room (input-group.css); InputGroupTextarea marks the control. */\n:root\n  .sp-textarea-control[data-profile=\"input-group\"][data-inset-end]\n  .sp-textarea {\n  padding-inline-end: var(--sp-composite-control-padding-inline-end);\n}\n\n/* `minLines` replaces the minimum height of the size with that many text lines\n * (fractions allowed) plus the vertical padding of the size. A textarea that\n * sizes to its content ignores the native `rows`, so this is the only way to a\n * shorter field. The Textarea sets the count in a style attribute; where a\n * Content Security Policy blocks it, the field keeps 2.5 lines, the height of\n * a description (S7). */\n:root .sp-textarea-control[data-min-lines] .sp-textarea {\n  min-height: calc(\n    var(--textarea-min-lines, 2.5) * 1lh + var(--sp-rw-space-2) * 2\n  );\n}\n\n:root .sp-textarea-control[data-size=\"sm\"][data-min-lines] .sp-textarea {\n  min-height: calc(\n    var(--textarea-min-lines, 2.5) * 1lh + var(--sp-rw-space-1) * 2\n  );\n}\n\n:root .sp-textarea-control[data-size=\"lg\"][data-min-lines] .sp-textarea {\n  min-height: calc(\n    var(--textarea-min-lines, 2.5) * 1lh + var(--sp-rw-space-2-5) * 2\n  );\n}\n\n:root .sp-textarea-control[data-size=\"xl\"][data-min-lines] .sp-textarea {\n  min-height: calc(\n    var(--textarea-min-lines, 2.5) * 1lh + var(--sp-rw-space-3) * 2\n  );\n}\n\n/* `maxLines` stops the growth at that many text lines plus the same padding;\n * longer text scrolls inside the field. Where a policy blocks the style\n * attribute, the height has no limit, as without maxLines. */\n:root .sp-textarea-control[data-max-lines] .sp-textarea {\n  max-height: calc(var(--textarea-max-lines) * 1lh + var(--sp-rw-space-2) * 2);\n}\n\n:root .sp-textarea-control[data-size=\"sm\"][data-max-lines] .sp-textarea {\n  max-height: calc(var(--textarea-max-lines) * 1lh + var(--sp-rw-space-1) * 2);\n}\n\n:root .sp-textarea-control[data-size=\"lg\"][data-max-lines] .sp-textarea {\n  max-height: calc(\n    var(--textarea-max-lines) * 1lh + var(--sp-rw-space-2-5) * 2\n  );\n}\n\n:root .sp-textarea-control[data-size=\"xl\"][data-max-lines] .sp-textarea {\n  max-height: calc(var(--textarea-max-lines) * 1lh + var(--sp-rw-space-3) * 2);\n}\n\n/* A label above a field sits on FieldLabel's 16 px line; a label beside a\n * checkbox, radio or switch keeps the 21 px body line, since those labels are\n * sentences and wrap (S13). Beside means holding the control, standing next to\n * it, or leading the text column next to it. One rule covers Label and\n * FieldLabel in all three cases, so the control, which centres on a 21 px line\n * (I5, checkbox.css, radio-group.css, switch.css), sits on the label's first\n * line. */\n:root .sp-base-label {\n  gap: var(--sp-rw-space-2);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-16);\n}\n\n:root\n  :is(\n    :is(.sp-base-label, .sp-field-label):has(\n      :is(.sp-checkbox, .sp-radio, .sp-switch)\n    ),\n    :where(:has(> :is(.sp-checkbox, .sp-radio, .sp-switch)))\n      > :is(.sp-base-label, .sp-field-label),\n    :where(:has(> :is(.sp-checkbox, .sp-radio, .sp-switch)))\n      > *\n      > :is(.sp-base-label, .sp-field-label)\n  ) {\n  line-height: var(--sp-rw-line-21);\n}\n\n/* Label, FieldLabel and SelectLabel share one colour and one disabled state\n * (T1). A label is disabled with its field (data-disabled) or with a disabled\n * form control it holds (an input, textarea, select, date segments, a Select\n * trigger, a checkbox, radio or switch, but never a button), and fades once,\n * by the action token; the control inside keeps its full opacity (\"Disabled\n * parts\"). The label of a Number Field follows the field's disabled state. */\n:root :where(.sp-base-label),\n:root :where(.sp-field-label),\n:root :where(.sp-select-label) {\n  color: var(--sp-semantic-color-foreground);\n}\n\n:root\n  :where(.sp-base-label):where(\n    [data-disabled],\n    :has(\n      :is(input, textarea, select):not(\n          [type=\"hidden\"],\n          [type=\"button\"],\n          [type=\"submit\"],\n          [type=\"reset\"],\n          [type=\"image\"]\n        ):disabled,\n      [role=\"spinbutton\"][aria-disabled=\"true\"],\n      [data-select-trigger]:is(:disabled, [data-disabled]),\n      :is(.sp-checkbox, .sp-radio, .sp-switch)[data-disabled]\n    )\n  ),\n:root\n  :where(.sp-field-label):where(\n    [data-disabled],\n    :has(\n      :is(input, textarea, select):not(\n          [type=\"hidden\"],\n          [type=\"button\"],\n          [type=\"submit\"],\n          [type=\"reset\"],\n          [type=\"image\"]\n        ):disabled,\n      [role=\"spinbutton\"][aria-disabled=\"true\"],\n      [data-select-trigger]:is(:disabled, [data-disabled]),\n      :is(.sp-checkbox, .sp-radio, .sp-switch)[data-disabled]\n    )\n  ),\n:root\n  :where(.sp-select-label):where(\n    [data-disabled],\n    :has(\n      :is(input, textarea, select):not(\n          [type=\"hidden\"],\n          [type=\"button\"],\n          [type=\"submit\"],\n          [type=\"reset\"],\n          [type=\"image\"]\n        ):disabled,\n      [role=\"spinbutton\"][aria-disabled=\"true\"],\n      [data-select-trigger]:is(:disabled, [data-disabled]),\n      :is(.sp-checkbox, .sp-radio, .sp-switch)[data-disabled]\n    )\n  ),\n:root .sp-number-field-scrub-area[data-disabled] > .sp-base-label {\n  cursor: not-allowed;\n  opacity: var(--sp-rw-disabled-opacity-action);\n}\n\n/* A choice card shares Card's surface radius and the native contour (one\n * physical pixel at any density) instead of a fixed 1px border and 10px radius. */\n:root .sp-base-label[data-variant=\"card\"] {\n  border: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root .sp-base-label[data-variant=\"card\"]:has([data-checked]) {\n  border-color: var(--sp-semantic-color-border-strong);\n}\n\n:root :where(label):has(:where(.sp-checkbox, .sp-radio, .sp-switch)):active,\n:root :where(.sp-label):has(:where(.sp-checkbox, .sp-radio, .sp-switch)):active,\n:root\n  :where(.sp-field-label):has(\n    :where(.sp-checkbox, .sp-radio, .sp-switch)\n  ):active {\n  -webkit-user-select: none;\n  user-select: none;\n}\n\n:root .sp-field-description {\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-18);\n}\n\n/* When its label holds a checkbox, radio or switch, a Field's description and\n * error start under the label text: the control's width and the label's 8 px\n * gap in (S13). The Field reads the size of the control from the choice tokens;\n * FieldDescription and FieldError take --field-choice-indent as their start\n * padding. */\n:root\n  .sp-field:has(> .sp-field-label :is(.sp-checkbox, .sp-radio, .sp-switch)) {\n  --field-choice-size: var(--sp-rw-choice-md);\n  --field-choice-width: var(--field-choice-size);\n  --field-choice-indent: calc(var(--field-choice-width) + var(--sp-rw-space-2));\n}\n\n:root\n  .sp-field:has(\n    > .sp-field-label :is(.sp-checkbox, .sp-radio, .sp-switch)[data-size=\"sm\"]\n  ) {\n  --field-choice-size: var(--sp-rw-choice-sm);\n}\n\n:root\n  .sp-field:has(\n    > .sp-field-label :is(.sp-checkbox, .sp-radio, .sp-switch)[data-size=\"lg\"]\n  ) {\n  --field-choice-size: var(--sp-rw-choice-lg);\n}\n\n:root\n  .sp-field:has(\n    > .sp-field-label :is(.sp-checkbox, .sp-radio, .sp-switch)[data-size=\"xl\"]\n  ) {\n  --field-choice-size: var(--sp-rw-choice-xl);\n}\n\n/* A switch is two thumbs wide. */\n:root .sp-field:has(> .sp-field-label .sp-switch) {\n  --field-choice-width: calc(var(--field-choice-size) * 2);\n}\n\n/* A Field with requiredIndicator marks the label of a required field with a red\n * asterisk; whether to show it is a choice per form. The control's own required\n * attribute stays the one source: the Field reads it through :has(:required),\n * which the native element of every control meets (Input, Textarea, Select,\n * Combobox, Autocomplete, Number Field, Checkbox, Switch, Radio Group and the\n * Date, Time and OTP fields). The asterisk is generated content with empty\n * alternative text, so assistive technology reads only the control's own\n * required state. The asterisk keeps --sp-rw-space-0-5 from the text. A label\n * of text alone lays out as a block, so the asterisk follows the last word when\n * the label wraps. A label that holds a control or an icon sets its parts 8 px\n * apart, and the asterisk takes that gap back. */\n:root\n  .sp-field[data-required-indicator]:has(:required)\n  > .sp-field-label::after {\n  content: \"*\";\n  content: \"*\" / \"\";\n  margin-inline-start: var(--sp-rw-space-0-5);\n  color: var(--sp-rw-fg-destructive);\n}\n\n:root\n  .sp-field[data-required-indicator]:has(:required)\n  > .sp-field-label:has(> *)::after {\n  margin-inline-start: calc(var(--sp-rw-space-0-5) - var(--sp-rw-space-2));\n}\n\n/* Floating label fields.\n * <Field labelPlacement=\"floating\"> draws its label inside the control. At\n * rest it stands in for the placeholder; it glides to the top and shrinks to\n * 12 px once the field has focus, a value, autofill or an open popup. The state\n * is read from the DOM, so a server-rendered page is right before hydration.\n *\n * The Field owns the label (its two states, the float condition and the\n * motion) and the one size of a floating field with the lines its label and\n * value sit on. Each control below lays itself out on them and keeps every\n * state rule of its usual owner. A control marks its own root with\n * data-label-placement=\"floating\" when it lays itself out for a floating\n * label. The label rules need that mark, so any other control, and a field\n * whose control only reaches it through a hidden proxy, keeps its label above\n * the control.\n *\n * A floating field takes no leading icon, so every label and value in a form\n * starts on one line. A control with a leading icon or start addon does not\n * mark itself and keeps its label above it; trailing actions keep their\n * room. */\n:root .sp-field[data-label-placement=\"floating\"] {\n  /* One size: every floating field is xl high. */\n  --field-floating-control-height: var(--sp-rw-control-xl);\n  /* The top of the 24 px label line at rest, centred in the field, and the\n   * insets of the label. It starts at the field's inline padding, where every\n   * value starts; its end keeps clear of trailing actions. A control moves the\n   * top and the end in its own subsection. */\n  --field-floating-label-top: var(--sp-rw-space-3);\n  --field-floating-label-start: var(--sp-rw-space-4);\n  --field-floating-label-end: var(--sp-rw-space-4);\n  /* Floated, the label is 12 px: 0.75 of its 16 px at rest. It shrinks\n   * towards its start corner. */\n  --field-floating-label-scale: 0.75;\n  --field-floating-label-origin: left top;\n  /* The value line starts under the floated label and keeps a little room\n   * below it. */\n  --field-floating-value-top: var(--sp-rw-space-5);\n  --field-floating-value-bottom: var(--sp-rw-space-1);\n  position: relative;\n  isolation: isolate;\n}\n\n/* At rest the label lies on the field's centre line, in the value's text style\n * and the placeholder colour. It is one line that truncates, and it takes no\n * pointer input, so a click anywhere in the field reaches the control. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    :is(\n      .sp-input-control,\n      .sp-textarea-control,\n      .sp-select-trigger\n    )[data-label-placement=\"floating\"]\n  )\n  > .sp-field-label {\n  position: absolute;\n  z-index: 1;\n  inset-block-start: var(--field-floating-label-top);\n  inset-inline-start: var(--field-floating-label-start);\n  display: block;\n  max-width: calc(\n    100% - var(--field-floating-label-start) - var(--field-floating-label-end)\n  );\n  overflow: hidden;\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-16);\n  font-weight: var(--sp-rw-font-weight-default);\n  line-height: var(--sp-rw-line-24);\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  pointer-events: none;\n  -webkit-user-select: none;\n  user-select: none;\n  transform-origin: var(--field-floating-label-origin);\n  transition:\n    transform var(--motion-duration-fast) var(--motion-ease-standard),\n    max-width var(--motion-duration-fast) var(--motion-ease-standard),\n    color var(--motion-duration-fast) var(--motion-ease-standard);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]:is(\n    :dir(rtl),\n    [dir=\"rtl\"],\n    [dir=\"rtl\"] *\n  ) {\n  --field-floating-label-origin: right top;\n}\n\n/* A required field's asterisk follows the floating label's text inline, as\n * the label is a block here rather than a flex row, and floats and shrinks\n * with it. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    :is(\n      .sp-input-control,\n      .sp-textarea-control,\n      .sp-select-trigger\n    )[data-label-placement=\"floating\"]\n  )\n  > .sp-field-label::after {\n  margin-inline-start: var(--sp-rw-space-0-5);\n}\n\n/* Floated: focus inside the field, a value, autofill, a chosen select value or\n * an open popup. The label moves to the top and shrinks towards its origin.\n * Its box widens by as much as it shrinks, so a long label keeps the whole\n * width of the field before it truncates. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    :is(\n      .sp-input-control,\n      .sp-textarea-control,\n      .sp-select-trigger\n    )[data-label-placement=\"floating\"]\n  ):is(\n    :focus-within,\n    :has(:autofill),\n    :has(:is(.sp-input, .sp-textarea):not(:placeholder-shown)),\n    :has(.sp-select-trigger:not([data-placeholder])),\n    :has([aria-expanded=\"true\"])\n  )\n  > .sp-field-label {\n  max-width: calc(\n    (\n        100% - var(--field-floating-label-start) -\n          var(--field-floating-label-end)\n      ) /\n      var(--field-floating-label-scale)\n  );\n  color: var(--sp-rw-fg-secondary);\n  transform: translateY(\n      calc(var(--sp-rw-space-1) - var(--field-floating-label-top))\n    )\n    scale(var(--field-floating-label-scale));\n}\n\n/* A placeholder shows only under the floated label, while the field has\n * focus. It fades in with the label and goes at once when the field is left,\n * so the returning label never crosses it. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-input, .sp-textarea)::placeholder {\n  transition: color var(--motion-duration-fast) var(--motion-ease-standard);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    :is(\n      .sp-input-control,\n      .sp-textarea-control,\n      .sp-select-trigger\n    )[data-label-placement=\"floating\"]\n  ):not(:focus-within)\n  :is(.sp-input, .sp-textarea)::placeholder {\n  color: transparent;\n  transition: none;\n}\n\n/* Each control lays itself out for a floating label below, in its own\n * subsection, on the Field's size, label insets and value line. */\n\n/* Input and InputGroup. The field is xl high with the xl radius at any size;\n * its input sets the 16 px value on the value line. An InputGroup hands the\n * value line to its input through its own composite padding signals. Surface,\n * focus, invalid, disabled and autofill stay with the shared field rules. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-input-control[data-label-placement=\"floating\"]:not([data-unstyled]),\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-base-input-group:has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  ) {\n  height: var(--field-floating-control-height);\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/* The xl inline padding, alone or in an InputGroup, where an addon's own\n * composite padding still takes its side. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-input-control[data-label-placement=\"floating\"]\n  > .sp-input {\n  --sp-input-padding-inline: var(--sp-rw-space-4);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-input-control[data-label-placement=\"floating\"]:not([data-unstyled])\n  > .sp-input {\n  padding-block: var(--field-floating-value-top)\n    var(--field-floating-value-bottom);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-base-input-group:has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  ) {\n  --sp-composite-control-padding-block-start: var(--field-floating-value-top);\n  --sp-composite-control-padding-block-end: var(--field-floating-value-bottom);\n\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* A trailing field action such as the password toggle keeps the field's room.\n * Its glyph ends at the field's inline padding, and the value and the label end\n * --sp-rw-space-2-5 before the glyph, which is 16 px in the middle of its 24 px\n * target. A compact InputGroup keeps its own room. An InputGroup that opens\n * with an addon keeps the label above it. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-input-group-addon[data-align=\"inline-end\"]\n      ~ .sp-input-control[data-label-placement=\"floating\"],\n    .sp-input-control[data-label-placement=\"floating\"]\n      ~ .sp-input-group-addon[data-align=\"inline-end\"]\n  ) {\n  --field-floating-label-end: calc(\n    var(--sp-rw-space-4) + var(--sp-rw-space-4) + var(--sp-rw-space-2-5)\n  );\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-base-input-group:has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  ):has(> .sp-input-group-addon[data-align=\"inline-end\"]) {\n  --sp-composite-control-padding-inline-end: calc(\n    var(--sp-rw-space-2-5) - (var(--sp-rw-control-xs) - var(--sp-rw-space-4)) /\n      2\n  );\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-base-input-group:has(> .sp-input-control[data-label-placement=\"floating\"])\n  > .sp-input-group-addon[data-align=\"inline-end\"] {\n  padding-inline-end: calc(\n    var(--sp-rw-space-4) - (var(--sp-rw-control-xs) - var(--sp-rw-space-4)) / 2\n  );\n}\n\n/* An Input that leaves room for an icon of its own at its end (insetEnd). One\n * with insetStart keeps the label above it. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-input-control[data-label-placement=\"floating\"][data-profile=\"default\"][data-inset-end]\n  ) {\n  --field-floating-label-end: var(--sp-rw-space-8);\n}\n\n/* Textarea. At rest the label lies on the first text line; floated it moves to\n * the top and the text starts under it. The field still grows by lines (S7)\n * from the xl minimum, now with the deeper top, and keeps its transparent\n * surface (B6). */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-textarea-control[data-label-placement=\"floating\"]\n  ) {\n  --field-floating-label-top: var(--field-floating-value-top);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-textarea-control[data-label-placement=\"floating\"] {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-textarea-control[data-label-placement=\"floating\"]\n  .sp-textarea {\n  min-height: calc(var(--sp-rw-space) * 28);\n  padding: var(--field-floating-value-top) var(--sp-rw-space-4)\n    var(--sp-rw-space-3);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-textarea-control[data-label-placement=\"floating\"][data-min-lines]\n  .sp-textarea {\n  min-height: calc(\n    var(--textarea-min-lines, 2.5) * 1lh + var(--field-floating-value-top) +\n      var(--sp-rw-space-3)\n  );\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-textarea-control[data-label-placement=\"floating\"][data-max-lines]\n  .sp-textarea {\n  max-height: calc(\n    var(--textarea-max-lines) * 1lh + var(--field-floating-value-top) +\n      var(--sp-rw-space-3)\n  );\n}\n\n/* Select. The trigger is xl high with the xl radius; its value sits on the\n * value line between the label's insets, so value and label start and end\n * together, and the caret stays centred at its xl place. The label stays up\n * once a value is chosen. A placeholder of the Select shows only while the\n * trigger has focus or is open. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-select-trigger[data-label-placement=\"floating\"]\n  ) {\n  --field-floating-label-end: var(--sp-rw-space-8);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  [data-chassis].sp-select-trigger[data-label-placement=\"floating\"] {\n  --sp-rw-select-height: var(--field-floating-control-height);\n  --sp-rw-select-value-start: var(--field-floating-label-start);\n  --sp-rw-select-value-end: var(--field-floating-label-end);\n  --sp-rw-select-caret-end: var(--sp-rw-space-3);\n\n  border-radius: var(--sp-rw-radius-surface);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* The value takes the value line itself, so the Button content around it\n * keeps the field's height and the caret, placed in that content, stays on the\n * field's centre. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-select-trigger[data-label-placement=\"floating\"]\n  .sp-select-value {\n  padding-block: var(--field-floating-value-top)\n    var(--field-floating-value-bottom);\n  transition: color var(--motion-duration-fast) var(--motion-ease-standard);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]:not(:focus-within)\n  .sp-select-trigger[data-label-placement=\"floating\"][data-placeholder]:not(\n    [aria-expanded=\"true\"]\n  )\n  .sp-select-value {\n  color: transparent;\n  transition: none;\n}\n\n/* Combobox and Autocomplete. Their input is the Input above, xl high with the\n * value line. The trigger or clear action takes the room of an InputGroup's\n * field action (P3): the action's glyph ends at the field's inline padding,\n * and the value and the label end --sp-rw-space-2-5 before it. An action's\n * 16 px glyph sits in the middle of its target. One with a start addon keeps\n * the label above it. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-input-control[data-label-placement=\"floating\"][data-profile=\"combobox\"][data-inset-end]\n  ) {\n  --field-floating-label-end: calc(\n    var(--sp-rw-space-4) + var(--sp-rw-space-4) + var(--sp-rw-space-2-5)\n  );\n}\n\n/* The value ends with the label. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  .sp-input-control[data-label-placement=\"floating\"][data-profile=\"combobox\"][data-inset-end]\n  > .sp-input {\n  padding-inline-end: var(--field-floating-label-end);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-combobox-trigger,\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-combobox-clear,\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-autocomplete-trigger,\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-autocomplete-clear {\n  --field-floating-action-target: var(--sp-rw-control-xs);\n  --field-floating-action-glyph: var(--sp-rw-space-4);\n\n  inset-inline-end: calc(\n    var(--sp-rw-space-4) -\n      (\n        var(--field-floating-action-target) - var(--field-floating-action-glyph)\n      ) /\n      2\n  );\n}\n\n/* The clear action draws a 12 px glyph, and an xl Combobox or Autocomplete\n * draws its actions on the larger target. */\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-combobox-clear,\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > .sp-autocomplete-clear {\n  --field-floating-action-glyph: var(--sp-rw-space-3);\n}\n\n:root\n  .sp-field[data-label-placement=\"floating\"]\n  :is(.sp-combobox-input-group, .sp-autocomplete-input-group):has(\n    > .sp-input-control[data-label-placement=\"floating\"]\n  )\n  > :is(\n    .sp-combobox-trigger,\n    .sp-combobox-clear,\n    .sp-autocomplete-trigger,\n    .sp-autocomplete-clear\n  )[data-size=\"icon-sm\"] {\n  --field-floating-action-target: var(--sp-rw-control-sm);\n}\n\n/* While a clear action shows (it replaces the trigger once there is a value),\n * the value and the label end --sp-rw-space-2-5 before its smaller glyph. */\n:root\n  .sp-field[data-label-placement=\"floating\"]:has(\n    .sp-input-control[data-label-placement=\"floating\"][data-profile=\"combobox\"]\n      ~ :is(.sp-combobox-clear, .sp-autocomplete-clear)\n  ) {\n  --field-floating-label-end: calc(\n    var(--sp-rw-space-4) + var(--sp-rw-space-3) + var(--sp-rw-space-2-5)\n  );\n}\n\n/* End of floating label fields */\n\n@media (pointer: coarse) {\n  :root :where(.sp-input),\n  :root :where(.sp-textarea) {\n    font-size: var(--sp-rw-font-16);\n    line-height: var(--sp-rw-line-24);\n  }\n}\n\n/* Checkbox and Radio. CheckboxGroup and RadioGroup space their items 8 px\n * apart with their own gap utility, so a consumer's gap replaces it. The\n * selection box of a Table is a Checkbox drawn by React Aria, so it takes these\n * rules too; React Aria names the checked state data-selected. */\n:root :where(.sp-checkbox),\n:root :where(.sp-radio),\n:root :where(.sp-table-selection-checkbox) {\n  --sp-rw-choice-size: var(--sp-rw-choice-md);\n  width: var(--sp-rw-choice-size);\n  height: var(--sp-rw-choice-size);\n  border: 0;\n  background: var(--sp-rw-bg-translucent);\n  box-shadow: var(--sp-rw-field-depth), var(--sp-rw-choice-rim);\n  color: var(--sp-rw-fg-inverse);\n  transition:\n    background-color var(--motion-duration-fast) var(--motion-ease-state),\n    box-shadow var(--motion-duration-fast) var(--motion-ease-state),\n    opacity var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root .sp-checkbox,\n:root .sp-table-selection-checkbox {\n  border-radius: var(--sp-rw-radius-xs);\n}\n\n:root .sp-radio {\n  border-radius: var(--sp-rw-radius-full);\n}\n\n:root\n  :where(\n    .sp-checkbox,\n    .sp-radio,\n    .sp-table-selection-checkbox\n  )[data-size=\"sm\"] {\n  --sp-rw-choice-size: var(--sp-rw-choice-sm);\n}\n\n:root\n  :where(\n    .sp-checkbox,\n    .sp-radio,\n    .sp-table-selection-checkbox\n  )[data-size=\"lg\"] {\n  --sp-rw-choice-size: var(--sp-rw-choice-lg);\n}\n\n:root\n  :where(\n    .sp-checkbox,\n    .sp-radio,\n    .sp-table-selection-checkbox\n  )[data-size=\"xl\"] {\n  --sp-rw-choice-size: var(--sp-rw-choice-xl);\n}\n\n/* A checked or indeterminate box fills with the accent and keeps a rim of a\n * deeper accent at 40%, derived from the accent so a brand override reaches\n * it (oklch(43.93% 0.2025 274.2 / 40%) for the default accent). */\n:root\n  :where(.sp-checkbox):where(\n    [data-checked],\n    [data-selected],\n    [data-indeterminate]\n  ),\n:root\n  :where(.sp-radio):where(\n    [data-checked],\n    [data-selected],\n    [data-indeterminate]\n  ),\n:root\n  :where(.sp-table-selection-checkbox):where(\n    [data-checked],\n    [data-selected],\n    [data-indeterminate]\n  ) {\n  background: var(--sp-rw-bg-accent);\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(from var(--sp-rw-bg-accent) calc(l - 0.1107) calc(c - 0.0245) h / 40%);\n}\n\n:root\n  :where(.sp-checkbox):not(\n    [data-checked],\n    [data-selected],\n    [data-disabled],\n    [data-indeterminate]\n  ):hover,\n:root\n  :where(.sp-radio):not(\n    [data-checked],\n    [data-selected],\n    [data-disabled],\n    [data-indeterminate]\n  ):hover,\n:root\n  :where(.sp-table-selection-checkbox):not(\n    [data-checked],\n    [data-selected],\n    [data-disabled],\n    [data-indeterminate]\n  ):hover {\n  background-image: linear-gradient(\n    var(--sp-rw-glow-hover),\n    var(--sp-rw-glow-hover)\n  );\n}\n\n:root\n  :where(.sp-checkbox, .sp-radio, .sp-table-selection-checkbox)[data-disabled] {\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n:root :where(.sp-checkbox, .sp-radio)[aria-invalid=\"true\"],\n:root .sp-radio-group[aria-invalid=\"true\"] .sp-radio {\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(67.3% 0.2102 26.56 / 48%);\n}\n\n/* The indicator fills its control. In a menu row (the menu-item profile) it is\n * the check in the row's leading slot instead, which the row centres. */\n:root\n  :where(.sp-checkbox-indicator):where(:not([data-profile=\"menu-item\"] > *)),\n:root :where(.sp-radio-indicator):where(:not([data-profile=\"menu-item\"] > *)) {\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  border-radius: inherit;\n  background: transparent;\n}\n\n:root .sp-checkbox-indicator,\n:root .sp-table-selection-indicator {\n  color: var(--sp-rw-fg-on-accent);\n}\n\n:root .sp-radio-indicator::before {\n  width: var(--sp-rw-space-1-5);\n  height: var(--sp-rw-space-1-5);\n  border-radius: var(--sp-rw-radius-full);\n  background: var(--sp-rw-fg-on-accent);\n}\n\n/* Switch: measured 32x20 default with a 16px thumb and 12px travel. The\n * unchecked track tints the surface it sits on (the same surface colour the\n * rings use, the page background as fallback). A fixed white track disappeared\n * on white surfaces such as Dialog, Popover and Card. A translucent surface\n * has no colour to tint, so it names the track itself (--sp-rw-surface-track,\n * set by glass).\n *\n * The track is a pill up to the surface radius, which every size reaches at\n * the md radius; a smaller provider radius squares it off, down to a square\n * track at none. The thumb stays concentric, 2 px inside it. */\n:root .sp-switch {\n  --sp-rw-switch-thumb: var(--sp-rw-choice-md);\n  --sp-rw-switch-radius: min(\n    calc((var(--sp-rw-switch-thumb) + var(--sp-rw-space-1)) / 2),\n    var(--sp-rw-radius-surface)\n  );\n  width: calc(var(--sp-rw-switch-thumb) * 2);\n  height: calc(var(--sp-rw-switch-thumb) + var(--sp-rw-space-1));\n  padding: var(--sp-rw-space-0-5);\n  border: 0;\n  border-radius: var(--sp-rw-switch-radius);\n  background: var(\n    --sp-rw-surface-track,\n    color-mix(\n      in oklab,\n      var(--sp-rw-fg-primary) 14%,\n      var(--sp-rw-surface-color, var(--sp-rw-bg-base))\n    )\n  );\n}\n\n/* The edge of every Switch track: the standalone control's and the track of a\n * Menu, Context Menu or Drawer Menu switch row. The unchecked track's edge is\n * the only cue that the switch is there, so it reaches 3:1 against the\n * surfaces around it, like the edge of an unchecked Checkbox\n * (--sp-rw-choice-rim, WCAG 1.4.11). The track is grey already, so a lighter\n * alpha than the Checkbox's renders the same grey. The checked track keeps its\n * faint edge. A menu track's checked state is its row's. */\n:root .sp-switch,\n:root .sp-switch-track {\n  --sp-rw-switch-edge: light-dark(oklch(0% 0 0 / 36%), oklch(100% 0 0 / 34%));\n  box-shadow:\n    inset 0 2px 2px -1px light-dark(oklch(0% 0 0 / 4%), oklch(0% 0 0 / 8%)),\n    inset 0 4px 4px -2px light-dark(oklch(0% 0 0 / 4%), oklch(0% 0 0 / 8%)),\n    inset 0 0 0 var(--sp-rw-border-default)\n      var(--sp-rw-switch-rim, var(--sp-rw-switch-edge));\n}\n\n:root .sp-switch[data-checked],\n:root :is([data-checked] > .sp-switch-track) {\n  --sp-rw-switch-edge: light-dark(oklch(0% 0 0 / 12%), oklch(100% 0 0 / 16%));\n}\n\n:root .sp-switch[data-size=\"sm\"] {\n  --sp-rw-switch-thumb: var(--sp-rw-choice-sm);\n}\n\n:root .sp-switch[data-size=\"lg\"] {\n  --sp-rw-switch-thumb: var(--sp-rw-choice-lg);\n}\n\n:root .sp-switch[data-size=\"xl\"] {\n  --sp-rw-switch-thumb: var(--sp-rw-choice-xl);\n}\n\n:root .sp-switch[data-checked] {\n  background: var(--sp-rw-switch-checked, var(--sp-rw-bg-accent));\n}\n\n:root .sp-switch .sp-switch-thumb {\n  width: var(--sp-rw-switch-thumb);\n  height: var(--sp-rw-switch-thumb);\n  border-radius: max(\n    0px,\n    calc(var(--sp-rw-switch-radius) - var(--sp-rw-space-0-5))\n  );\n  background: oklch(100% 0 0);\n  box-shadow:\n    inset 0 1px 0 oklch(100% 0 0),\n    inset 0 -1px 0 oklch(100% 0 0 / 80%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(100% 0 0),\n    0 2px 2px -1px oklch(0% 0 0 / 4%),\n    0 4px 4px -2px oklch(0% 0 0 / 2%),\n    0 0 0 var(--sp-rw-border-default) oklch(0% 0 0 / 12%);\n  /* The thumb travel below replaces the shared thumb's translate utility, which\n   * menu switch items still use; keep it off so the travel is never doubled. */\n  translate: none !important;\n  scale: none;\n  transform: translateX(0);\n  transition:\n    transform var(--motion-duration-fast) var(--motion-ease-switch),\n    border-radius var(--motion-duration-fast) var(--motion-ease-switch);\n}\n\n:root .sp-switch[data-checked] .sp-switch-thumb {\n  transform: translateX(calc(var(--sp-rw-switch-thumb) - var(--sp-rw-space-1)));\n}\n\n/* The thumb starts at the track's inline start, so in a right-to-left page\n * it travels left, within the track. A build for older browsers turns\n * :dir(rtl) into a list of right-to-left languages, so the dir attribute is\n * named as well, as Tailwind's rtl variant does. */\n:root\n  .sp-switch[data-checked]:is(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *)\n  .sp-switch-thumb {\n  transform: translateX(\n    calc((var(--sp-rw-switch-thumb) - var(--sp-rw-space-1)) * -1)\n  );\n}\n\n/* While a switch, its label or its field label is pressed, the thumb of the\n * standalone control and of a menu row stretches from its leading edge by a\n * tenth of its width; a disabled one stays. */\n:root\n  :is(.sp-base-switch, .sp-base-label, .sp-field-label):active\n  .sp-switch-thumb:not([data-disabled]) {\n  scale: 110% 1;\n}\n\n:root .sp-switch[data-disabled] {\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n:root .sp-switch[aria-invalid=\"true\"] {\n  box-shadow:\n    inset 0 2px 2px -1px light-dark(oklch(0% 0 0 / 4%), oklch(0% 0 0 / 8%)),\n    inset 0 4px 4px -2px light-dark(oklch(0% 0 0 / 4%), oklch(0% 0 0 / 8%)),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(67.3% 0.2102 26.56 / 48%);\n}\n\n/* Toggle */\n:root .sp-base-toggle {\n  height: var(--sp-rw-control-md);\n  min-width: var(--sp-rw-control-md);\n  gap: var(--sp-rw-button-content-gap);\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  padding-inline: var(--sp-rw-space-2);\n  background: transparent;\n  box-shadow: none;\n  color: var(--sp-rw-fg-secondary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-base-toggle[data-size=\"sm\"] {\n  height: var(--sp-rw-control-sm);\n  min-width: var(--sp-rw-control-sm);\n  padding-inline: var(--sp-rw-space-1-5);\n}\n\n:root .sp-base-toggle[data-size=\"lg\"] {\n  height: var(--sp-rw-control-lg);\n  min-width: var(--sp-rw-control-lg);\n  padding-inline: var(--sp-rw-space-2-5);\n}\n\n:root .sp-base-toggle[data-size=\"xl\"] {\n  height: var(--sp-rw-control-xl);\n  min-width: var(--sp-rw-control-xl);\n  padding-inline: var(--sp-rw-space-3);\n  border-radius: var(--sp-rw-radius-surface);\n  /* The xl label of the control scale, as on Button and Tabs. */\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* padding=\"comfortable\": 12 px at the sides at every size, the padding the\n * Toggle declares; it follows the size rules above, which it ties. */\n:root .sp-base-toggle[data-padding=\"comfortable\"] {\n  padding-inline: var(--sp-rw-space-3);\n}\n\n:root .sp-base-toggle[data-variant=\"outline\"] {\n  box-shadow: var(--sp-rw-outline-rim);\n}\n\n/* A disabled Toggle shows no hover and no press: disabled by the attribute, or\n * by aria-disabled where a Toolbar keeps it in the tab order. */\n:root\n  .sp-base-toggle:not(:disabled, [aria-disabled=\"true\"]):hover:not(\n    [data-pressed]\n  ) {\n  background: var(--sp-rw-glow-hover);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  .sp-base-toggle:not(:disabled, [aria-disabled=\"true\"]):active:not(\n    [data-pressed]\n  ) {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root .sp-base-toggle[data-pressed] {\n  background: var(--sp-rw-glow-pressed);\n  color: var(--sp-rw-fg-primary);\n}\n\n/* Public Toggle profiles own reusable consumer-specific chassis details. */\n:root .sp-base-toggle[data-padding=\"icon\"] {\n  aspect-ratio: 1;\n}\n\n/* Theme is expressed by the glyph, not a latched selection surface. */\n:root .sp-base-toggle[data-profile=\"theme\"] {\n  background: transparent;\n  color: var(--sp-rw-fg-primary);\n}\n\n:root\n  .sp-base-toggle[data-profile=\"theme\"]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ):hover {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root\n  .sp-base-toggle[data-profile=\"theme\"]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ):active {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root .sp-base-toggle[data-profile=\"group\"][data-pressed] {\n  background: var(--sp-semantic-color-input);\n}\n\n@supports (color: color-mix(in lab, red, red)) {\n  :root .sp-base-toggle[data-profile=\"group\"][data-pressed] {\n    background: color-mix(\n      in oklab,\n      var(--sp-semantic-color-input) 64%,\n      transparent\n    );\n  }\n}\n\n.dark\n  .sp-base-toggle[data-profile=\"group\"][data-variant=\"outline\"][data-pressed],\n[data-theme=\"dark\"]\n  .sp-base-toggle[data-profile=\"group\"][data-variant=\"outline\"][data-pressed] {\n  background: var(--sp-semantic-color-input);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"]\n  > .sp-base-toggle[data-profile=\"group\"] {\n  border: 0;\n  border-radius: 0;\n  box-shadow: none;\n}\n\n/* A join between two toggles is the ToggleGroupSeparator's hairline, drawn the\n * same way: a background on the toggle's start edge (its ::before), so both\n * lines paint equally thick at every pixel density. */\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"horizontal\"]\n  > .sp-base-toggle[data-profile=\"group\"]\n  + .sp-base-toggle[data-profile=\"group\"]::before {\n  inset-inline-end: auto;\n  width: var(--sp-rw-border-default);\n  border-radius: 0;\n  background: var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"vertical\"]\n  > .sp-base-toggle[data-profile=\"group\"]\n  + .sp-base-toggle[data-profile=\"group\"]::before {\n  inset-block-end: auto;\n  height: var(--sp-rw-border-default);\n  border-radius: 0;\n  background: var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"horizontal\"]\n  > .sp-base-toggle[data-profile=\"group\"]:first-child {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-end-start-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"horizontal\"]\n  > .sp-base-toggle[data-profile=\"group\"]:last-child {\n  border-start-end-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"vertical\"]\n  > .sp-base-toggle[data-profile=\"group\"]:first-child {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-start-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-toggle-group[data-variant=\"outline\"][data-orientation=\"vertical\"]\n  > .sp-base-toggle[data-profile=\"group\"]:last-child {\n  border-end-start-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n/* Group joins direct controls at their shared edges. Only the outer corners\n * keep the control radius, regardless of each child's standalone chassis. */\n:root .sp-group {\n  gap: 0;\n}\n\n:root .sp-group:has(> .sp-group) {\n  gap: var(--sp-rw-space-2);\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :not(.sp-separator, .sp-group) {\n  border-radius: 0;\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :first-child:not(.sp-separator, .sp-group) {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-end-start-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :last-child:not(.sp-separator, .sp-group) {\n  border-start-end-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n:root .sp-group[data-orientation=\"vertical\"] > :not(.sp-separator, .sp-group) {\n  border-radius: 0;\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :first-child:not(.sp-separator, .sp-group) {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-start-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :last-child:not(.sp-separator, .sp-group) {\n  border-end-start-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n/* Related controls either keep breathing room or share one visible chassis. */\n:root\n  :is(\n    .sp-toggle-group[data-variant=\"outline\"],\n    .sp-group:has(> [data-chassis][data-variant=\"outline\"])\n  ) {\n  gap: 0px;\n  border-radius: var(--sp-rw-radius-control);\n  background: transparent;\n  box-shadow: var(--sp-rw-field-shadow);\n}\n\n:root .sp-group:has(> [data-chassis][data-variant=\"outline\"]) {\n  box-shadow: 0 0 0 var(--sp-rw-border-default) var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-group:has(> [data-chassis][data-variant=\"outline\"])\n  > :not(.sp-separator) {\n  border: 0;\n  border-radius: 0;\n  box-shadow: none;\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"]):has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :not(.sp-separator)\n  + :not(.sp-separator) {\n  border-inline-start: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]:has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :not(.sp-separator)\n  + :not(.sp-separator) {\n  border-block-start: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"]):has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :first-child:not(.sp-separator) {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-end-start-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"]):has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :last-child:not(.sp-separator) {\n  border-start-end-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]:has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :first-child:not(.sp-separator) {\n  border-start-start-radius: var(--sp-rw-radius-control);\n  border-start-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]:has(\n    > [data-chassis][data-variant=\"outline\"]\n  )\n  > :last-child:not(.sp-separator) {\n  border-end-start-radius: var(--sp-rw-radius-control);\n  border-end-end-radius: var(--sp-rw-radius-control);\n}\n\n:root\n  .sp-group:has(> [data-chassis][data-variant=\"outline\"])\n  > [data-chassis][data-variant=\"outline\"] {\n  --sp-rw-button-rim: none;\n}\n\n/* separator=\"inset\" replaces the full-height join between direct buttons with\n * the short centered divider that Group draws on each button's end edge. */\n:root\n  .sp-group[data-separator=\"inset\"]:not([data-orientation=\"vertical\"])\n  > .sp-button:not(:last-child)::before {\n  width: var(--sp-rw-border-default);\n  background: var(--sp-rw-border-translucent);\n}\n\n:root\n  .sp-group[data-separator=\"inset\"]:not([data-orientation=\"vertical\"])\n  > .sp-button:not(:last-child)\n  + :not(.sp-separator) {\n  border-inline-start: 0;\n}\n\n/* The ::before layer of every joined child, a chassis or not, meets its\n * neighbour's at a join, as the child's edge and corners do. A part that\n * another part follows squares the corners of its end edge, and one that\n * follows another part those of its start edge; a part other than a separator\n * also reaches half a pixel past that edge. In a vertical group a part that\n * another part follows hides its layer. A part that another part follows is a\n * [data-slot] child that is not the last of them,\n * :not(:nth-last-child(1 of [data-slot]), :not([data-slot])), and one that\n * follows another part is not the first of them; neither walks the siblings\n * as :has() would (F-08). Chromium does not match an :nth-child() of a\n * selector that stands right before ::before, nor inside an :is() there, which\n * the minifier removes, but it matches one inside :not() (Chromium 151). */\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :not(:nth-last-child(1 of [data-slot]), :not([data-slot]))::before {\n  border-start-end-radius: 0;\n  border-end-end-radius: 0;\n  content: \"\";\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :not(:nth-last-child(1 of [data-slot]), :not([data-slot])):not(\n    .sp-separator\n  )::before {\n  inset-inline-end: -0.5px;\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :not(:nth-child(1 of [data-slot]), :not([data-slot]))::before {\n  border-start-start-radius: 0;\n  border-end-start-radius: 0;\n  content: \"\";\n}\n\n:root\n  .sp-group:not([data-orientation=\"vertical\"])\n  > :not(:nth-child(1 of [data-slot]), :not([data-slot])):not(\n    .sp-separator\n  )::before {\n  inset-inline-start: -0.5px;\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :not(:nth-last-child(1 of [data-slot]), :not([data-slot]))::before {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n  content: \"\";\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :not(:nth-last-child(1 of [data-slot]), :not([data-slot])):not(\n    .sp-separator\n  )::before {\n  display: none;\n  bottom: -0.5px;\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :not(:nth-child(1 of [data-slot]), :not([data-slot]))::before {\n  border-top-left-radius: 0;\n  border-top-right-radius: 0;\n  content: \"\";\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :not(:nth-child(1 of [data-slot]), :not([data-slot])):not(\n    .sp-separator\n  )::before {\n  top: -0.5px;\n}\n\n/* In Dark the first child of a vertical group shows its layer and the last\n * hides it, where the rules above leave it shown. */\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :first-child:where(.dark *)::before,\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :first-child:where([data-theme=\"dark\"] *)::before {\n  display: block;\n  content: \"\";\n}\n\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :last-child:where(.dark *)::before,\n:root\n  .sp-group[data-orientation=\"vertical\"]\n  > :last-child:where([data-theme=\"dark\"] *)::before {\n  display: none;\n  content: \"\";\n}\n\n/* separator=\"inset\": the divider is 20 px high, centred on the end edge of a\n * button that another part follows; the joins above keep their offsets. */\n:root .sp-group[data-separator=\"inset\"] > .sp-button:not(:last-child)::before {\n  inset-block: auto;\n  inset-inline-start: auto;\n  inset-inline-end: 0;\n  top: 50%;\n  height: var(--sp-rw-space-5);\n  translate: 0 -50%;\n}\n\n/* A separator in Dark: the input colour at 32 %, and at 64 % beside the item\n * under the pointer or pressed, the separator before that item and the one\n * after it. */\n:root\n  .sp-base-separator[data-profile=\"group\"]:where(\n    .dark,\n    .dark *,\n    [data-theme=\"dark\"],\n    [data-theme=\"dark\"] *\n  )::before {\n  background-color: color-mix(\n    in oklab,\n    var(--sp-semantic-color-input) 32%,\n    transparent\n  );\n}\n\n:root\n  .sp-group\n  > .sp-separator:has(~ button:hover):not(\n    :has(~ .sp-separator ~ [data-slot]:hover)\n  ):where(.dark *, [data-theme=\"dark\"] *)::before,\n:root\n  .sp-group\n  > .sp-separator:has(~ [data-slot][data-pressed]):not(\n    :has(~ .sp-separator ~ [data-slot][data-pressed])\n  ):where(.dark *, [data-theme=\"dark\"] *)::before,\n:root\n  .sp-group\n  > button:hover\n  ~ .sp-separator:not([data-slot]:hover ~ .sp-separator ~ .sp-separator):where(\n    .dark *,\n    [data-theme=\"dark\"] *\n  )::before,\n:root\n  .sp-group\n  > [data-slot][data-pressed]\n  ~ .sp-separator:not(\n    [data-slot][data-pressed] ~ .sp-separator ~ .sp-separator\n  ):where(.dark *, [data-theme=\"dark\"] *)::before {\n  background-color: color-mix(\n    in oklab,\n    var(--sp-semantic-color-input) 64%,\n    transparent\n  );\n  content: \"\";\n}\n\n/* A Group that pairs GroupText with an input is one field. The shared field\n * rules above give the Group the frame, focus, invalid and disabled states, so\n * the input hands over its own contour, fill and states instead of drawing a\n * second frame beside the text. The separator no longer tucks under the\n * input's own ring on focus, so it stays where it is. */\n:root\n  .sp-group:has(> .sp-group-text):has(> .sp-input-control:not([data-unstyled]))\n  > .sp-input-control:not([data-unstyled]) {\n  background: transparent;\n  box-shadow: none;\n  opacity: 1;\n}\n\n:root\n  .sp-group:has(> .sp-group-text):has(> .sp-input-control:not([data-unstyled]))\n  > .sp-separator {\n  translate: none;\n}\n\n/* The ellipsis trigger is a bare inline action as large as the ellipsis, so\n * the row keeps its height; its hit area reaches 24 px square around it\n * (WCAG 2.5.8) without taking room. */\n:root .sp-breadcrumb-item [data-chassis]:has(.sp-breadcrumb-ellipsis)::after {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: max(100%, var(--sp-rw-space-6));\n  height: max(100%, var(--sp-rw-space-6));\n  content: \"\";\n  translate: -50% -50%;\n}\n\n/* A Breadcrumb trail wraps onto more lines where it has no room; a header bar,\n * which is one line, keeps it on one row instead (Dashboard Shell header). The\n * trail can shrink inside a grid or flex parent, the Dashboard Shell header\n * included, so a current page that truncates never makes its container wider\n * (rulebook K5). This rule is the trail's one owner of its minimum width. */\n:where(.sp-breadcrumb) {\n  min-width: 0;\n}\n\n:where(.sp-breadcrumb-list) {\n  flex-wrap: wrap;\n}\n\n/* Breadcrumb links and the ellipsis ease their colour with the kit's state\n * motion (M1); reduced motion is instant (M5). */\n:where(.sp-breadcrumb-link),\n:where(.sp-breadcrumb-ellipsis) {\n  transition: color var(--motion-duration-fast) var(--motion-ease-standard);\n}\n\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-breadcrumb-link,\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-breadcrumb-ellipsis {\n  transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-breadcrumb-link,\n  :root .sp-breadcrumb-ellipsis {\n    transition: none;\n  }\n}\n\n/* Slider */\n:root .sp-slider {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* The Slider stacks what it holds, such as a label and value row, 8 px above\n * the control; the labels below the control tuck under its hit area (S10). */\n:where(.sp-slider) {\n  display: flex;\n  flex-direction: column;\n  gap: var(--sp-rw-space-2);\n}\n\n/* The thumb eases its ring and scale and the indicator its extent; both follow\n * the pointer at once while the thumb is dragged. Reduced motion, from the\n * system or the kit, is instant (M5). */\n:where(.sp-slider-thumb) {\n  transition-duration: var(--motion-duration-slow);\n  transition-property: box-shadow, scale;\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:where(.sp-slider-indicator) {\n  transition-duration: var(--motion-duration-slow);\n  transition-property: inset-inline-start, bottom, width, height;\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:where(.sp-slider-thumb[data-dragging]),\n:where(.sp-slider-track:has(> .sp-slider-thumb:active) > .sp-slider-indicator) {\n  transition: none;\n}\n\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-slider-thumb,\n:is(:root[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-slider-indicator {\n  transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root .sp-slider-thumb,\n  :root .sp-slider-indicator {\n    transition: none;\n  }\n}\n\n:root .sp-slider-track {\n  border-radius: var(--sp-rw-radius-full);\n  background: transparent;\n  box-shadow: none;\n}\n\n:root .sp-slider-track::before {\n  border-radius: var(--sp-rw-radius-full);\n  background: light-dark(oklch(0% 0 0 / 14%), oklch(100% 0 0 / 14%));\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-border-translucent);\n}\n\n:root .sp-slider-track[data-orientation=\"horizontal\"] {\n  height: var(--sp-rw-space-1-5);\n}\n\n:root .sp-slider-track[data-orientation=\"vertical\"] {\n  width: var(--sp-rw-space-1-5);\n}\n\n:root .sp-slider-track[data-size=\"sm\"][data-orientation=\"horizontal\"] {\n  height: var(--sp-rw-space-1);\n}\n\n:root .sp-slider-track[data-size=\"sm\"][data-orientation=\"vertical\"] {\n  width: var(--sp-rw-space-1);\n}\n\n:root .sp-slider-track[data-size=\"lg\"][data-orientation=\"horizontal\"] {\n  height: var(--sp-rw-space-2);\n}\n\n:root .sp-slider-track[data-size=\"lg\"][data-orientation=\"vertical\"] {\n  width: var(--sp-rw-space-2);\n}\n\n:root .sp-slider-track[data-size=\"xl\"][data-orientation=\"horizontal\"] {\n  height: var(--sp-rw-space-2-5);\n}\n\n:root .sp-slider-track[data-size=\"xl\"][data-orientation=\"vertical\"] {\n  width: var(--sp-rw-space-2-5);\n}\n\n:root .sp-slider-indicator {\n  border-radius: inherit;\n  background: var(--sp-rw-bg-accent);\n}\n\n:root .sp-slider-thumb {\n  --sp-rw-slider-thumb-rim: oklch(0% 0 0 / 24%);\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  border: 0;\n  border-radius: var(--sp-rw-radius-full);\n  background: oklch(100% 0 0);\n  box-shadow:\n    inset 0 1px 0 oklch(100% 0 0),\n    inset 0 -1px 0 oklch(100% 0 0 / 80%),\n    inset 0 0 0 var(--sp-rw-border-default) oklch(100% 0 0),\n    0 2px 2px -1px oklch(0% 0 0 / 4%),\n    0 4px 4px -2px oklch(0% 0 0 / 2%),\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-slider-thumb-rim);\n}\n\n:root .sp-slider-thumb[data-size=\"sm\"] {\n  width: calc(var(--sp-rw-space) * 3.5);\n  height: calc(var(--sp-rw-space) * 3.5);\n}\n\n:root .sp-slider-thumb[data-size=\"lg\"] {\n  width: calc(var(--sp-rw-space) * 4.5);\n  height: calc(var(--sp-rw-space) * 4.5);\n}\n\n:root .sp-slider-thumb[data-size=\"xl\"] {\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n}\n\n:root .sp-slider[aria-invalid=\"true\"] .sp-slider-indicator {\n  background: var(--sp-rw-bg-destructive);\n}\n\n:root .sp-slider[aria-invalid=\"true\"] .sp-slider-thumb {\n  --sp-rw-slider-thumb-rim: oklch(67.3% 0.2102 26.56 / 48%);\n}\n\n/* A disabled Slider fades once, with its label row, track and thumbs. */\n:root .sp-slider[data-disabled] {\n  opacity: var(--sp-semantic-state-disabled-opacity);\n}\n\n/* Disabled parts. A field or a label that fades as disabled fades the parts it\n * holds with it: a disabled picker, step, clear or chip remove Button and a\n * disabled checkbox, radio or switch inside it keep their full opacity instead\n * of fading a second time. These rules follow the Button and choice control\n * disabled rules, which they outrank by order. */\n:root\n  :where(\n    .sp-base-input-group:has(\n      :is(input:not([type=\"hidden\"]), textarea):disabled,\n      [role=\"spinbutton\"][aria-disabled=\"true\"]\n    ),\n    .sp-number-field-group[data-disabled],\n    .sp-combobox-chips:has(:is(input:not([type=\"hidden\"]), textarea):disabled),\n    .sp-combobox-chips[data-layout=\"standalone\"]:has(:disabled),\n    .sp-base-badge[data-disabled]\n  )\n  [data-chassis][data-size][data-variant]:is(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  opacity: 1;\n}\n\n:root\n  :where(.sp-base-label, .sp-field-label, .sp-select-label):where(\n    [data-disabled],\n    :has(\n      :is(input, textarea, select):not(\n          [type=\"hidden\"],\n          [type=\"button\"],\n          [type=\"submit\"],\n          [type=\"reset\"],\n          [type=\"image\"]\n        ):disabled,\n      [role=\"spinbutton\"][aria-disabled=\"true\"],\n      [data-select-trigger]:is(:disabled, [data-disabled]),\n      :is(.sp-checkbox, .sp-radio, .sp-switch)[data-disabled]\n    )\n  )\n  :is(\n    .sp-input-control,\n    .sp-textarea-control,\n    .sp-base-input-group,\n    .sp-number-field-group,\n    .sp-checkbox,\n    .sp-radio,\n    .sp-switch,\n    [data-chassis][data-size][data-variant]\n  ):is(:disabled, [data-disabled], [aria-disabled=\"true\"], :has(:disabled)) {\n  opacity: 1;\n}\n\n:root .sp-slider-labels,\n:root .sp-slider-value {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-12);\n  line-height: var(--sp-rw-line-18);\n}\n\n/* Avatar and Badge. The avatar is a size container, so its initials can scale\n * with it. */\n:root .sp-avatar {\n  position: relative;\n  container-type: inline-size;\n  background: var(--sp-rw-bg-depth);\n  color: var(--sp-rw-fg-secondary);\n  font-size: var(--sp-rw-font-12);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-18);\n}\n\n:root .sp-avatar::after {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-border-translucent);\n  content: \"\";\n  pointer-events: none;\n}\n\n:root .sp-avatar-image {\n  display: block;\n  border-radius: inherit;\n  object-fit: cover;\n}\n\n/* The fallback takes the radius of its avatar, as the image does, and its\n * initials are 12 px in the default 32 px avatar and grow with larger ones,\n * from 10 px at the smallest. */\n:root .sp-avatar-fallback {\n  border-radius: inherit;\n  background: color-mix(\n    in oklch,\n    var(--sp-rw-bg-primary) 96%,\n    var(--sp-rw-fg-primary) 4%\n  );\n  color: var(--sp-rw-fg-secondary);\n  font-size: max(var(--sp-rw-font-10), 37.5cqw);\n  line-height: 1;\n}\n\n/* The default badge is a white surface, the card colour, with the hairline\n * contour below; every other variant paints over it.\n *\n * Every badge's corners come from --sp-rw-radius-badge, which the kit leaves\n * unset: a badge then takes the xs role radius of its scope. Set the token to\n * var(--sp-rw-radius-full) on :root to make every badge a pill, at any provider\n * radius. One badge takes shape=\"pill\". */\n:root .sp-base-badge {\n  height: var(--sp-rw-space-5);\n  min-width: var(--sp-rw-space-5);\n  gap: var(--sp-rw-space-1);\n  border: var(--sp-rw-border-default) solid transparent;\n  border-radius: var(--sp-rw-radius-badge, var(--sp-rw-radius-xs));\n  padding: var(--sp-rw-space-0-5) var(--sp-rw-space-1-5);\n  background: var(--sp-rw-bg-secondary);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-12);\n  font-weight: var(--sp-rw-badge-font-weight, 450);\n  line-height: var(--sp-rw-font-12);\n}\n\n/* The hairline contour of the neutral variants is their border, which every\n * badge has (transparent on the others): it sits on the badge's edge with the\n * fill inside it, so no band of fill shows outside the line, as shadcn's\n * outline badge draws it. The default badge draws it on its white surface and\n * the outline badge on the surface behind it, so the two differ only by the\n * fill. */\n:root .sp-base-badge:where([data-variant=\"default\"], [data-variant=\"outline\"]) {\n  border-color: var(--sp-rw-border-translucent);\n}\n\n/* The icon follows the badge size: 10, 12, 14 and 16 px for sm, md, lg\n * and xl, set by the size rules below. */\n:root .sp-base-badge > svg:not([class*=\"size-\"]) {\n  width: var(--sp-badge-icon-size, var(--sp-rw-space-3));\n  height: var(--sp-badge-icon-size, var(--sp-rw-space-3));\n}\n\n/* Entitlement profile: the label of a plan or an entitlement, with its icon in\n * a 16 px box. It takes the info tint of the accent in Light and Dark, so the\n * profile adds only the geometry. */\n:root .sp-base-badge[data-profile=\"entitlement\"] {\n  gap: 1px;\n  border: 0;\n  padding: 1px var(--sp-rw-space-1-5) 1px var(--sp-rw-space-1);\n  box-shadow: none;\n  font-weight: 480;\n  line-height: var(--sp-rw-line-18);\n}\n\n:root .sp-base-badge[data-profile=\"entitlement\"] > svg {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  padding: var(--sp-rw-space-0-5);\n}\n\n/* Pill shape: the one place that rounds a Badge fully, like the pill Button;\n * `rounded-full` stays an alias. It sets the badge's own radius token, which\n * the rule above reads, so the light and anything else that follows the\n * badge's corners turns pill with it. Profiles that draw their own circle set\n * their radius directly. */\n:root .sp-base-badge:is([data-shape=\"pill\"], .rounded-full) {\n  --sp-rw-radius-badge: var(--sp-rw-radius-full);\n}\n\n:root .sp-base-badge[data-size=\"sm\"] {\n  --sp-badge-icon-size: var(--sp-rw-space-2-5);\n  height: var(--sp-rw-space-4);\n  min-width: var(--sp-rw-space-4);\n  padding-inline: var(--sp-rw-space-1);\n  font-size: var(--sp-rw-font-10);\n  line-height: var(--sp-rw-line-16);\n}\n\n:root .sp-base-badge[data-size=\"lg\"] {\n  --sp-badge-icon-size: calc(var(--sp-rw-space) * 3.5);\n  height: var(--sp-rw-space-6);\n  min-width: var(--sp-rw-space-6);\n  padding-inline: var(--sp-rw-space-2);\n  font-size: var(--sp-rw-font-13);\n}\n\n:root .sp-base-badge[data-size=\"xl\"] {\n  --sp-badge-icon-size: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-7);\n  min-width: var(--sp-rw-space-7);\n  padding-inline: var(--sp-rw-space-2-5);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* A removable Tag keeps its remove action close: 2 px from the label and\n * 4 px from the end, at every size. */\n:root .sp-tag[data-removable] {\n  gap: var(--sp-rw-space-0-5);\n  padding-right: var(--sp-rw-space-1);\n}\n\n/* Secondary is the neutral fill, mixed from the kit's tokens: the muted grey at\n * 60 % in Light, and 6 % of the text colour in Dark, which lightens the surface\n * it sits on. It stays translucent, so it reads on a white card, on a tinted\n * row and on a dark card (Lukas, 16 October 2026). */\n:root .sp-base-badge[data-variant=\"secondary\"] {\n  background: light-dark(\n    color-mix(in oklch, var(--sp-rw-bg-muted) 60%, transparent),\n    color-mix(in oklch, var(--sp-rw-fg-primary) 6%, transparent)\n  );\n  color: var(--sp-rw-fg-secondary);\n}\n\n/* The outline badge has no fill of its own: whatever it sits on shows through. */\n:root .sp-base-badge[data-variant=\"outline\"] {\n  background: transparent;\n  color: var(--sp-rw-fg-secondary);\n}\n\n:root\n  .sp-base-badge:where(\n    [data-variant=\"success\"],\n    [data-variant=\"info\"],\n    [data-variant=\"warning\"],\n    [data-variant=\"error\"],\n    [data-variant=\"destructive\"],\n    [data-variant=\"accent\"],\n    [data-variant=\"inverse\"]\n  ) {\n  background: var(--sp-rw-badge-bg);\n  color: var(--sp-rw-badge-fg);\n}\n\n:root .sp-base-badge[data-variant=\"success\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-positive-translucent);\n  --sp-rw-badge-fg: var(--sp-rw-fg-positive);\n}\n\n:root .sp-base-badge[data-variant=\"info\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-accent-translucent);\n  --sp-rw-badge-fg: var(--sp-rw-fg-accent);\n}\n\n:root .sp-base-badge[data-variant=\"warning\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-attention-translucent);\n  --sp-rw-badge-fg: var(--sp-rw-fg-attention);\n}\n\n:root .sp-base-badge[data-variant=\"error\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-destructive-translucent);\n  --sp-rw-badge-fg: var(--sp-rw-fg-destructive);\n}\n\n/* Destructive is filled like the destructive Button; error stays tinted. */\n:root .sp-base-badge[data-variant=\"destructive\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-destructive);\n  --sp-rw-badge-fg: var(--sp-rw-fg-emphasis);\n}\n\n/* Accent is the solid brand fill, filled like the accent Button; the accent\n * tint is the info variant. Badge renders the deprecated `brand` as accent. */\n:root .sp-base-badge[data-variant=\"accent\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-accent);\n  --sp-rw-badge-fg: var(--sp-rw-fg-on-accent);\n}\n\n/* Inverse is filled like the inverse Button, the primary action: the black that\n * the default badge used to be. */\n:root .sp-base-badge[data-variant=\"inverse\"] {\n  --sp-rw-badge-bg: var(--sp-rw-bg-inverse);\n  --sp-rw-badge-fg: var(--sp-rw-fg-inverse);\n}\n\n/* Link and button badges: a hover tint from the badge's own text color works on\n * every variant, and the control focus ring replaces the utility ring. The\n * contour of the default and outline badges is their border, so it stays. */\n:root :is(a, button).sp-base-badge:hover {\n  background-image: linear-gradient(\n    color-mix(in srgb, currentColor 6%, transparent),\n    color-mix(in srgb, currentColor 6%, transparent)\n  );\n}\n\n:root :is(a, button).sp-base-badge:focus-visible {\n  box-shadow: none;\n}\n\n/* AvatarGroup size and ring. Every avatar in a group and the overflow count\n * take the group's size (32 px outside a group) and draw one 2 px ring in the\n * color of the surface they sit on: --sp-rw-surface-color, which Card, Dialog,\n * Popover and the other surfaces publish, or the page background. It separates\n * each circle from the one it overlaps, so a group passes no size or ring\n * class. An avatar is a circle of that size; the count is at least one. */\n:root .sp-base-badge[data-profile=\"group-overflow-count\"],\n:root .sp-avatar-group .sp-avatar {\n  min-width: var(--avatar-group-size, var(--sp-rw-space-8));\n  height: var(--avatar-group-size, var(--sp-rw-space-8));\n  box-shadow: 0 0 0 2px var(--sp-rw-surface-color, var(--sp-rw-bg-base));\n}\n\n:root .sp-avatar-group .sp-avatar {\n  width: var(--avatar-group-size, var(--sp-rw-space-8));\n}\n\n/* AvatarGroupCount: the circle at the end of a group, a kit-internal Badge\n * profile. A count never truncates: a longer one grows as a pill of the same\n * height, with 4 px inside its ends, and a short one stays a circle. The ring\n * above replaces the default badge's hairline contour; the rim below draws its\n * edge. */\n:root .sp-base-badge[data-profile=\"group-overflow-count\"] {\n  gap: 0;\n  border: 0;\n  border-radius: var(--sp-rw-radius-full);\n  padding: 0 var(--sp-rw-space-1);\n  background: var(--sp-rw-bg-primary);\n  color: var(--color-muted-foreground);\n  font-size: var(--sp-rw-font-12);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-16);\n}\n\n:root .sp-base-badge[data-profile=\"group-overflow-count\"]::before {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default) var(--color-border);\n  content: \"\";\n  pointer-events: none;\n}\n\n/* Count profile: a compact notification count, 16px high with a full radius\n * and 10px type. IconButton passes it and Button only places the Badge. A\n * count never truncates: it grows as a pill, at least as wide as it is high. */\n:root .sp-base-badge[data-profile=\"count\"] {\n  display: block;\n  width: auto;\n  height: var(--sp-rw-space-4);\n  min-width: var(--sp-rw-space-4);\n  max-width: none;\n  border: 0;\n  border-radius: var(--sp-rw-radius-full);\n  padding: 0 var(--sp-rw-space-1);\n  outline: 2px solid var(--sp-rw-surface-color, var(--sp-rw-bg-base));\n  font-size: var(--sp-rw-font-10);\n  line-height: var(--sp-rw-line-16);\n  text-align: center;\n  white-space: nowrap;\n}\n\n/* The label of a count stays whole: one line at its full width, never\n * clamped. */\n:root .sp-base-badge[data-profile=\"count\"] .sp-badge-label,\n:root .sp-base-badge[data-profile=\"group-overflow-count\"] .sp-badge-label {\n  display: inline;\n  overflow: visible;\n  -webkit-line-clamp: none;\n  line-height: inherit;\n  white-space: nowrap;\n}\n\n/* Verified profile: a solid accent circle with a check, the mark of a verified\n * account. The size scale gives its diameter. Like the count it sits on another\n * element, so its ring takes the surrounding surface colour; the host only\n * places it. */\n:root .sp-base-badge[data-profile=\"verified\"] {\n  min-width: 0;\n  aspect-ratio: 1;\n  gap: 0;\n  border: 0;\n  border-radius: var(--sp-rw-radius-full);\n  padding: 0;\n  background: var(--sp-rw-bg-accent);\n  box-shadow: none;\n  color: var(--sp-rw-fg-on-accent);\n  outline: 2px solid var(--sp-rw-surface-color, var(--sp-rw-bg-base));\n}\n\n:root .sp-base-badge[data-profile=\"verified\"] > svg {\n  width: 60%;\n  height: 60%;\n  opacity: 1;\n  stroke-width: 3;\n}\n\n/* Status profile: a state in words after a coloured dot, with no fill and no\n * contour, for list rows and running text. The words carry the state in the\n * secondary text colour, at the badge's size and weight; the dot repeats it.\n * The dot grows with the size, 6 px at sm and md and 8 px at lg and xl, with\n * as much space before the words. It takes the tone of the variant: success,\n * warning, error or destructive, info or accent, and otherwise the grey of the\n * Avatar's offline dot. */\n:root .sp-base-badge[data-profile=\"status\"] {\n  --sp-badge-dot-size: var(--sp-rw-space-1-5);\n  --sp-badge-dot-color: var(--color-muted-foreground);\n  height: auto;\n  min-width: 0;\n  gap: var(--sp-badge-dot-size);\n  border: 0;\n  border-radius: 0;\n  padding: 0;\n  background: transparent;\n  box-shadow: none;\n  color: var(--sp-rw-fg-secondary);\n}\n\n:root\n  .sp-base-badge[data-profile=\"status\"]:where(\n    [data-size=\"lg\"],\n    [data-size=\"xl\"]\n  ) {\n  --sp-badge-dot-size: var(--sp-rw-space-2);\n}\n\n:root .sp-base-badge[data-profile=\"status\"][data-variant=\"success\"] {\n  --sp-badge-dot-color: var(--color-success);\n}\n\n:root .sp-base-badge[data-profile=\"status\"][data-variant=\"warning\"] {\n  --sp-badge-dot-color: var(--color-warning);\n}\n\n:root\n  .sp-base-badge[data-profile=\"status\"]:where(\n    [data-variant=\"error\"],\n    [data-variant=\"destructive\"]\n  ) {\n  --sp-badge-dot-color: var(--color-destructive);\n}\n\n:root\n  .sp-base-badge[data-profile=\"status\"]:where(\n    [data-variant=\"info\"],\n    [data-variant=\"accent\"]\n  ) {\n  --sp-badge-dot-color: var(--sp-rw-bg-accent);\n}\n\n/* The status dot, which the status profile renders before its words. */\n:root .sp-badge-dot {\n  width: var(--sp-badge-dot-size, var(--sp-rw-space-1-5));\n  height: var(--sp-badge-dot-size, var(--sp-rw-space-1-5));\n  flex-shrink: 0;\n  border-radius: var(--sp-rw-radius-full);\n  background: var(--sp-badge-dot-color, var(--color-muted-foreground));\n}\n\n/* One surface contract for every card-like surface: no border of its own, the\n * card colour, and the one edge of the surface tokens, so a change to it\n * reaches all of them (rulebook B7). A Frame panel is a card surface on the\n * mid-tone frame, not the page colour (thin contours need a slightly darker\n * surround). Without this it kept the generic 1px border, whose 40% hairline\n * colour is meant for 0.5px, and two soft shadows: a line about five times\n * heavier than a Card's, doubled along the bottom edge. Radius stays with the\n * component: a panel's is concentric with its frame. */\n:root :where(.sp-card),\n:root :where(.sp-card-frame),\n:root :where(.sp-frame-panel) {\n  border: 0;\n  --sp-rw-surface-color: var(--sp-rw-bg-secondary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-surface-xs-shadow);\n  color: var(--sp-rw-fg-primary);\n}\n\n:root :where(.sp-card),\n:root :where(.sp-card-frame) {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n:root :where(.sp-card)::before,\n:root :where(.sp-card-frame)::before,\n:root :where(.sp-frame-panel)::before {\n  background: transparent;\n  box-shadow: none;\n}\n\n:root .sp-card[data-variant=\"message\"] {\n  border: var(--sp-rw-border-default) solid var(--sp-rw-border-translucent);\n  border-start-start-radius: var(--sp-rw-radius-xs);\n  box-shadow: none;\n}\n\n/* A failed item keeps the raised surface and adds the destructive contour of an\n * invalid field and the faint tint of an error Alert. */\n:root .sp-card[data-variant=\"error\"] {\n  --sp-rw-surface-color: color-mix(\n    in oklab,\n    var(--sp-rw-bg-secondary),\n    var(--sp-rw-bg-destructive) 4%\n  );\n  box-shadow:\n    0 0 0 var(--sp-rw-border-default) var(--sp-rw-border-destructive),\n    var(--sp-rw-surface-xs-shadow);\n}\n\n/* An inset Card sits inside another surface instead of floating on it: page\n * tone, pressed in, no elevation. */\n:root .sp-card[data-variant=\"inset\"] {\n  --sp-rw-surface-color: var(--sp-rw-bg-primary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: var(--sp-rw-surface-inset-shadow);\n}\n\n/* An elevated Card lifts off a surface of the same colour: the soft shadow\n * also reads above the top edge. */\n:root .sp-card[data-variant=\"elevated\"] {\n  box-shadow: var(--sp-rw-surface-elevated-shadow);\n}\n\n:root .sp-card[data-variant=\"subtle\"] {\n  border-radius: calc(var(--sp-rw-radius-surface) * 2);\n  --sp-rw-surface-color: var(--sp-rw-bg-primary);\n  background: var(--sp-rw-surface-color);\n  box-shadow: none;\n}\n\n/* A subtle card that holds only its panel, a well, insets it by 24 px. Beside a\n * header or footer the panel takes the inset and the collapses of every other\n * card, so its text starts with theirs (rulebook P3). */\n:root\n  .sp-card[data-variant=\"subtle\"]:not(\n    :has(> .sp-card-header, > .sp-card-footer)\n  )\n  > .sp-card-panel {\n  padding: var(--sp-rw-space-6);\n}\n\n:root .sp-card-frame {\n  overflow: hidden;\n  box-shadow: var(--sp-rw-surface-sm-shadow);\n}\n\n/* A Card joined into a CardFrame runs edge to edge: the frame draws the outer\n * edge and clips the Card at it, so the Card keeps square corners. Rounded ones\n * curled the hairline along its top into the frame's edge, a second line right\n * beside the first. */\n:root .sp-card-frame > .sp-card {\n  border-radius: 0;\n}\n\n/* The inset of a card's parts: 20 px, or 16 px in a compact card (the Card's\n * density, by default its provider's). Three tokens take the place of the\n * density's inset. The kit only reads them, so a theme sets them on :root after\n * the base import, and one card in its className:\n * - --sp-rw-card-inset, the shared value;\n * - --sp-rw-card-inset-block, the space above and below, and\n *   --sp-rw-card-inset-inline, the sides; each is the shared value unless set.\n * The inset pads the header, the panel and the footer, the sides of the bands\n * and the space above and below a divided header, and an action beside a title\n * alone reaches into it. A CardFrame sets --card-inset-inline on the cards\n * joined into it, so their text starts with the frame header's; every Card\n * resets it (card.tsx), so a card nested in a joined card keeps its own sides.\n * Each part works out its two insets once, for the rules below. */\n:root :where(.sp-base-card) {\n  --card-density-inset: var(--sp-rw-space-5);\n}\n\n:root .sp-base-card[data-density=\"compact\"] {\n  --card-density-inset: var(--sp-rw-space-4);\n}\n\n:root .sp-card-header,\n:root .sp-card-panel,\n:root .sp-card-footer,\n:root .sp-card > .sp-motion-reveal > .sp-alert[data-layout=\"band\"] {\n  --card-part-inset-block: var(\n    --sp-rw-card-inset-block,\n    var(--sp-rw-card-inset, var(--card-density-inset, var(--sp-rw-space-5)))\n  );\n  --card-part-inset-inline: var(\n    --card-inset-inline,\n    var(\n      --sp-rw-card-inset-inline,\n      var(--sp-rw-card-inset, var(--card-density-inset, var(--sp-rw-space-5)))\n    )\n  );\n}\n\n:root .sp-card-header,\n:root .sp-card-panel {\n  padding: var(--card-part-inset-block) var(--card-part-inset-inline);\n}\n\n:root .sp-card-header {\n  gap: var(--sp-rw-space-1-5);\n}\n\n/* A divided header (border-b) keeps the panel's own padding below its line. */\n:root .sp-card-header:not(.border-b) + .sp-card-panel {\n  padding-top: 0;\n}\n\n:root .sp-card:has(> .sp-card-panel) > .sp-card-header {\n  padding-bottom: var(--sp-rw-space-3);\n}\n\n/* A divided header, above a Separator or with its own bottom border, is a band\n * of its own: as much space below its text as above it, the card inset, where\n * a header before a panel otherwise keeps 12 px (rulebook S4b). The tinted\n * band keeps its own 12 px above and below. */\n:root .sp-card > .sp-card-header.border-b,\n:root .sp-card > .sp-card-header:has(+ .sp-separator),\n:root\n  .sp-card\n  > .sp-card-header:has(+ .sp-motion-reveal > .sp-alert[data-layout=\"band\"]) {\n  padding-bottom: var(--card-part-inset-block);\n}\n\n/* A header's media (CardMedia) leads its text, 12 px before it: the header's\n * 6 px column gap and 6 px of its own. Its box is one title line high, so a\n * bare icon centres on the title's first line and an avatar taller than the\n * line starts at its top (rulebook I3, I5, I7). The icon is 20 px and takes\n * the accent, as an icon beside a Metric Card heading does. A neutral media\n * (tone=\"neutral\") draws it in the secondary text colour instead, for a module\n * icon that leaves colour to states. */\n:root .sp-card-media {\n  min-height: var(--sp-rw-line-27);\n  align-items: center;\n  justify-content: center;\n  margin-inline-end: var(--sp-rw-space-1-5);\n}\n\n:root .sp-card-media > svg {\n  color: var(--sp-rw-fg-accent);\n}\n\n:root .sp-card-media[data-tone=\"neutral\"] > svg {\n  color: var(--sp-rw-fg-secondary);\n}\n\n:root .sp-card-media > svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n}\n\n/* Context beside the title (CardTitleMeta) shares the title's row and first\n * baseline, 8 px after it, and moves below it when the row is full. It takes\n * the description's text (below) at the regular weight. */\n:root .sp-card-title:has(> .sp-card-title-meta) {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: baseline;\n  column-gap: var(--sp-rw-space-2);\n}\n\n:root .sp-card-title-meta {\n  font-weight: var(--sp-rw-font-weight-default);\n}\n\n/* The part before the footer gives up its bottom padding, so the footer's\n * 16 px separate it from the actions: after a panel and after a header alone\n * (the tinted band keeps its own band). A divided part keeps its padding. */\n:root .sp-card > .sp-card-panel:has(+ .sp-card-footer:not(.border-t)),\n:root\n  .sp-card:not([data-variant=\"header\"])\n  > .sp-card-header:not(.border-b):has(+ .sp-card-footer:not(.border-t)) {\n  padding-bottom: 0;\n}\n\n:root .sp-card-footer {\n  padding: var(--sp-rw-space-4) var(--card-part-inset-inline)\n    var(--card-part-inset-block);\n}\n\n/* A footer divided off by a separator or its own top border is a band of its\n * own, with equal space above and below its actions (rulebook S4). */\n:root .sp-card > .sp-separator + .sp-card-footer,\n:root .sp-card > .sp-card-footer.border-t {\n  padding-bottom: var(--sp-rw-space-4);\n}\n\n:root .sp-card[data-variant=\"header\"] > .sp-card-header {\n  border-start-start-radius: inherit;\n  border-start-end-radius: inherit;\n  border-bottom: var(--sp-rw-border-default) solid\n    var(--sp-rw-border-translucent);\n  background: var(--sp-rw-bg-primary);\n  padding: var(--sp-rw-space-3) var(--card-part-inset-inline);\n}\n\n/* The rows of a header (rulebook S2, S3, I5, I7). card.tsx gives a media\n * (CardMedia) a column before the text and an action (CardAction) one after\n * it; the header's other children are its text parts and share one column. A\n * header holds two text parts, such as a title and a description, when a text\n * part follows another. The rules below test it with one condition:\n *\n *   :has(\n *     > :not(.sp-card-action):not(.sp-card-media)\n *       ~ :not(.sp-card-action):not(.sp-card-media)\n *   )\n *\n * - With two text parts, the media and the action span both rows from the\n *   top. The Component base block starts the parts at the top of their row\n *   and centres the media and the action on theirs.\n * - With one text part, such as a title alone, the header has no second row.\n *   A media or an action centres the parts on each other. The empty grid row\n *   keeps no gap: its 6 px made the band bottom-heavy, and in a default card\n *   it added 6 px between the title and the content (the title keeps its\n *   16 px, only the empty row goes; rulebook S2). An action keeps the header\n *   as tall as one without an action (Lukas, 15 October 2026): it reaches up\n *   to 12 px into the padding above and below, the band's padding and the\n *   space below a header before its panel, or less with a smaller block\n *   inset. So a link or a Button up to xl beside the 27 px line takes no\n *   height of its own; only what the 12 px cannot take, such as an xl Button\n *   beside the band's 20 px line, adds to the row, and the action never leaves\n *   the padding.\n * card.tsx sets none of these properties (rulebook P6): the base block, the\n * header's gap above and these rules own the header's align-items and\n * row-gap and the media's and the action's align-self, grid-row-end and\n * margin-block. */\n:root\n  .sp-card-header:has(\n    > :not(.sp-card-action):not(.sp-card-media)\n      ~ :not(.sp-card-action):not(.sp-card-media)\n  )\n  > .sp-card-media,\n:root\n  .sp-card-header:has(\n    > :not(.sp-card-action):not(.sp-card-media)\n      ~ :not(.sp-card-action):not(.sp-card-media)\n  )\n  > .sp-card-action {\n  grid-row-end: 3;\n  align-self: flex-start;\n}\n\n:root\n  .sp-card-header:has(> .sp-card-action, > .sp-card-media):not(\n    :has(\n      > :not(.sp-card-action):not(.sp-card-media)\n        ~ :not(.sp-card-action):not(.sp-card-media)\n    )\n  ) {\n  align-items: center;\n}\n\n:root\n  .sp-card\n  > .sp-card-header:not(\n    :has(\n      > :not(.sp-card-action):not(.sp-card-media)\n        ~ :not(.sp-card-action):not(.sp-card-media)\n    )\n  ) {\n  row-gap: 0;\n}\n\n:root\n  .sp-card-header:not(\n    :has(\n      > :not(.sp-card-action):not(.sp-card-media)\n        ~ :not(.sp-card-action):not(.sp-card-media)\n    )\n  )\n  > .sp-card-action {\n  margin-block: calc(\n    min(var(--sp-rw-space-3), var(--card-part-inset-block)) * -1\n  );\n}\n\n:root .sp-card[data-variant=\"header\"] > .sp-card-header + .sp-card-panel {\n  padding-top: var(--card-part-inset-block);\n}\n\n/* In the band the media follows the compact title: its box is the 20 px title\n * line and a bare icon is 16 px. */\n:root .sp-card[data-variant=\"header\"] > .sp-card-header > .sp-card-media {\n  min-height: var(--sp-rw-line-20);\n}\n\n:root\n  .sp-card[data-variant=\"header\"]\n  > .sp-card-header\n  > .sp-card-media\n  > svg:not([class*=\"size-\"]) {\n  width: var(--sp-rw-control-icon);\n  height: var(--sp-rw-control-icon);\n}\n\n/* The tinted band is a compact header: its title is 14 px in the strong\n * weight (--sp-rw-font-weight-strong, 600). */\n:root .sp-card[data-variant=\"header\"] > .sp-card-header > .sp-card-title {\n  font-size: var(--sp-rw-font-14);\n  font-weight: var(--sp-rw-font-weight-strong);\n  line-height: var(--sp-rw-line-20);\n}\n\n/* A Fieldset's description explains a group, as a Card's does, and takes the\n * same text. So does the context beside a card's title. */\n:root .sp-card-description,\n:root .sp-card-title-meta,\n:root .sp-fieldset-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* The public Button select-trigger profile owns the field\n * chassis. Select owns only value/caret placement and popup/collection anatomy. */\n:root [data-chassis][data-profile=\"select-trigger\"] {\n  --sp-rw-select-rim: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(0% 0 0 / 16%);\n  --sp-rw-select-height: var(--sp-rw-control-md);\n  --sp-rw-select-value-start: var(--sp-rw-space-3);\n  --sp-rw-select-value-end: var(--sp-rw-space-7);\n  --sp-rw-select-leading-gap: var(--sp-rw-space-2);\n  --sp-rw-select-caret-end: var(--sp-rw-space-2);\n\n  isolation: isolate;\n  height: var(--sp-rw-select-height);\n  min-height: var(--sp-rw-select-height);\n  gap: 0;\n  border: 0;\n  border-radius: var(--sp-rw-radius-control);\n  padding: 0;\n  background: var(--sp-rw-bg-translucent);\n  box-shadow:\n    var(--sp-rw-control-micro), var(--sp-rw-select-rim),\n    var(--sp-rw-control-spotlights), var(--sp-rw-control-depth);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-21);\n  transition: none;\n}\n\n:root [data-chassis][data-profile=\"select-trigger\"][data-size=\"sm\"] {\n  --sp-rw-select-height: var(--sp-rw-control-sm);\n  --sp-rw-select-value-start: var(--sp-rw-space-2-5);\n  --sp-rw-select-value-end: 26px;\n  --sp-rw-select-leading-gap: var(--sp-rw-space-1-5);\n  --sp-rw-select-caret-end: var(--sp-rw-space-1-5);\n\n  height: var(--sp-rw-select-height);\n  min-height: var(--sp-rw-select-height);\n}\n\n:root [data-chassis][data-profile=\"select-trigger\"][data-size=\"lg\"] {\n  --sp-rw-select-height: var(--sp-rw-control-lg);\n\n  height: var(--sp-rw-select-height);\n  min-height: var(--sp-rw-select-height);\n}\n\n:root [data-chassis][data-profile=\"select-trigger\"][data-size=\"xl\"] {\n  --sp-rw-select-height: var(--sp-rw-control-xl);\n  --sp-rw-select-value-start: var(--sp-rw-space-4);\n  --sp-rw-select-value-end: var(--sp-rw-space-8);\n  --sp-rw-select-caret-end: var(--sp-rw-space-3);\n\n  height: var(--sp-rw-select-height);\n  min-height: var(--sp-rw-select-height);\n  font-size: var(--sp-rw-font-16);\n  line-height: var(--sp-rw-line-24);\n}\n\n/* The xl field radius, as Input, InputGroup, Number Field and OTP Field take\n * it. The size sits in :where() so a Group's joined corners still win. */\n:root [data-chassis][data-profile=\"select-trigger\"]:where([data-size=\"xl\"]) {\n  border-radius: var(--sp-rw-radius-surface);\n}\n\n/*\n * This profile retains the accepted single-root drawing while Button remains\n * the stable visual owner for default and custom-rendered Select triggers.\n */\n:root\n  [data-chassis][data-profile=\"select-trigger\"]\n  > .sp-button-chassis-interaction,\n:root [data-chassis][data-profile=\"select-trigger\"] > .sp-button-chassis-rim {\n  display: none;\n}\n\n:root:where(.dark) [data-chassis][data-profile=\"select-trigger\"],\n:root:where([data-theme=\"dark\"]) [data-chassis][data-profile=\"select-trigger\"],\n:root :where(.dark) [data-chassis][data-profile=\"select-trigger\"],\n:root\n  :where([data-theme=\"dark\"])\n  [data-chassis][data-profile=\"select-trigger\"] {\n  --sp-rw-select-rim: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(100% 0 0 / 16%);\n}\n\n:root [data-chassis][data-profile=\"select-trigger\"]::before {\n  position: absolute;\n  z-index: -1;\n  inset: 0;\n  border-radius: inherit;\n  background: var(--sp-rw-control-dome);\n  content: \"\";\n  pointer-events: none;\n}\n\n:root\n  [data-chassis][data-profile=\"select-trigger\"]:hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background:\n    linear-gradient(var(--sp-rw-glow-hover), var(--sp-rw-glow-hover)),\n    var(--sp-rw-bg-translucent);\n}\n\n:root\n  [data-chassis][data-profile=\"select-trigger\"]:not(\n    [data-profile-mode=\"ghost\"]\n  ):active:not([data-disabled], :disabled, [aria-disabled=\"true\"]),\n:root\n  [data-chassis][data-profile=\"select-trigger\"]:not(\n    [data-profile-mode=\"ghost\"]\n  )[data-popup-open]:not([data-disabled], :disabled, [aria-disabled=\"true\"]) {\n  background:\n    linear-gradient(var(--sp-rw-glow-pressed), var(--sp-rw-glow-pressed)),\n    var(--sp-rw-bg-translucent);\n  box-shadow:\n    var(--sp-rw-control-micro), var(--sp-rw-select-rim),\n    var(--sp-rw-control-spotlights), var(--sp-rw-control-depth) !important;\n}\n\n/* The ghost mode is the Select's text-with-caret form, for a value that sits in\n * a row or a toolbar, such as a role or a sort order. It draws no fill, rim,\n * dome or depth at rest and takes the shared hover and pressed levels while it\n * is used. Value and caret placement, the size scale, focus and the invalid\n * state stay the field's own. */\n:root [data-chassis][data-profile=\"select-trigger\"][data-profile-mode=\"ghost\"] {\n  background: transparent;\n  box-shadow: none;\n}\n\n:root\n  [data-chassis][data-profile=\"select-trigger\"][data-profile-mode=\"ghost\"]::before {\n  background: none;\n}\n\n:root\n  [data-chassis][data-profile=\"select-trigger\"][data-profile-mode=\"ghost\"]:hover:not(\n    [data-disabled],\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root\n  [data-chassis][data-profile=\"select-trigger\"][data-profile-mode=\"ghost\"]:is(\n    :active,\n    [data-popup-open]\n  ):not([data-disabled], :disabled, [aria-disabled=\"true\"]) {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root [data-chassis][data-profile=\"select-trigger\"][aria-invalid=\"true\"] {\n  box-shadow: var(--sp-rw-field-invalid-shadow);\n}\n\n:root .sp-select-value {\n  padding-right: var(--sp-rw-select-value-end);\n  padding-left: var(--sp-rw-select-value-start);\n}\n\n:root [data-select-trigger] svg {\n  margin-inline: 0;\n}\n\n:root [data-select-trigger] > .sp-button-content > svg:first-child {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  margin-left: var(--sp-rw-select-value-start);\n}\n\n:root\n  [data-select-trigger]\n  > .sp-button-content\n  > svg:first-child\n  + .sp-select-value {\n  padding-left: var(--sp-rw-select-leading-gap);\n}\n\n:root .sp-select-icon {\n  position: absolute;\n  top: 50%;\n  right: var(--sp-rw-select-caret-end);\n  display: inline-flex;\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  align-items: center;\n  justify-content: center;\n  color: var(--sp-rw-fg-tertiary);\n  transform: translateY(-50%);\n  pointer-events: none;\n}\n\n:root .sp-select-icon svg {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  margin: 0;\n  opacity: 1;\n  transition: transform var(--motion-duration-fast) var(--motion-ease-state);\n}\n\n:root [data-select-trigger][data-popup-open] .sp-select-icon svg {\n  transform: rotate(180deg);\n}\n\n:root [data-select-trigger][data-popup-open] .sp-select-icon {\n  color: var(--sp-rw-fg-primary);\n}\n\n/* The panel of a Select is the surface that holds its list (sp-select-surface\n * in select.tsx). Base UI renders the scroll arrows beside it, as divs of the\n * popup too, so the rule names the surface alone: a rule for every div of the\n * popup gave each arrow the panel's fill, radius and shadow (FB-8). */\n:root .sp-select-surface {\n  overflow: hidden;\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  padding: 0;\n  background: var(--sp-rw-bg-secondary);\n  box-shadow: var(--sp-rw-floating-md-shadow);\n}\n\n:root .sp-select-surface::before {\n  box-shadow: none;\n}\n\n/* A scroll arrow is a 24 px strip along the panel's top or bottom edge, inside\n * it: the chevron sits on the panel's colour, which fades into the list over\n * the next 24 px. The fade is the surface's colour, so no seam shows, and it\n * takes the surface's radius at its edge, so the panel's corners stay round. */\n:root .sp-select-scroll-up-arrow::before,\n:root .sp-select-scroll-down-arrow::before {\n  position: absolute;\n  inset-inline: 0;\n  height: 200%;\n  content: \"\";\n  pointer-events: none;\n}\n\n:root .sp-select-scroll-up-arrow::before {\n  top: 0;\n  border-start-start-radius: var(--sp-rw-radius-surface);\n  border-start-end-radius: var(--sp-rw-radius-surface);\n  background: linear-gradient(var(--sp-rw-bg-secondary) 50%, transparent);\n}\n\n:root .sp-select-scroll-down-arrow::before {\n  bottom: 0;\n  border-end-start-radius: var(--sp-rw-radius-surface);\n  border-end-end-radius: var(--sp-rw-radius-surface);\n  background: linear-gradient(\n    to top,\n    var(--sp-rw-bg-secondary) 50%,\n    transparent\n  );\n}\n\n:root .sp-select-list {\n  padding: var(--sp-rw-space-1);\n}\n\n:root .sp-select-item {\n  --sp-rw-select-item-height: var(--anchor-height, var(--sp-rw-control-md));\n\n  min-height: var(--sp-rw-select-item-height);\n  /* The check leads, in the 16 px slot and 8 px gap of a Combobox item, and\n   * the row ends 16 px after its label, as a Combobox item does (I8). */\n  grid-template-columns: var(--sp-rw-space-4) minmax(0, 1fr);\n  gap: var(--sp-rw-space-2);\n  border-radius: var(--sp-rw-radius-control);\n  padding-block: clamp(\n    3px,\n    calc(var(--sp-rw-select-item-height) - var(--sp-rw-control-sm)),\n    var(--sp-rw-space-1)\n  );\n  padding-inline: var(--sp-rw-space-2) var(--sp-rw-space-4);\n  color: var(--sp-rw-fg-primary);\n  font-size: clamp(\n    var(--sp-rw-font-14),\n    calc(var(--sp-rw-select-item-height) - var(--sp-rw-control-md)),\n    var(--sp-rw-font-16)\n  );\n  font-weight: 400;\n  line-height: clamp(\n    var(--sp-rw-line-21),\n    calc(var(--sp-rw-select-item-height) - var(--sp-rw-space-6)),\n    var(--sp-rw-line-24)\n  );\n}\n\n:root .sp-select-item[data-highlighted] {\n  background: var(--sp-rw-glow-hover);\n}\n\n:root .sp-select-item:active {\n  background: var(--sp-rw-glow-pressed);\n}\n\n:root .sp-select-item[data-selected] {\n  font-weight: 450;\n}\n\n:root .sp-select-item svg {\n  width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n}\n\n/* Settings list */\n:root .sp-settings-list {\n  gap: var(--sp-rw-space-4);\n}\n\n:root .sp-settings-list-header {\n  padding-inline: var(--sp-rw-space-6);\n}\n\n:root .sp-settings-list-title {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-16);\n  font-weight: var(--sp-rw-font-weight-label);\n  line-height: var(--sp-rw-line-24);\n}\n\n:root .sp-settings-list-description {\n  color: var(--sp-rw-fg-tertiary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-settings-list-item {\n  position: relative;\n  border-bottom-width: 0;\n  padding-block: var(--sp-rw-space-5);\n}\n\n:root .sp-settings-list-item[data-layout=\"responsive\"] {\n  gap: var(--sp-rw-space-3);\n}\n\n:root .sp-settings-list-item[data-layout=\"inline\"] {\n  gap: var(--sp-rw-space-6);\n}\n\n:root .sp-settings-list-item:first-child {\n  padding-top: 0;\n}\n\n:root .sp-settings-list-item:last-child {\n  padding-bottom: 0;\n}\n\n:root .sp-settings-list-item-copy {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root .sp-settings-list-item-description {\n  color: var(--sp-rw-fg-tertiary);\n}\n\n/* The list is a size container (settings-list.tsx): the header inset and the\n * row gaps follow the list's own width, as its rows and card padding do. */\n@container settings-list (width < 36rem) {\n  :root .sp-settings-list-header {\n    padding-inline: 0;\n  }\n}\n\n@container settings-list (width >= 36rem) {\n  :root .sp-settings-list-item[data-layout=\"responsive\"],\n  :root .sp-settings-list-item[data-layout=\"inline\"] {\n    gap: var(--sp-rw-space-12);\n  }\n}\n\n:root .sp-alert {\n  gap: var(--sp-rw-space-0-5) var(--sp-rw-space-2);\n  border: 0;\n  border-radius: var(--sp-rw-radius-surface);\n  padding: var(--sp-rw-space-3) calc(var(--sp-rw-space) * 3.5);\n  background: var(--sp-rw-bg-secondary);\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-border-translucent);\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n/* The columns beside the text column of the kit base: a 16 px icon column\n * before it when an icon leads, and from 640 px an action column after it,\n * only beside a title or a description, so no empty track stays (S11). A\n * banner or a band has columns of its own (Alert layouts, below). */\n:root .sp-alert:not([data-layout]):has(> svg) {\n  grid-template-columns: var(--sp-rw-space-4) minmax(0, 1fr);\n}\n\n@media (width >= 40rem) {\n  :root\n    .sp-alert:not([data-layout]):has(.sp-alert-action):has(\n      .sp-alert-title,\n      .sp-alert-description\n    ) {\n    grid-template-columns: minmax(0, 1fr) auto;\n  }\n\n  :root\n    .sp-alert:not([data-layout]):has(> svg):has(.sp-alert-action):has(\n      .sp-alert-title,\n      .sp-alert-description\n    ) {\n    grid-template-columns: var(--sp-rw-space-4) minmax(0, 1fr) auto;\n  }\n}\n\n/* Each status rim is its status colour at 32%: the accent for info, and the\n * positive, attention and destructive fills, so an override of a status\n * token reaches its rim. */\n:root .sp-alert[data-variant=\"info\"] {\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(from var(--sp-rw-bg-accent) l c h / 32%);\n}\n\n:root .sp-alert[data-variant=\"success\"] {\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(from var(--sp-rw-bg-positive) l c h / 32%);\n}\n\n:root .sp-alert[data-variant=\"warning\"] {\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(from var(--sp-rw-bg-attention) l c h / 32%);\n}\n\n:root .sp-alert[data-variant=\"error\"] {\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    oklch(from var(--sp-rw-bg-destructive) l c h / 32%);\n}\n\n:root .sp-alert-title {\n  font-weight: var(--sp-rw-font-weight-label);\n}\n\n:root .sp-alert-description {\n  color: var(--sp-rw-fg-tertiary);\n}\n\n/* Alert layouts (Lukas, 16 October 2026; coss origin's banner for the\n * structure, Salesforce Lightning's alert for the link in the text). A banner\n * runs across the top of a page, above its header; a band runs across the top\n * of a Card, under its header. Both take their tone's tint over the full\n * width, a hairline in the tone's rim colour below it, and no radius. The\n * icon, the text, an action and the close control stand on one row while the\n * Alert's own width allows it; below 32rem of that width the icon and the\n * close control sit on the title's line and an action goes under the text.\n * Columns: icon, text, action, close. The spacing between them is the items'\n * own margin, so a column without an item takes no space (rulebook S11). */\n:root .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"]) {\n  container-type: inline-size;\n  grid-template-columns: auto minmax(0, 1fr) auto auto;\n  gap: 0;\n  border-radius: 0;\n  padding-block: calc(var(--sp-rw-space) * 3.5);\n  background: var(--sp-rw-alert-tint);\n  box-shadow: inset 0 calc(var(--sp-rw-border-default) * -1) 0\n    var(--sp-rw-alert-rim);\n  --sp-rw-alert-tint: var(--sp-rw-bg-secondary);\n  --sp-rw-alert-rim: var(--sp-rw-border-translucent);\n}\n\n:root .sp-alert[data-layout=\"banner\"] {\n  padding-inline: calc(var(--sp-rw-space) * 7);\n}\n\n/* A band starts where the card's text starts. */\n:root .sp-alert[data-layout=\"band\"] {\n  padding-inline: var(--card-part-inset-inline, var(--sp-rw-space-5))\n    var(--sp-rw-space-3);\n}\n\n/* Each tone tints with its status fill: 8 % for an error, 12 % for a warning\n * (whose attention colour is lighter), 12 % and 16 % in Dark, and draws its\n * hairline in the rim colour of the inline Alert. */\n:root\n  .sp-alert:is(\n    [data-layout=\"banner\"],\n    [data-layout=\"band\"]\n  )[data-variant=\"error\"] {\n  --sp-rw-alert-tint: light-dark(\n    oklch(from var(--sp-rw-bg-destructive) l c h / 8%),\n    oklch(from var(--sp-rw-bg-destructive) l c h / 12%)\n  );\n  --sp-rw-alert-rim: oklch(from var(--sp-rw-bg-destructive) l c h / 32%);\n}\n\n:root\n  .sp-alert:is(\n    [data-layout=\"banner\"],\n    [data-layout=\"band\"]\n  )[data-variant=\"warning\"] {\n  --sp-rw-alert-tint: light-dark(\n    oklch(from var(--sp-rw-bg-attention) l c h / 12%),\n    oklch(from var(--sp-rw-bg-attention) l c h / 16%)\n  );\n  --sp-rw-alert-rim: oklch(from var(--sp-rw-bg-attention) l c h / 32%);\n}\n\n:root\n  .sp-alert:is(\n    [data-layout=\"banner\"],\n    [data-layout=\"band\"]\n  )[data-variant=\"info\"] {\n  --sp-rw-alert-tint: light-dark(\n    oklch(from var(--sp-rw-bg-accent) l c h / 8%),\n    oklch(from var(--sp-rw-bg-accent) l c h / 12%)\n  );\n  --sp-rw-alert-rim: oklch(from var(--sp-rw-bg-accent) l c h / 32%);\n}\n\n:root\n  .sp-alert:is(\n    [data-layout=\"banner\"],\n    [data-layout=\"band\"]\n  )[data-variant=\"success\"] {\n  --sp-rw-alert-tint: light-dark(\n    oklch(from var(--sp-rw-bg-positive) l c h / 8%),\n    oklch(from var(--sp-rw-bg-positive) l c h / 12%)\n  );\n  --sp-rw-alert-rim: oklch(from var(--sp-rw-bg-positive) l c h / 32%);\n}\n\n/* The icon: 20 px on a banner, 16 px on a band, centred on the text. */\n:root .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"]) > svg {\n  grid-column: 1;\n  grid-row: 1 / span 2;\n  align-self: center;\n  margin-inline-end: var(--sp-rw-space-3);\n}\n\n:root .sp-alert[data-layout=\"banner\"] > svg {\n  width: var(--sp-rw-space-5);\n  height: var(--sp-rw-space-5);\n  margin-inline-end: var(--sp-rw-space-4);\n}\n\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-title,\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-description {\n  grid-column: 2;\n}\n\n/* A short title breaks into even lines, in the primary text colour for every\n * tone: only the icon carries the tone. The sentence below reads in the\n * secondary text colour, which keeps its contrast on the tint. */\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-title {\n  text-wrap: balance;\n}\n\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-description {\n  color: var(--sp-rw-fg-secondary);\n}\n\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-title\n  + .sp-alert-description {\n  margin-top: var(--sp-rw-space-0-5);\n}\n\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-action {\n  grid-column: 3;\n  grid-row: 1 / span 2;\n  align-self: center;\n  margin-inline-start: var(--sp-rw-space-4);\n}\n\n:root\n  .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n  > .sp-alert-close {\n  grid-column: 4;\n  grid-row: 1 / span 2;\n  align-self: center;\n  margin-inline-start: var(--sp-rw-space-3);\n}\n\n@container (width < 32rem) {\n  :root .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"]) > svg,\n  :root\n    .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n    > .sp-alert-close {\n    grid-row: 1;\n    align-self: start;\n  }\n\n  /* The 24 px close control centres on the title's first line. */\n  :root\n    .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n    > .sp-alert-close {\n    margin-block: calc((1lh - var(--sp-rw-space-6)) / 2);\n  }\n\n  :root\n    .sp-alert:is([data-layout=\"banner\"], [data-layout=\"band\"])\n    > .sp-alert-action {\n    grid-column: 2;\n    grid-row: 3;\n    justify-self: start;\n    margin-inline-start: 0;\n    margin-top: var(--sp-rw-space-3);\n  }\n}\n\n/* The shimmer is a highlight band across the muted fill; animate-skeleton\n * slides it by its position. */\n:root .sp-skeleton {\n  --radius-sm: var(--sp-rw-radius-xs);\n  background-color: var(--sp-rw-bg-muted);\n  background-image: linear-gradient(\n    120deg,\n    transparent 40%,\n    var(--skeleton-highlight),\n    transparent 60%\n  );\n  background-position: 0 0;\n  background-size: 200% 100%;\n  --skeleton-highlight: oklch(100% 0 0 / 64%);\n}\n\n:root:where(.dark) .sp-skeleton,\n:root:where([data-theme=\"dark\"]) .sp-skeleton,\n:root :where(.dark) .sp-skeleton,\n:root :where([data-theme=\"dark\"]) .sp-skeleton {\n  --skeleton-highlight: oklch(100% 0 0 / 4%);\n}\n\n:root .sp-spinner {\n  color: currentColor;\n}\n\n:root .sp-spinner-default {\n  -webkit-mask-image: conic-gradient(\n    from 90deg,\n    black 0deg 135deg,\n    transparent 315deg\n  );\n  mask-image: conic-gradient(from 90deg, black 0deg 135deg, transparent 315deg);\n}\n\n:root .sp-separator {\n  background: var(--sp-rw-border-translucent);\n}\n\n:root .sp-separator[data-orientation=\"horizontal\"] {\n  height: var(--sp-rw-border-default);\n}\n\n:root .sp-separator[data-orientation=\"vertical\"] {\n  width: var(--sp-rw-border-default);\n}\n\n:root .sp-otp-field > .sp-separator {\n  width: var(--sp-rw-space-3);\n  height: var(--sp-rw-space-0-5);\n  border-radius: var(--sp-rw-radius-full);\n}\n\n:root :where(.sp-menu-separator),\n:root :where(.sp-context-menu-separator),\n:root :where(.sp-drawer-menu-separator) {\n  width: auto;\n  height: var(--sp-rw-border-default);\n  margin-block: 4px;\n  margin-inline: -4px;\n  background: var(--sp-rw-border-translucent);\n}\n\n/* Progress and Meter share geometry but retain their distinct semantics. Their\n * track and fill colours are theme tokens (--sp-rw-progress-track and\n * --sp-rw-progress-fill, declared with the light and dark tokens at the top of\n * this file), so the dark theme never has to outrank a component rule. */\n/* The gap between the label row and the track comes from the shared size map\n * (surface.ts) for both; a Meter gap here once kept 8 px at every size. */\n:root :where(.sp-progress),\n:root :where(.sp-meter) {\n  color: var(--sp-rw-fg-primary);\n  font-size: var(--sp-rw-font-14);\n  line-height: var(--sp-rw-line-21);\n}\n\n:root :where(.sp-progress-label),\n:root :where(.sp-meter-label) {\n  min-width: 0;\n  color: var(--sp-rw-fg-primary);\n  font-weight: var(--sp-rw-font-weight-label);\n  overflow-wrap: anywhere;\n}\n\n:root :where(.sp-progress-value),\n:root :where(.sp-meter-value) {\n  color: var(--sp-rw-fg-tertiary);\n  font-variant-numeric: tabular-nums;\n}\n\n:root .sp-progress-track,\n:root .sp-meter-track {\n  border-radius: var(--sp-rw-radius-control);\n  background: var(--sp-rw-progress-track);\n  box-shadow: none;\n}\n\n:root :where(.sp-progress-indicator),\n:root :where(.sp-meter-indicator) {\n  border-radius: inherit;\n  background: var(--sp-rw-progress-fill);\n}\n\n/* The Meter fill moves with the kit's value motion, like a figure that changes\n * in place, and a change of tone fades on the same timing. Above the minimum it\n * keeps a 7 px minimum width, so a small value stays visible; at the minimum\n * the meter is empty and shows no fill. Progress is the owner of its own value\n * transition and may be empty, so its motion (progress.css) and minimum width\n * (progress.tsx) are not a shared rule. The fill takes the colour of the\n * meter's tone, or the progress fill without one. */\n:root :where(.sp-meter-indicator) {\n  min-width: 7px;\n  background: var(--sp-rw-meter-fill, var(--sp-rw-progress-fill));\n  transition-property: all;\n  transition-duration: var(--motion-duration-value);\n  transition-timing-function: var(--motion-ease-standard);\n}\n\n:root :where(.sp-meter[data-empty] .sp-meter-indicator) {\n  min-width: 0;\n}\n\n/* Reduced motion moves the fill at once (M5): the document mode and the\n * provider preset here, the system setting in the shared block below. */\n:is(html[data-motion-reduce=\"true\"], [data-sp-motion=\"reduced\"])\n  .sp-meter-indicator {\n  transition: none;\n}\n\n/* A toned Meter fills with the solid colour of a kit status family, the one the\n * Badge's error, warning and success variants tint with. The fill's value\n * transition runs on every property, so a new tone fades in with the new\n * value. */\n:root .sp-meter[data-tone=\"error\"] {\n  --sp-rw-meter-fill: var(--sp-rw-bg-destructive);\n}\n\n:root .sp-meter[data-tone=\"warning\"] {\n  --sp-rw-meter-fill: var(--sp-rw-bg-attention);\n}\n\n:root .sp-meter[data-tone=\"success\"] {\n  --sp-rw-meter-fill: var(--sp-rw-bg-positive);\n}\n\n/* The circle paints with the bar's progress tokens: the same track, and the\n * same fill, which Dark lifts so it reads on a dark surface. */\n:root .sp-progress-circle circle:first-child {\n  stroke: var(--sp-rw-progress-track);\n}\n\n:root .sp-progress-circle circle:last-child {\n  stroke: var(--sp-rw-progress-fill);\n}\n\n:root .sp-kbd {\n  min-width: var(--sp-rw-space-4);\n  height: var(--sp-rw-space-4);\n  gap: var(--sp-rw-space-0-5);\n  border-radius: var(--sp-rw-radius-xs);\n  padding-inline: var(--sp-rw-space-1);\n  background:\n    linear-gradient(var(--sp-rw-bg-muted), var(--sp-rw-bg-muted)),\n    linear-gradient(var(--sp-rw-bg-translucent), var(--sp-rw-bg-translucent));\n  box-shadow: inset 0 0 0 var(--sp-rw-border-default)\n    var(--sp-rw-border-translucent);\n  color: var(--sp-rw-fg-secondary);\n  font-size: var(--sp-rw-font-10);\n  font-weight: 400;\n  line-height: 1.5;\n}\n\n:root .sp-kbd-group {\n  gap: var(--sp-rw-space-0-5);\n  color: var(--sp-rw-fg-secondary);\n}\n\n/* Tooltip popup. A tone sets its fill, text and contour through\n * --sp-rw-tooltip-bg, --sp-rw-tooltip-fg, --sp-rw-tooltip-rim and\n * --sp-rw-tooltip-contour; without one it keeps the secondary surface.\n * It is at most 18rem (288 px) wide, within the 240 to 300 px that\n * established kits give their tooltips: a long text wraps into a compact\n * block of about 45 characters a line in the tooltip's 12 px, at every\n * control size, and an unbroken word breaks (rulebook K5). A max-w-* class\n * in className sets another width (P6). */\n:root .sp-tooltip-popup {\n  min-height: var(--sp-rw-control-xs);\n  max-width: calc(var(--sp-rw-space) * 72);\n  border-radius: var(--sp-rw-radius-sm);\n  background: var(--sp-rw-tooltip-bg, var(--sp-rw-bg-secondary));\n  box-shadow:\n    0 0 0 var(--sp-rw-border-default) transparent,\n    0 0 0 var(--sp-rw-border-default)\n      var(--sp-rw-tooltip-bg, var(--sp-rw-bg-secondary)),\n    0 0 0 calc(var(--sp-rw-border-default) * 2)\n      var(--sp-rw-tooltip-contour, var(--sp-rw-border-translucent)),\n    0 0 16px 4px oklch(0% 0 0 / 4%),\n    0 2px 1px oklch(0% 0 0 / 4%),\n    0 2px 1px -0.5px oklch(0% 0 0 / 4%),\n    0 4px 3px -1.5px oklch(0% 0 0 / 4%),\n    0 7px 6px -3px oklch(0% 0 0 / 4%),\n    0 13px 12px -6px oklch(0% 0 0 / 4%);\n  color: var(--sp-rw-tooltip-fg, var(--sp-rw-fg-primary));\n  font-size: var(--sp-rw-font-12);\n  font-weight: 400;\n  line-height: var(--sp-rw-line-16);\n  overflow-wrap: anywhere;\n}\n\n:root .sp-tooltip-popup::before {\n  box-shadow: none;\n}\n\n/* The error tone reports an error: the solid destructive fill and the\n * on-destructive text of the Badge's destructive variant, with no contour of\n * its own. */\n:root .sp-tooltip-popup[data-tone=\"error\"] {\n  --sp-rw-tooltip-bg: var(--sp-rw-bg-destructive);\n  --sp-rw-tooltip-fg: var(--sp-rw-fg-emphasis);\n  --sp-rw-tooltip-rim: transparent;\n  --sp-rw-tooltip-contour: transparent;\n}\n\n/* An arrow points from the popup at its anchor: a triangle in the popup's\n * fill, --sp-rw-space-2 wide and --sp-rw-space-1 deep, outside the edge that\n * faces the anchor. Base UI centres it on the anchor along that edge. */\n:root .sp-tooltip-arrow {\n  width: var(--sp-rw-space-2);\n  height: var(--sp-rw-space-1);\n  background: var(--sp-rw-tooltip-bg, var(--sp-rw-bg-secondary));\n}\n\n:root .sp-tooltip-arrow[data-side=\"top\"] {\n  bottom: calc(var(--sp-rw-space-1) * -1);\n  clip-path: polygon(0 0, 100% 0, 50% 100%);\n}\n\n:root .sp-tooltip-arrow[data-side=\"bottom\"] {\n  top: calc(var(--sp-rw-space-1) * -1);\n  clip-path: polygon(50% 0, 100% 100%, 0 100%);\n}\n\n/* The logical sides name the dir attribute beside :dir(rtl), which a build\n * for older browsers turns into a list of right-to-left languages, as\n * Tailwind's rtl variant does; left to right is every other page. */\n:root\n  .sp-tooltip-arrow:is(\n    [data-side=\"right\"],\n    [data-side=\"inline-end\"]:not(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *),\n    [data-side=\"inline-start\"]:is(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *)\n  ) {\n  left: calc(var(--sp-rw-space-1) * -1);\n  width: var(--sp-rw-space-1);\n  height: var(--sp-rw-space-2);\n  clip-path: polygon(100% 0, 100% 100%, 0 50%);\n}\n\n:root\n  .sp-tooltip-arrow:is(\n    [data-side=\"left\"],\n    [data-side=\"inline-start\"]:not(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *),\n    [data-side=\"inline-end\"]:is(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *)\n  ) {\n  right: calc(var(--sp-rw-space-1) * -1);\n  width: var(--sp-rw-space-1);\n  height: var(--sp-rw-space-2);\n  clip-path: polygon(0 0, 100% 50%, 0 100%);\n}\n\n/* The inline padding is the variable the viewport's content width subtracts\n * (above), so a text that changes while the tooltip is open, such as the\n * \"Copied\" of a CopyButton, keeps the line it was measured on. */\n:root .sp-tooltip-viewport {\n  --viewport-inline-padding: var(--sp-rw-space-1-5);\n  --viewport-edge-width: 0px;\n  padding: var(--sp-rw-space-1) var(--viewport-inline-padding);\n}\n\n:root:where(.dark) .sp-tooltip-popup,\n:root:where([data-theme=\"dark\"]) .sp-tooltip-popup,\n:root :where(.dark) .sp-tooltip-popup,\n:root :where([data-theme=\"dark\"]) .sp-tooltip-popup {\n  box-shadow:\n    0 0 0 var(--sp-rw-border-default)\n      var(--sp-rw-tooltip-rim, oklch(55% 0.002 107 / 36%)),\n    0 0 0 var(--sp-rw-border-default)\n      var(--sp-rw-tooltip-bg, var(--sp-rw-bg-secondary)),\n    0 0 0 calc(var(--sp-rw-border-default) * 2)\n      var(--sp-rw-tooltip-contour, oklch(0% 0 0 / 28%)),\n    0 0 16px 4px oklch(0% 0 0 / 12%),\n    0 2px 1px oklch(0% 0 0 / 12%),\n    0 2px 1px -0.5px oklch(0% 0 0 / 12%),\n    0 4px 3px -1.5px oklch(0% 0 0 / 12%),\n    0 7px 6px -3px oklch(0% 0 0 / 12%),\n    0 13px 12px -6px oklch(0% 0 0 / 12%);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root :where([data-chassis]),\n  :root :where(.sp-input-control),\n  :root :where(.sp-textarea-control),\n  :root :where(.sp-checkbox),\n  :root :where(.sp-radio),\n  :root :where(.sp-switch-thumb),\n  :root :where(.sp-base-toggle),\n  :root :where(.sp-progress-indicator),\n  :root :where(.sp-meter-indicator) {\n    transition-duration: 0.01ms;\n  }\n\n  :root :where(.sp-progress-indicator),\n  :root :where(.sp-meter-indicator) {\n    transition: none;\n  }\n\n  :root .sp-stepper-indicator::after {\n    transition: none;\n  }\n}\n\n/* Navigation links keep the shared Button focus, pressed and disabled contract.\n * Header navigation renders them as ghost Buttons with the navigation\n * treatment of the Sidebar rows above: secondary text at rest, a light fill\n * and the full foreground on hover, press and an open disclosure, and the\n * persistent fill with the full foreground for the current destination (the\n * only owner of aria-current). The profile only owns the label-to-icon gap. */\n:root [data-chassis][data-profile=\"navigation-link\"] {\n  gap: var(--sp-rw-space-1);\n}\n\n/* Text navigation rows (the mobile menu) keep their earlier treatment: rest is\n * the full foreground; hover and an open disclosure lighten the text only. The\n * current destination returns to the full foreground and adds the persistent\n * fill joined to the Sidebar's active-row rule above. */\n:root [data-chassis][data-profile=\"navigation-link\"][data-variant=\"text\"] {\n  --sp-rw-navigation-link-text-hover: color-mix(\n    in srgb,\n    var(--sp-rw-fg-primary) 80%,\n    transparent\n  );\n  --sp-rw-button-interaction: var(--sp-rw-control-interaction);\n  padding-inline: var(--sp-rw-space-1-5);\n  transition: color var(--motion-duration-fast) var(--motion-ease-soft-in-out);\n}\n\n:root\n  [data-chassis][data-profile=\"navigation-link\"][data-variant=\"text\"]:is(\n    :hover,\n    [data-popup-open]\n  ):not(:disabled, [aria-disabled=\"true\"], [aria-current=\"page\"]) {\n  --sp-rw-button-text: var(--sp-rw-navigation-link-text-hover);\n}\n\n/* Base UI also marks an open disclosure as pressed. Open is the light colour\n * change only, never a second opacity step on top of it. */\n:root\n  [data-chassis][data-profile=\"navigation-link\"][data-variant=\"text\"][data-popup-open]:not(\n    :disabled,\n    [aria-disabled=\"true\"]\n  ) {\n  opacity: 1;\n}\n\n/* ModalImageCrop: the crop frame and its four corner handles. Both draw in the\n * accent ink, which keeps its contrast on a bright picture and on the mask, in\n * light and dark. The frame also carries the mask over the rest of the picture.\n * A handle is a small square with a light fill; the Button around it supplies\n * the 24 px target, the focus outline and the states. */\n:root .sp-modal-image-crop-frame {\n  border: calc(var(--sp-rw-border-default) * 2) solid var(--sp-rw-fg-accent);\n  box-shadow: 0 0 0 999px var(--sp-rw-bg-scrim);\n}\n\n:root .sp-modal-image-crop-handle {\n  display: block;\n  width: var(--sp-rw-space-3);\n  height: var(--sp-rw-space-3);\n  border: calc(var(--sp-rw-border-default) * 2) solid var(--sp-rw-fg-accent);\n  border-radius: calc(var(--sp-rw-space-1) / 2);\n  background: var(--sp-primitive-color-white);\n}\n\n/* Glass surface. Components set it through `variant=\"glass\"`; geometry, states\n * and motion stay with the component, and this rule owns only the shared glass\n * look. It stays at the end of this file: the Popover and Tooltip surface rules\n * share its specificity, so the later rule wins. It sets no transition, so the\n * popup motion of motion.css stays in place. */\n:root .sp-glass-surface {\n  /* Text and icons on glass take the glass ink: the primary step at full\n   * strength, the secondary and tertiary steps as shares of it, instead of the\n   * fixed greys of opaque surfaces, which wash out on the film. */\n  --sp-rw-fg-primary: var(--sp-rw-glass-foreground);\n  --sp-rw-fg-secondary: color-mix(\n    in oklab,\n    var(--sp-rw-glass-foreground) 80%,\n    transparent\n  );\n  --sp-rw-fg-tertiary: color-mix(\n    in oklab,\n    var(--sp-rw-glass-foreground) 68%,\n    transparent\n  );\n  --sp-semantic-color-foreground: var(--sp-rw-glass-foreground);\n  --sp-semantic-color-card-foreground: var(--sp-rw-glass-foreground);\n  --sp-semantic-color-popover-foreground: var(--sp-rw-glass-foreground);\n  --sp-semantic-color-muted-foreground: var(--sp-rw-fg-tertiary);\n  --sp-rw-fg-destructive: var(--sp-rw-glass-destructive);\n  --sp-semantic-color-destructive-foreground: var(--sp-rw-glass-destructive);\n  /* Glass has no opaque colour for a control to tint. An unchecked switch\n   * track is a dark translucent channel, so the white thumb stands out; a grey\n   * track had almost the lightness of the glass and disappeared. The checked\n   * track and the rim come from the glass tone. */\n  --sp-rw-surface-track: oklch(0% 0 0 / 32%);\n  --sp-rw-switch-checked: var(--sp-rw-glass-switch-checked);\n  --sp-rw-switch-rim: oklch(100% 0 0 / 22%);\n  position: relative;\n  border: 0;\n  background: var(--sp-rw-glass-background);\n  box-shadow: var(--sp-rw-glass-shadow);\n  color: var(--sp-rw-glass-foreground);\n  -webkit-backdrop-filter: var(--sp-rw-glass-filter);\n  backdrop-filter: var(--sp-rw-glass-filter);\n  color-scheme: var(--sp-rw-glass-scheme);\n}\n\n/* The sheen is part of glass, also on a Popover, which hides its own ::before. */\n:root .sp-glass-surface::before {\n  display: block;\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  background: var(--sp-rw-glass-sheen);\n  box-shadow: none;\n  content: \"\";\n  pointer-events: none;\n}\n\n/* A tooltip's arrow takes the glass fill of its popup. */\n:root .sp-glass-surface > .sp-tooltip-arrow {\n  background: var(--sp-rw-glass-background);\n}\n\n/* Clear glass lies on artwork, such as the card on the picture of a card\n * preview. The picture does not follow the theme, so the glass does not either:\n * a faint neutral film that mutes the colours below, light text, plain blur\n * (the default glass adds brightness and saturation) and a bright rim with a soft\n * top highlight. Set `data-glass-tone=\"clear\"` next to the glass class. */\n:root .sp-glass-surface[data-glass-tone=\"clear\"] {\n  --sp-rw-glass-background: oklab(0.6 0 0 / 14%);\n  --sp-rw-glass-foreground: oklch(100% 0 0);\n  --sp-rw-glass-filter: blur(12px);\n  --sp-rw-glass-sheen: linear-gradient(\n    180deg,\n    rgb(255 255 255 / 10%) 0%,\n    transparent 40%\n  );\n  --sp-rw-glass-shadow:\n    inset 0 0 0 var(--sp-rw-border-default) rgb(255 255 255 / 34%),\n    inset 0 var(--sp-rw-border-default) 0 rgb(255 255 255 / 30%),\n    0 8px 20px -12px rgb(0 0 0 / 24%);\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/design-rework.css"},{"path":"packages/ui/src/system/styles/recipes.css","content":".sp-focus-ring:focus-visible,\n.sp-focus-ring--visible {\n  outline: var(--sp-semantic-state-focus-width) solid\n    var(--sp-semantic-color-focus);\n  outline-offset: var(--sp-semantic-state-focus-offset);\n}\n\n.sp-focus-ring--inset:focus-visible,\n.sp-focus-ring--inset.sp-focus-ring--visible {\n  outline-offset: calc(var(--sp-semantic-state-focus-width) * -1);\n}\n\n.sp-focus-ring--ring:focus-visible,\n.sp-focus-ring--ring.sp-focus-ring--visible {\n  outline: none;\n  box-shadow:\n    0 0 0 var(--sp-primitive-dimension-px) var(--sp-semantic-color-background),\n    0 0 0 calc(var(--sp-primitive-dimension-px) + 2px)\n      var(--sp-semantic-color-ring);\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/recipes.css"},{"path":"packages/ui/src/system/styles/surface.ts","content":"// A floating overlay is a frame and a surface. The frame (the floating-panel\n// motion, the positioning context and the ::before layer) is shared by every\n// variant. The opaque surface (fill, edge, shadow, text colour and the ::before\n// highlight) belongs to the default variant only: on glass the shared glass\n// rule draws them, and no utility may set them as well (rulebook P6). The\n// surface is the sp-floating-surface rule in design-rework.css, at zero\n// specificity, so the surface rule of a popup and a consumer's utility both\n// win over it.\nconst floatingOverlayFrame =\n  \"sp-floating-panel relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit]\";\nconst floatingOverlaySurface = \"sp-floating-surface\";\n\nexport const surfaceClassNames = {\n  iconWell: \"sp-icon-well\",\n  contained: \"rounded-lg border bg-background\",\n  edgeOverlay: `relative ${floatingOverlaySurface} before:pointer-events-none before:absolute before:inset-0`,\n  // The popup of Dialog and AlertDialog. Its motion (sp-dialog-motion),\n  // its scale and its slide on small screens (sp-dialog-mobile-stick)\n  // are design-rework.css rules, with the reduced-motion rules beside them (M5).\n  // Behind a nested dialog it stays visible and steps back: 10% less opacity\n  // per nested dialog here, and 10% smaller there on a wide screen.\n  dialogOverlayPopup:\n    \"relative row-start-2 min-h-0 min-w-0 origin-center flex-col opacity-[calc(1-0.1*var(--nested-dialogs))] sp-dialog-motion will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]\",\n  dialogOverlayCompositePopup:\n    \"relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 origin-center flex-col opacity-[calc(1-0.1*var(--nested-dialogs))] sp-dialog-motion will-change-transform data-ending-style:opacity-0 data-starting-style:opacity-0\",\n  dialogOverlayPopupMobileStick:\n    \"sp-dialog-mobile-stick max-sm:origin-bottom max-sm:before:rounded-none\",\n  dialogOverlayViewport: \"fixed inset-0 z-50 justify-items-center\",\n  // The disabled state of a field is the shared field rule in\n  // design-rework.css.\n  formControl:\n    \"relative inline-flex w-full ring-ring/24 has-focus-visible:has-aria-invalid:ring-destructive/16 dark:has-aria-invalid:ring-destructive/24\",\n  formControlSoft:\n    \"relative inline-flex w-full ring-ring/24 focus-within:has-aria-invalid:ring-destructive/16 dark:has-aria-invalid:ring-destructive/24\",\n  formControlCell:\n    \"relative min-w-0 text-center text-base ring-ring/24 focus-visible:z-10 focus-visible:ring-ring/24 disabled:cursor-not-allowed aria-invalid:focus-visible:ring-destructive/16 sm:text-sm dark:aria-invalid:focus-visible:ring-destructive/24\",\n  // The InputGroup's focus and invalid rings are the shared field rules'\n  // box-shadows in design-rework.css, so it sets no ring colour.\n  formControlComposite: \"relative inline-flex w-full min-w-0 items-center\",\n  formControlCompositeSoft:\n    \"relative inline-flex w-full min-w-0 items-center ring-ring/24 focus-within:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/16 dark:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/24\",\n  glass: \"sp-glass-surface\",\n  floatingOverlay: `${floatingOverlayFrame} ${floatingOverlaySurface}`,\n  floatingOverlayFrame,\n  floatingOverlaySurface,\n  overlay: `relative ${floatingOverlaySurface} before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit]`,\n  // The command dialog's popup. Its fill is the command profile's rule\n  // (design-rework.css), which publishes the surface colour, so a glass or\n  // theme override reaches it (rulebook P6).\n  overlayBorderless:\n    \"relative border-0 text-[var(--sp-rw-fg-primary)] shadow-[var(--sp-rw-floating-lg-shadow)]\",\n  // The backdrop of Dialog and AlertDialog. Its fade is the\n  // sp-overlay-backdrop-motion rule in design-rework.css, so reduced\n  // motion can make it instant (M5).\n  overlayBackdrop: \"sp-overlay-backdrop-motion fixed inset-0 z-50\",\n  raised:\n    \"relative before:pointer-events-none before:absolute before:inset-0 before:rounded-[inherit]\",\n} as const;\n\nconst linearIndicatorFill = \"\";\n\nexport const linearIndicatorClassNames = {\n  // The shared fill shape. Each owner adds its own value transition: Progress\n  // defines linear interpolation in progress.css, Meter keeps `indicator`,\n  // whose transition is the meter rule's in design-rework.css.\n  fill: linearIndicatorFill,\n  indicator: linearIndicatorFill,\n  label: \"\",\n  root: \"flex w-full flex-col\",\n  track: \"block w-full overflow-hidden\",\n  value: \"\",\n} as const;\n\nexport const linearIndicatorSizeClassNames = {\n  label: {\n    md: \"text-sm\",\n    lg: \"text-sm\",\n    sm: \"text-sm\",\n    xl: \"text-base sm:text-sm\",\n  },\n  root: {\n    md: \"gap-2\",\n    lg: \"gap-2\",\n    sm: \"gap-1.5\",\n    xl: \"gap-2.5\",\n  },\n  track: {\n    md: \"h-1.5\",\n    lg: \"h-2\",\n    sm: \"h-1\",\n    xl: \"h-2.5\",\n  },\n  value: {\n    md: \"text-sm\",\n    lg: \"text-sm\",\n    sm: \"text-xs\",\n    xl: \"text-base sm:text-sm\",\n  },\n} as const;\n\nexport type SurfaceClassName = keyof typeof surfaceClassNames;\n\nexport type FormControlSurface = \"default\" | \"elevated\" | \"inset\";\nexport type FormControlVariant = FormControlSurface | \"soft\";\n\nexport const formControlSurfaceClassNames = {\n  default: \"\",\n  // `shadow-form-control` is a marker, not a Tailwind utility. design-rework.css\n  // draws the elevated contour for every element that carries it\n  // (--sp-rw-surface-elevated-shadow, the elevated Card's), in both themes.\n  elevated: \"shadow-form-control\",\n  inset: \"\",\n} as const satisfies Record<FormControlSurface, string>;\n\nexport type FormControlTextSize = \"sm\" | \"base\";\n\n// Markers, not Tailwind utilities: the field text of each size is a kit base\n// rule in design-rework.css, so a field's own text rules refine it.\nexport const formControlTextClassNames = {\n  base: \"sp-field-text-base\",\n  sm: \"sp-field-text-sm\",\n} as const satisfies Record<FormControlTextSize, string>;\n","type":"registry:file","target":"~/src/siteplane/system/styles/surface.ts"},{"path":"packages/ui/src/system/styles/choice-menu-item.tsx","content":"import type { ReactNode } from \"react\";\nimport type React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { wrapTextRuns } from \"../utils/text-runs\";\n\n// A menu row keeps one line. Its text sits in a label span that can shrink\n// and ends in an ellipsis when the popup is narrower than the label.\nexport const menuRowLabelClassName = \"min-w-0 truncate\";\n\n// Wraps each run of text children of a menu row in that label span. Icons,\n// shortcuts and other elements stay direct children of the row, so the row\n// recipe still lays them out.\nexport function menuRowLabel(children: ReactNode): ReactNode[] {\n  return wrapTextRuns(children, (text, index) => (\n    <span\n      className={cn(\"sp-menu-row-label\", menuRowLabelClassName)}\n      data-slot=\"menu-row-label\"\n      key={`label-${index}`}\n    >\n      {text}\n    </span>\n  ));\n}\n\n// The rows keep one size at every control size, like the menu rows (S1): the\n// shared menu-row recipe in design-rework.css sets their height, text and icon.\n// The sp-choice-menu-item rule there gives a row the accent fill while\n// it is hovered, focused or highlighted, where no menu-row state rule does.\nexport const choiceMenuItemClassName =\n  \"sp-choice-menu-item grid in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] w-full cursor-pointer items-center whitespace-nowrap outline-none data-disabled:cursor-not-allowed data-disabled:opacity-(--sp-rw-disabled-opacity-action) [&_svg]:pointer-events-none [&_svg]:shrink-0\";\n\n// A check or radio indicator takes the leading icon slot of an action row\n// (rulebook I8). Its column is as wide as the icon, which the shared menu-row\n// slot in design-rework.css defines once, and the indicator carries no offset\n// of its own, so every label in a menu starts at the same x.\nexport const choiceMenuItemCheckGridClassName =\n  \"grid-cols-[var(--sp-rw-menu-row-icon)_minmax(0,1fr)]\";\n\n// The label of a check or radio row: one line, ending in an ellipsis when the\n// popup is narrower than the label.\nexport const choiceMenuItemLabelClassName = \"col-start-2 min-w-0 truncate\";\n\n// A switch row keeps the leading slot of the other rows of its menu (rulebook\n// I8): its label starts where the labels of icon, check and radio rows start,\n// and an icon passed first takes the slot. A menu whose rows lead with no mark\n// sets --sp-rw-menu-row-lead to 0 (design-rework.css), so its labels start at\n// the row padding. The switch sits at the end.\nexport const choiceMenuItemSwitchGridClassName =\n  \"grid-cols-[minmax(0,1fr)_auto]\";\n\nexport const choiceMenuItemSwitchLabelClassName =\n  \"col-start-1 flex min-w-0 items-center gap-(--sp-rw-menu-row-gap) ps-[calc(var(--sp-rw-menu-row-lead,1)*(var(--sp-rw-menu-row-icon)+var(--sp-rw-menu-row-gap)))] has-[>svg:first-child]:ps-0\";\n\n// A radio row with its indicator at the end (`indicatorPosition=\"end\"`) is an\n// identity row, such as a workspace in a switcher: it leads with its logo,\n// avatar or icon like an action row, and its check ends the row. It takes the\n// switch row's grid. A row that leads with an element puts it in the leading\n// slot; a row of text alone keeps the slot empty, so its label starts where the\n// other labels start (I8).\nexport const choiceMenuItemEndGridClassName = choiceMenuItemSwitchGridClassName;\n\nexport const choiceMenuItemEndLabelClassName =\n  \"col-start-1 flex min-w-0 items-center gap-(--sp-rw-menu-row-gap) ps-[calc(var(--sp-rw-menu-row-lead,1)*(var(--sp-rw-menu-row-icon)+var(--sp-rw-menu-row-gap)))] has-[>:not([data-slot=menu-row-label]):first-child]:ps-0\";\n\n// A Menu or ContextMenu switch keeps the size that fits the compact row at\n// every control size; an explicit size still sets another one.\nexport const choiceMenuSwitchSize = \"lg\";\n\n// The check of a choice row: the checkbox and radio items of Menu,\n// ContextMenu and DrawerMenu and the items of Select and Combobox. It draws\n// itself in and out like the check of the Checkbox: its path carries the\n// Checkbox's `sp-check-path-motion`, and both read the state of the\n// indicator around the glyph, so the rules live once in motion.css. The glyph\n// adds the Checkbox indicator's pop. An indicator that holds this glyph stays\n// mounted (`keepMounted`): Base UI unmounts one that runs no animation of its\n// own, which would cut the leave short. The glyph keeps its size and its slot,\n// so the motion changes no geometry.\nexport function ChoiceMenuItemCheck(): React.ReactElement {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className=\"sp-check-glyph-motion\"\n      fill=\"none\"\n      height=\"24\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeWidth=\"2\"\n      viewBox=\"0 0 24 24\"\n      width=\"24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        className=\"sp-check-path-motion\"\n        d=\"M5.252 12.7 10.2 18.63 18.748 5.37\"\n      />\n    </svg>\n  );\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/choice-menu-item.tsx"},{"path":"packages/ui/src/system/styles/checkbox-glyph.tsx","content":"import type { SiteplaneControlSize } from \"../provider/siteplane-ui-provider\";\n\n// The check and the indeterminate dash of a checkbox, sized for the box of its\n// control size. The Checkbox and the selection box of a Table render this\n// glyph, so every checkbox of the kit shares one geometry, one stroke and one\n// size scale. Both paths carry the shared check motion (motion.css): the\n// element that holds the state wraps the svg, and the paths draw in and out as\n// it comes and goes. Each path is about 24 units long, the length of the dash.\nconst checkboxGlyphSizeClassName = {\n  sm: \"size-3 sm:size-2.5\",\n  md: \"size-3.5 sm:size-3\",\n  lg: \"size-4 sm:size-3.5\",\n  xl: \"size-4.5 sm:size-4\",\n} as const satisfies Record<SiteplaneControlSize, string>;\n\nexport function CheckboxGlyph({ size }: { size: SiteplaneControlSize }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={checkboxGlyphSizeClassName[size]}\n      fill=\"none\"\n      height=\"24\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeWidth=\"3\"\n      viewBox=\"0 0 24 24\"\n      width=\"24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        className=\"sp-check-path-motion\"\n        d=\"M5.252 12.7 10.2 18.63 18.748 5.37\"\n      />\n      <path className=\"sp-checkbox-indeterminate\" d=\"M5.252 12h13.496\" />\n    </svg>\n  );\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/checkbox-glyph.tsx"},{"path":"packages/ui/src/system/styles/generated/tokens.css","content":":root {\n  --sp-component-collection-item-min-height: 32px;\n  --sp-component-collection-item-padding-inline: 8px;\n  --sp-component-control-gap: 8px;\n  --sp-component-control-height-lg: 48px;\n  --sp-component-control-height-md: 40px;\n  --sp-component-control-height-sm: 32px;\n  --sp-component-control-padding-inline-lg: 16px;\n  --sp-component-control-padding-inline-md: 12px;\n  --sp-component-control-padding-inline-sm: 10px;\n  --sp-component-field-gap: 6px;\n  --sp-component-field-message-gap: 4px;\n  --sp-component-overlay-offset: 8px;\n  --sp-component-overlay-padding: 16px;\n  --sp-primitive-color-amber-100: oklch(0.95 0.05 85);\n  --sp-primitive-color-amber-400: oklch(0.828 0.189 84.429);\n  --sp-primitive-color-amber-500: oklch(0.769 0.188 70.08);\n  --sp-primitive-color-amber-600: oklch(0.61 0.14 70);\n  --sp-primitive-color-amber-700: oklch(0.555 0.163 48.998);\n  --sp-primitive-color-background-dark: oklch(0.1957 0 0);\n  --sp-primitive-color-background-light: oklch(0.9699 0 107);\n  --sp-primitive-color-black-alpha-10: rgb(0 0 0 / 10%);\n  --sp-primitive-color-black-alpha-4: rgb(0 0 0 / 4%);\n  --sp-primitive-color-black-alpha-40: oklch(0 0 0 / 0.4);\n  --sp-primitive-color-black-alpha-8: rgb(0 0 0 / 8%);\n  --sp-primitive-color-blue-100: oklch(0.93 0.035 250);\n  --sp-primitive-color-blue-400: oklch(0.707 0.165 254.624);\n  --sp-primitive-color-blue-500: oklch(0.623 0.214 259.815);\n  --sp-primitive-color-blue-600: oklch(0.53 0.17 252);\n  --sp-primitive-color-blue-700: oklch(0.488 0.243 264.376);\n  --sp-primitive-color-brand: oklch(0.335 0.061 267.9);\n  --sp-primitive-color-foreground-dark: oklch(0.9521 0 0);\n  --sp-primitive-color-green-100: oklch(0.94 0.045 145);\n  --sp-primitive-color-green-400: oklch(0.765 0.177 163.223);\n  --sp-primitive-color-green-500: oklch(0.696 0.17 162.48);\n  --sp-primitive-color-green-600: oklch(0.54 0.145 145);\n  --sp-primitive-color-green-700: oklch(0.508 0.118 165.612);\n  --sp-primitive-color-muted-foreground-dark: oklch(0.6233 0 0);\n  --sp-primitive-color-muted-foreground-light: oklch(0.5 0.001 107);\n  --sp-primitive-color-neutral-100: oklch(0.97 0 0);\n  --sp-primitive-color-neutral-200: oklch(0.922 0 0);\n  --sp-primitive-color-neutral-400: oklch(0.708 0 0);\n  --sp-primitive-color-neutral-50: oklch(0.985 0 0);\n  --sp-primitive-color-neutral-500: oklch(0.556 0 0);\n  --sp-primitive-color-neutral-700: oklch(0.371 0 0);\n  --sp-primitive-color-neutral-800: oklch(0.269 0 0);\n  --sp-primitive-color-neutral-900: oklch(0.205 0 0);\n  --sp-primitive-color-neutral-950: oklch(0.145 0 0);\n  --sp-primitive-color-red-100: oklch(0.94 0.04 25);\n  --sp-primitive-color-red-400: oklch(0.704 0.191 22.216);\n  --sp-primitive-color-red-500: oklch(0.637 0.237 25.331);\n  --sp-primitive-color-red-500-dark: rgb(251.022 64.923 74.409);\n  --sp-primitive-color-red-600: oklch(0.52 0.19 25);\n  --sp-primitive-color-red-700: oklch(0.505 0.213 27.518);\n  --sp-primitive-color-surface-dark: oklch(0.2431 0 0);\n  --sp-primitive-color-white: oklch(1 0 0);\n  --sp-primitive-color-white-alpha-4: rgb(255 255 255 / 4%);\n  --sp-primitive-color-white-alpha-6: rgb(255 255 255 / 6%);\n  --sp-primitive-color-white-alpha-8: rgb(255 255 255 / 8%);\n  --sp-primitive-dimension-font-lg: 20px;\n  --sp-primitive-dimension-font-md: 16px;\n  --sp-primitive-dimension-font-sm: 14px;\n  --sp-primitive-dimension-font-xs: 12px;\n  --sp-primitive-dimension-px: 1px;\n  --sp-primitive-dimension-radius-sm: 6px;\n  --sp-primitive-dimension-space-1: 4px;\n  --sp-primitive-dimension-space-1-5: 6px;\n  --sp-primitive-dimension-space-10: 40px;\n  --sp-primitive-dimension-space-12: 48px;\n  --sp-primitive-dimension-space-2: 8px;\n  --sp-primitive-dimension-space-2-5: 10px;\n  --sp-primitive-dimension-space-3: 12px;\n  --sp-primitive-dimension-space-4: 16px;\n  --sp-primitive-dimension-space-5: 20px;\n  --sp-primitive-dimension-space-6: 24px;\n  --sp-primitive-dimension-space-8: 32px;\n  --sp-primitive-font-mono: ui-monospace, SFMono-Regular, Consolas, \"Liberation Mono\", monospace;\n  --sp-primitive-font-sans: Inter, ui-sans-serif, system-ui, sans-serif;\n  --sp-primitive-number-font-medium: 500;\n  --sp-primitive-number-font-semibold: 600;\n  --sp-primitive-number-opacity-disabled: 0.5;\n  --sp-semantic-color-accent: rgb(0 0 0 / 4%);\n  --sp-semantic-color-background: oklch(0.9699 0 107);\n  --sp-semantic-color-border: rgb(0 0 0 / 8%);\n  --sp-semantic-color-border-strong: oklch(0.708 0 0);\n  --sp-semantic-color-card: oklch(1 0 0);\n  --sp-semantic-color-card-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-control: oklch(1 0 0);\n  --sp-semantic-color-control-disabled: oklch(0.97 0 0);\n  --sp-semantic-color-control-disabled-foreground: var(--sp-rw-fg-quaternary);\n  --sp-semantic-color-control-hover: oklch(0.97 0 0);\n  --sp-semantic-color-destructive: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-destructive-foreground: oklch(0.505 0.213 27.518);\n  --sp-semantic-color-destructive-hover: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-focus: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-info: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-info-foreground: oklch(0.488 0.243 264.376);\n  --sp-semantic-color-input: rgb(0 0 0 / 10%);\n  --sp-semantic-color-invalid: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-invalid-muted: oklch(0.94 0.04 25);\n  --sp-semantic-color-muted: rgb(0 0 0 / 4%);\n  --sp-semantic-color-muted-foreground: var(--sp-rw-fg-tertiary);\n  --sp-semantic-color-overlay-backdrop: oklch(0 0 0 / 0.4);\n  --sp-semantic-color-popover: oklch(1 0 0);\n  --sp-semantic-color-popover-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-primary: oklch(0.335 0.061 267.9);\n  --sp-semantic-color-primary-foreground: var(--sp-rw-fg-emphasis);\n  --sp-semantic-color-primary-hover: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-ring: oklch(0.708 0 0);\n  --sp-semantic-color-secondary: rgb(0 0 0 / 4%);\n  --sp-semantic-color-secondary-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-success: oklch(0.696 0.17 162.48);\n  --sp-semantic-color-success-foreground: oklch(0.508 0.118 165.612);\n  --sp-semantic-color-surface: oklch(1 0 0);\n  --sp-semantic-color-surface-muted: rgb(0 0 0 / 4%);\n  --sp-semantic-color-surface-subtle: rgb(0 0 0 / 4%);\n  --sp-semantic-color-warning: oklch(0.769 0.188 70.08);\n  --sp-semantic-color-warning-foreground: oklch(0.555 0.163 48.998);\n  --sp-semantic-font-family: Inter, ui-sans-serif, system-ui, sans-serif;\n  --sp-semantic-font-family-mono: ui-monospace, SFMono-Regular, Consolas, \"Liberation Mono\", monospace;\n  --sp-semantic-font-size-body: 14px;\n  --sp-semantic-font-size-label: 14px;\n  --sp-semantic-font-weight-medium: 500;\n  --sp-semantic-font-weight-semibold: 600;\n  --sp-semantic-state-disabled-opacity: 0.5;\n  --sp-semantic-state-focus-offset: 2px;\n  --sp-semantic-state-focus-width: 2px;\n}\n\n.light,\n[data-theme=\"light\"] {\n  --sp-semantic-color-accent: rgb(0 0 0 / 4%);\n  --sp-semantic-color-background: oklch(0.9699 0 107);\n  --sp-semantic-color-border: rgb(0 0 0 / 8%);\n  --sp-semantic-color-border-strong: oklch(0.708 0 0);\n  --sp-semantic-color-card: oklch(1 0 0);\n  --sp-semantic-color-card-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-control: oklch(1 0 0);\n  --sp-semantic-color-control-disabled: oklch(0.97 0 0);\n  --sp-semantic-color-control-disabled-foreground: var(--sp-rw-fg-quaternary);\n  --sp-semantic-color-control-hover: oklch(0.97 0 0);\n  --sp-semantic-color-destructive: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-destructive-foreground: oklch(0.505 0.213 27.518);\n  --sp-semantic-color-destructive-hover: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-focus: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-info: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-info-foreground: oklch(0.488 0.243 264.376);\n  --sp-semantic-color-input: rgb(0 0 0 / 10%);\n  --sp-semantic-color-invalid: oklch(0.637 0.237 25.331);\n  --sp-semantic-color-invalid-muted: oklch(0.94 0.04 25);\n  --sp-semantic-color-muted: rgb(0 0 0 / 4%);\n  --sp-semantic-color-muted-foreground: var(--sp-rw-fg-tertiary);\n  --sp-semantic-color-overlay-backdrop: oklch(0 0 0 / 0.4);\n  --sp-semantic-color-popover: oklch(1 0 0);\n  --sp-semantic-color-popover-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-primary: oklch(0.335 0.061 267.9);\n  --sp-semantic-color-primary-foreground: var(--sp-rw-fg-emphasis);\n  --sp-semantic-color-primary-hover: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-ring: oklch(0.708 0 0);\n  --sp-semantic-color-secondary: rgb(0 0 0 / 4%);\n  --sp-semantic-color-secondary-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-success: oklch(0.696 0.17 162.48);\n  --sp-semantic-color-success-foreground: oklch(0.508 0.118 165.612);\n  --sp-semantic-color-surface: oklch(1 0 0);\n  --sp-semantic-color-surface-muted: rgb(0 0 0 / 4%);\n  --sp-semantic-color-surface-subtle: rgb(0 0 0 / 4%);\n  --sp-semantic-color-warning: oklch(0.769 0.188 70.08);\n  --sp-semantic-color-warning-foreground: oklch(0.555 0.163 48.998);\n}\n\n.dark,\n[data-theme=\"dark\"] {\n  --sp-semantic-color-accent: rgb(255 255 255 / 4%);\n  --sp-semantic-color-background: oklch(0.1957 0 0);\n  --sp-semantic-color-border: rgb(255 255 255 / 6%);\n  --sp-semantic-color-border-strong: oklch(0.556 0 0);\n  --sp-semantic-color-card: oklch(0.2431 0 0);\n  --sp-semantic-color-card-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-control: oklch(0.205 0 0);\n  --sp-semantic-color-control-disabled: oklch(0.269 0 0);\n  --sp-semantic-color-control-disabled-foreground: var(--sp-rw-fg-quaternary);\n  --sp-semantic-color-control-hover: oklch(0.269 0 0);\n  --sp-semantic-color-destructive: rgb(251.022 64.923 74.409);\n  --sp-semantic-color-destructive-foreground: oklch(0.704 0.191 22.216);\n  --sp-semantic-color-destructive-hover: oklch(0.94 0.04 25);\n  --sp-semantic-color-focus: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-info: oklch(0.623 0.214 259.815);\n  --sp-semantic-color-info-foreground: oklch(0.707 0.165 254.624);\n  --sp-semantic-color-input: rgb(255 255 255 / 8%);\n  --sp-semantic-color-invalid: rgb(251.022 64.923 74.409);\n  --sp-semantic-color-invalid-muted: oklch(0.269 0 0);\n  --sp-semantic-color-muted: rgb(255 255 255 / 4%);\n  --sp-semantic-color-muted-foreground: var(--sp-rw-fg-tertiary);\n  --sp-semantic-color-overlay-backdrop: oklch(0 0 0 / 0.4);\n  --sp-semantic-color-popover: oklch(0.2431 0 0);\n  --sp-semantic-color-popover-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-primary: oklch(0.335 0.061 267.9);\n  --sp-semantic-color-primary-foreground: var(--sp-rw-fg-emphasis);\n  --sp-semantic-color-primary-hover: oklch(0.93 0.035 250);\n  --sp-semantic-color-ring: oklch(0.556 0 0);\n  --sp-semantic-color-secondary: rgb(255 255 255 / 4%);\n  --sp-semantic-color-secondary-foreground: var(--sp-rw-fg-primary);\n  --sp-semantic-color-success: oklch(0.696 0.17 162.48);\n  --sp-semantic-color-success-foreground: oklch(0.765 0.177 163.223);\n  --sp-semantic-color-surface: oklch(0.2431 0 0);\n  --sp-semantic-color-surface-muted: rgb(255 255 255 / 4%);\n  --sp-semantic-color-surface-subtle: rgb(255 255 255 / 4%);\n  --sp-semantic-color-warning: oklch(0.769 0.188 70.08);\n  --sp-semantic-color-warning-foreground: oklch(0.828 0.189 84.429);\n}\n\n.dark .light,\n.dark [data-theme=\"light\"],\n[data-theme=\"dark\"] .light,\n[data-theme=\"dark\"] [data-theme=\"light\"] {\n  --sp-semantic-color-card-foreground: oklch(10% 0 0);\n  --sp-semantic-color-control-disabled-foreground: oklch(69% 0.001 107);\n  --sp-semantic-color-foreground: oklch(10% 0 0);\n  --sp-semantic-color-muted-foreground: oklch(50% 0.001 107);\n  --sp-semantic-color-popover-foreground: oklch(10% 0 0);\n  --sp-semantic-color-primary-foreground: oklch(100% 0 0);\n  --sp-semantic-color-secondary-foreground: oklch(10% 0 0);\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/generated/tokens.css"},{"path":"packages/ui/src/system/styles/generated/tailwind.css","content":"@theme inline {\n  --color-accent: var(--sp-semantic-color-accent);\n  --color-background: var(--sp-semantic-color-background);\n  --color-border: var(--sp-semantic-color-border);\n  --color-border-strong: var(--sp-semantic-color-border-strong);\n  --color-card: var(--sp-semantic-color-card);\n  --color-card-foreground: var(--sp-semantic-color-card-foreground);\n  --color-control: var(--sp-semantic-color-control);\n  --color-control-disabled: var(--sp-semantic-color-control-disabled);\n  --color-control-disabled-foreground: var(--sp-semantic-color-control-disabled-foreground);\n  --color-control-hover: var(--sp-semantic-color-control-hover);\n  --color-destructive: var(--sp-semantic-color-destructive);\n  --color-destructive-foreground: var(--sp-semantic-color-destructive-foreground);\n  --color-destructive-hover: var(--sp-semantic-color-destructive-hover);\n  --color-focus: var(--sp-semantic-color-focus);\n  --color-foreground: var(--sp-semantic-color-foreground);\n  --color-info: var(--sp-semantic-color-info);\n  --color-info-foreground: var(--sp-semantic-color-info-foreground);\n  --color-input: var(--sp-semantic-color-input);\n  --color-invalid: var(--sp-semantic-color-invalid);\n  --color-invalid-muted: var(--sp-semantic-color-invalid-muted);\n  --color-muted: var(--sp-semantic-color-muted);\n  --color-muted-foreground: var(--sp-semantic-color-muted-foreground);\n  --color-overlay-backdrop: var(--sp-semantic-color-overlay-backdrop);\n  --color-popover: var(--sp-semantic-color-popover);\n  --color-popover-foreground: var(--sp-semantic-color-popover-foreground);\n  --color-primary: var(--sp-semantic-color-primary);\n  --color-primary-foreground: var(--sp-semantic-color-primary-foreground);\n  --color-primary-hover: var(--sp-semantic-color-primary-hover);\n  --color-ring: var(--sp-semantic-color-ring);\n  --color-secondary: var(--sp-semantic-color-secondary);\n  --color-secondary-foreground: var(--sp-semantic-color-secondary-foreground);\n  --color-success: var(--sp-semantic-color-success);\n  --color-success-foreground: var(--sp-semantic-color-success-foreground);\n  --color-surface: var(--sp-semantic-color-surface);\n  --color-surface-muted: var(--sp-semantic-color-surface-muted);\n  --color-surface-subtle: var(--sp-semantic-color-surface-subtle);\n  --color-warning: var(--sp-semantic-color-warning);\n  --color-warning-foreground: var(--sp-semantic-color-warning-foreground);\n}\n","type":"registry:file","target":"~/src/siteplane/system/styles/generated/tailwind.css"},{"path":"packages/ui/src/system/tokens/primitives.tokens.json","content":"{\n  \"$schema\": \"https://www.designtokens.org/schemas/2025.10/format.json\",\n  \"primitive\": {\n    \"color\": {\n      \"$type\": \"color\",\n      \"white\": { \"$value\": { \"colorSpace\": \"oklch\", \"components\": [1, 0, 0] } },\n      \"brand\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.335, 0.061, 267.9] }\n      },\n      \"neutral-50\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.985, 0, 0] }\n      },\n      \"neutral-100\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.97, 0, 0] }\n      },\n      \"neutral-200\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.922, 0, 0] }\n      },\n      \"neutral-400\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.708, 0, 0] }\n      },\n      \"neutral-500\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.556, 0, 0] }\n      },\n      \"muted-foreground-light\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.5, 0.001, 107] }\n      },\n      \"muted-foreground-dark\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.6233, 0, 0]\n        }\n      },\n      \"background-light\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.9699, 0, 107]\n        }\n      },\n      \"background-dark\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.1957, 0, 0]\n        }\n      },\n      \"foreground-dark\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.9521, 0, 0]\n        }\n      },\n      \"surface-dark\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.2431, 0, 0]\n        }\n      },\n      \"neutral-700\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.371, 0, 0] }\n      },\n      \"neutral-800\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.269, 0, 0] }\n      },\n      \"neutral-900\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.205, 0, 0] }\n      },\n      \"neutral-950\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.145, 0, 0] }\n      },\n      \"blue-100\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.93, 0.035, 250] }\n      },\n      \"blue-400\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.707, 0.165, 254.624]\n        }\n      },\n      \"blue-500\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.623, 0.214, 259.815]\n        }\n      },\n      \"blue-600\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.53, 0.17, 252] }\n      },\n      \"blue-700\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.488, 0.243, 264.376]\n        }\n      },\n      \"red-100\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.94, 0.04, 25] }\n      },\n      \"red-400\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.704, 0.191, 22.216]\n        }\n      },\n      \"red-500\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.637, 0.237, 25.331]\n        }\n      },\n      \"red-500-dark\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [0.9844, 0.2546, 0.2918]\n        }\n      },\n      \"red-600\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.52, 0.19, 25] }\n      },\n      \"red-700\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.505, 0.213, 27.518]\n        }\n      },\n      \"green-100\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.94, 0.045, 145] }\n      },\n      \"green-400\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.765, 0.177, 163.223]\n        }\n      },\n      \"green-500\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.696, 0.17, 162.48]\n        }\n      },\n      \"green-600\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.54, 0.145, 145] }\n      },\n      \"green-700\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.508, 0.118, 165.612]\n        }\n      },\n      \"amber-100\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.95, 0.05, 85] }\n      },\n      \"amber-400\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.828, 0.189, 84.429]\n        }\n      },\n      \"amber-500\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.769, 0.188, 70.08]\n        }\n      },\n      \"amber-600\": {\n        \"$value\": { \"colorSpace\": \"oklch\", \"components\": [0.61, 0.14, 70] }\n      },\n      \"amber-700\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0.555, 0.163, 48.998]\n        }\n      },\n      \"black-alpha-40\": {\n        \"$value\": {\n          \"colorSpace\": \"oklch\",\n          \"components\": [0, 0, 0],\n          \"alpha\": 0.4\n        }\n      },\n      \"black-alpha-4\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [0, 0, 0],\n          \"alpha\": 0.04\n        }\n      },\n      \"black-alpha-8\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [0, 0, 0],\n          \"alpha\": 0.08\n        }\n      },\n      \"black-alpha-10\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [0, 0, 0],\n          \"alpha\": 0.1\n        }\n      },\n      \"white-alpha-4\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [1, 1, 1],\n          \"alpha\": 0.04\n        }\n      },\n      \"white-alpha-6\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [1, 1, 1],\n          \"alpha\": 0.06\n        }\n      },\n      \"white-alpha-8\": {\n        \"$value\": {\n          \"colorSpace\": \"srgb\",\n          \"components\": [1, 1, 1],\n          \"alpha\": 0.08\n        }\n      }\n    },\n    \"dimension\": {\n      \"$type\": \"dimension\",\n      \"px\": { \"$value\": { \"value\": 1, \"unit\": \"px\" } },\n      \"space-1\": { \"$value\": { \"value\": 4, \"unit\": \"px\" } },\n      \"space-1-5\": { \"$value\": { \"value\": 6, \"unit\": \"px\" } },\n      \"space-2\": { \"$value\": { \"value\": 8, \"unit\": \"px\" } },\n      \"space-2-5\": { \"$value\": { \"value\": 10, \"unit\": \"px\" } },\n      \"space-3\": { \"$value\": { \"value\": 12, \"unit\": \"px\" } },\n      \"space-4\": { \"$value\": { \"value\": 16, \"unit\": \"px\" } },\n      \"space-5\": { \"$value\": { \"value\": 20, \"unit\": \"px\" } },\n      \"space-6\": { \"$value\": { \"value\": 24, \"unit\": \"px\" } },\n      \"space-8\": { \"$value\": { \"value\": 32, \"unit\": \"px\" } },\n      \"space-10\": { \"$value\": { \"value\": 40, \"unit\": \"px\" } },\n      \"space-12\": { \"$value\": { \"value\": 48, \"unit\": \"px\" } },\n      \"radius-sm\": { \"$value\": { \"value\": 6, \"unit\": \"px\" } },\n      \"font-xs\": { \"$value\": { \"value\": 12, \"unit\": \"px\" } },\n      \"font-sm\": { \"$value\": { \"value\": 14, \"unit\": \"px\" } },\n      \"font-md\": { \"$value\": { \"value\": 16, \"unit\": \"px\" } },\n      \"font-lg\": { \"$value\": { \"value\": 20, \"unit\": \"px\" } }\n    },\n    \"number\": {\n      \"$type\": \"number\",\n      \"opacity-disabled\": { \"$value\": 0.5 },\n      \"font-medium\": { \"$value\": 500 },\n      \"font-semibold\": { \"$value\": 600 }\n    },\n    \"font\": {\n      \"$type\": \"fontFamily\",\n      \"sans\": {\n        \"$value\": [\"Inter\", \"ui-sans-serif\", \"system-ui\", \"sans-serif\"]\n      },\n      \"mono\": {\n        \"$value\": [\n          \"ui-monospace\",\n          \"SFMono-Regular\",\n          \"Consolas\",\n          \"Liberation Mono\",\n          \"monospace\"\n        ]\n      }\n    }\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/tokens/primitives.tokens.json"},{"path":"packages/ui/src/system/tokens/semantic.tokens.json","content":"{\n  \"$schema\": \"https://www.designtokens.org/schemas/2025.10/format.json\",\n  \"semantic\": {\n    \"color\": {\n      \"$type\": \"color\",\n      \"$description\": \"The grey text colours (foreground, card, popover, secondary, muted, disabled and the text on the primary fill) alias the kit's text tokens in packages/ui/src/system/styles/design-rework.css, written as {rw.fg.*}: one grey system in both themes (Lukas, 15 October 2026). The status text colours keep their own values.\",\n      \"background\": { \"$value\": \"{primitive.color.background-light}\" },\n      \"foreground\": { \"$value\": \"{rw.fg.primary}\" },\n      \"surface\": { \"$value\": \"{primitive.color.white}\" },\n      \"card\": { \"$value\": \"{primitive.color.white}\" },\n      \"card-foreground\": { \"$value\": \"{rw.fg.primary}\" },\n      \"surface-muted\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"surface-subtle\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"popover\": { \"$value\": \"{semantic.color.surface}\" },\n      \"popover-foreground\": { \"$value\": \"{rw.fg.primary}\" },\n      \"muted\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"muted-foreground\": { \"$value\": \"{rw.fg.tertiary}\" },\n      \"border\": { \"$value\": \"{primitive.color.black-alpha-8}\" },\n      \"border-strong\": { \"$value\": \"{primitive.color.neutral-400}\" },\n      \"primary\": { \"$value\": \"{primitive.color.brand}\" },\n      \"primary-hover\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"primary-foreground\": { \"$value\": \"{rw.fg.emphasis}\" },\n      \"secondary\": { \"$value\": \"{semantic.color.surface-muted}\" },\n      \"secondary-foreground\": { \"$value\": \"{semantic.color.foreground}\" },\n      \"accent\": { \"$value\": \"{semantic.color.surface-muted}\" },\n      \"destructive\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"destructive-hover\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"destructive-foreground\": { \"$value\": \"{primitive.color.red-700}\" },\n      \"info\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"info-foreground\": { \"$value\": \"{primitive.color.blue-700}\" },\n      \"success\": { \"$value\": \"{primitive.color.green-500}\" },\n      \"success-foreground\": { \"$value\": \"{primitive.color.green-700}\" },\n      \"warning\": { \"$value\": \"{primitive.color.amber-500}\" },\n      \"warning-foreground\": { \"$value\": \"{primitive.color.amber-700}\" },\n      \"focus\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"ring\": { \"$value\": \"{primitive.color.neutral-400}\" },\n      \"input\": { \"$value\": \"{primitive.color.black-alpha-10}\" },\n      \"control\": { \"$value\": \"{primitive.color.white}\" },\n      \"control-hover\": { \"$value\": \"{primitive.color.neutral-100}\" },\n      \"control-disabled\": { \"$value\": \"{primitive.color.neutral-100}\" },\n      \"control-disabled-foreground\": { \"$value\": \"{rw.fg.quaternary}\" },\n      \"invalid\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"invalid-muted\": { \"$value\": \"{primitive.color.red-100}\" },\n      \"overlay-backdrop\": { \"$value\": \"{primitive.color.black-alpha-40}\" }\n    },\n    \"font\": {\n      \"family\": { \"$type\": \"fontFamily\", \"$value\": \"{primitive.font.sans}\" },\n      \"family-mono\": {\n        \"$type\": \"fontFamily\",\n        \"$value\": \"{primitive.font.mono}\"\n      },\n      \"size-body\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.font-sm}\"\n      },\n      \"size-label\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.font-sm}\"\n      },\n      \"weight-medium\": {\n        \"$type\": \"number\",\n        \"$value\": \"{primitive.number.font-medium}\"\n      },\n      \"weight-semibold\": {\n        \"$type\": \"number\",\n        \"$value\": \"{primitive.number.font-semibold}\"\n      }\n    },\n    \"state\": {\n      \"disabled-opacity\": {\n        \"$type\": \"number\",\n        \"$value\": \"{primitive.number.opacity-disabled}\"\n      },\n      \"focus-width\": {\n        \"$type\": \"dimension\",\n        \"$value\": { \"value\": 2, \"unit\": \"px\" }\n      },\n      \"focus-offset\": {\n        \"$type\": \"dimension\",\n        \"$value\": { \"value\": 2, \"unit\": \"px\" }\n      }\n    }\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/tokens/semantic.tokens.json"},{"path":"packages/ui/src/system/tokens/component.tokens.json","content":"{\n  \"$schema\": \"https://www.designtokens.org/schemas/2025.10/format.json\",\n  \"component\": {\n    \"control\": {\n      \"height-sm\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-8}\"\n      },\n      \"height-md\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-10}\"\n      },\n      \"height-lg\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-12}\"\n      },\n      \"padding-inline-sm\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-2-5}\"\n      },\n      \"padding-inline-md\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-3}\"\n      },\n      \"padding-inline-lg\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-4}\"\n      },\n      \"gap\": { \"$type\": \"dimension\", \"$value\": \"{primitive.dimension.space-2}\" }\n    },\n    \"field\": {\n      \"gap\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-1-5}\"\n      },\n      \"message-gap\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-1}\"\n      }\n    },\n    \"overlay\": {\n      \"padding\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-4}\"\n      },\n      \"offset\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-2}\"\n      }\n    },\n    \"collection-item\": {\n      \"min-height\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-8}\"\n      },\n      \"padding-inline\": {\n        \"$type\": \"dimension\",\n        \"$value\": \"{primitive.dimension.space-2}\"\n      }\n    }\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/tokens/component.tokens.json"},{"path":"packages/ui/src/system/tokens/themes/light.tokens.json","content":"{\n  \"$schema\": \"https://www.designtokens.org/schemas/2025.10/format.json\",\n  \"semantic\": {\n    \"color\": {\n      \"$type\": \"color\",\n      \"$description\": \"The grey text colours alias the kit's text tokens in every theme (semantic.tokens.json), so this theme leaves them out.\",\n      \"background\": { \"$value\": \"{primitive.color.background-light}\" },\n      \"surface\": { \"$value\": \"{primitive.color.white}\" },\n      \"card\": { \"$value\": \"{primitive.color.white}\" },\n      \"surface-muted\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"surface-subtle\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"muted\": { \"$value\": \"{primitive.color.black-alpha-4}\" },\n      \"border\": { \"$value\": \"{primitive.color.black-alpha-8}\" },\n      \"border-strong\": { \"$value\": \"{primitive.color.neutral-400}\" },\n      \"primary\": { \"$value\": \"{primitive.color.brand}\" },\n      \"primary-hover\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"destructive\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"destructive-hover\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"destructive-foreground\": { \"$value\": \"{primitive.color.red-700}\" },\n      \"info\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"info-foreground\": { \"$value\": \"{primitive.color.blue-700}\" },\n      \"success\": { \"$value\": \"{primitive.color.green-500}\" },\n      \"success-foreground\": { \"$value\": \"{primitive.color.green-700}\" },\n      \"warning\": { \"$value\": \"{primitive.color.amber-500}\" },\n      \"warning-foreground\": { \"$value\": \"{primitive.color.amber-700}\" },\n      \"focus\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"ring\": { \"$value\": \"{primitive.color.neutral-400}\" },\n      \"input\": { \"$value\": \"{primitive.color.black-alpha-10}\" },\n      \"control\": { \"$value\": \"{primitive.color.white}\" },\n      \"control-hover\": { \"$value\": \"{primitive.color.neutral-100}\" },\n      \"control-disabled\": { \"$value\": \"{primitive.color.neutral-100}\" },\n      \"invalid\": { \"$value\": \"{primitive.color.red-500}\" },\n      \"invalid-muted\": { \"$value\": \"{primitive.color.red-100}\" },\n      \"overlay-backdrop\": { \"$value\": \"{primitive.color.black-alpha-40}\" }\n    }\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/tokens/themes/light.tokens.json"},{"path":"packages/ui/src/system/tokens/themes/dark.tokens.json","content":"{\n  \"$schema\": \"https://www.designtokens.org/schemas/2025.10/format.json\",\n  \"semantic\": {\n    \"color\": {\n      \"$type\": \"color\",\n      \"$description\": \"The grey text colours alias the kit's text tokens in every theme (semantic.tokens.json), so this theme leaves them out.\",\n      \"background\": { \"$value\": \"{primitive.color.background-dark}\" },\n      \"surface\": { \"$value\": \"{primitive.color.surface-dark}\" },\n      \"card\": { \"$value\": \"{primitive.color.surface-dark}\" },\n      \"surface-muted\": { \"$value\": \"{primitive.color.white-alpha-4}\" },\n      \"surface-subtle\": { \"$value\": \"{primitive.color.white-alpha-4}\" },\n      \"muted\": { \"$value\": \"{primitive.color.white-alpha-4}\" },\n      \"border\": { \"$value\": \"{primitive.color.white-alpha-6}\" },\n      \"border-strong\": { \"$value\": \"{primitive.color.neutral-500}\" },\n      \"primary\": { \"$value\": \"{primitive.color.brand}\" },\n      \"primary-hover\": { \"$value\": \"{primitive.color.blue-100}\" },\n      \"destructive\": { \"$value\": \"{primitive.color.red-500-dark}\" },\n      \"destructive-hover\": { \"$value\": \"{primitive.color.red-100}\" },\n      \"destructive-foreground\": { \"$value\": \"{primitive.color.red-400}\" },\n      \"info\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"info-foreground\": { \"$value\": \"{primitive.color.blue-400}\" },\n      \"success\": { \"$value\": \"{primitive.color.green-500}\" },\n      \"success-foreground\": { \"$value\": \"{primitive.color.green-400}\" },\n      \"warning\": { \"$value\": \"{primitive.color.amber-500}\" },\n      \"warning-foreground\": { \"$value\": \"{primitive.color.amber-400}\" },\n      \"focus\": { \"$value\": \"{primitive.color.blue-500}\" },\n      \"ring\": { \"$value\": \"{primitive.color.neutral-500}\" },\n      \"input\": { \"$value\": \"{primitive.color.white-alpha-8}\" },\n      \"control\": { \"$value\": \"{primitive.color.neutral-900}\" },\n      \"control-hover\": { \"$value\": \"{primitive.color.neutral-800}\" },\n      \"control-disabled\": { \"$value\": \"{primitive.color.neutral-800}\" },\n      \"invalid\": { \"$value\": \"{primitive.color.red-500-dark}\" },\n      \"invalid-muted\": { \"$value\": \"{primitive.color.neutral-800}\" },\n      \"overlay-backdrop\": { \"$value\": \"{primitive.color.black-alpha-40}\" }\n    }\n  }\n}\n","type":"registry:file","target":"~/src/siteplane/system/tokens/themes/dark.tokens.json"},{"path":"packages/ui/src/system/utils/cn.ts","content":"import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  const merged = twMerge(clsx(inputs));\n  // A part's class (`sp-<slot>`, slot-class.ts) can arrive twice: from the\n  // part itself and from the host that renders it through `render`.\n  return merged.includes(\"sp-\")\n    ? Array.from(new Set(merged.split(\" \"))).join(\" \")\n    : merged;\n}\n\n/**\n * The props of a Base UI part as a kit component takes them: `className` is a\n * string. Base UI also accepts a function of the part's state, but the kit\n * merges its own classes with `cn()` and styles states through data\n * attributes, so every kit component takes the string alone.\n */\nexport type WithStringClassName<Props> = Omit<Props, \"className\"> & {\n  className?: string | undefined;\n};\n","type":"registry:file","target":"~/src/siteplane/system/utils/cn.ts"},{"path":"packages/ui/src/system/utils/slot-class.ts","content":"import * as React from \"react\";\n\nimport { cn } from \"./cn\";\n\n/*\n * Kit stylesheets key their rules on one class per part (rulebook P6):\n * `sp-<slot>` for the part's `data-slot` and `sp-base-<foundation>` for its\n * `data-foundation`. With a class as the last compound of a selector, the\n * browser tries a rule only on the parts it styles. The data attributes stay\n * the public hooks; the classes are internal.\n *\n * A part can end up with another slot than its own: the props spread after it\n * replace its `data-slot` when a composite re-slots a kit part, and so does\n * the element of a `render` prop. These helpers return the class of the value\n * the element ends up with, so the class always follows the data attribute.\n */\n\ntype Props = object | null | undefined;\n\n/** The element of a part's `render` prop, given on its own or in `props`. */\nfunction renderElement(props: Props, render: unknown): unknown {\n  return render === undefined && props && \"render\" in props\n    ? (props as { render?: unknown }).render\n    : render;\n}\n\nfunction finalValue(\n  name: \"data-foundation\" | \"data-slot\",\n  own: string | undefined,\n  props: Props,\n  render: unknown,\n): string | undefined {\n  let value: unknown = own;\n  if (props && name in props) value = (props as Record<string, unknown>)[name];\n  const element = renderElement(props, render);\n  if (React.isValidElement(element)) {\n    const elementProps = element.props as Record<string, unknown> | null;\n    if (elementProps && name in elementProps) value = elementProps[name];\n  }\n  return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\n/**\n * The class for a part, unless the element of its `render` prop carries it\n * already: a kit element that names its own part merges its className with the\n * part's, so the class would appear twice.\n */\nfunction classFor(name: string, props: Props, render: unknown) {\n  const element = renderElement(props, render);\n  if (!React.isValidElement(element)) return name;\n  const className = (element.props as { className?: unknown } | null)\n    ?.className;\n  return typeof className === \"string\" && className.split(/\\s+/).includes(name)\n    ? undefined\n    : name;\n}\n\n/**\n * The class of the slot a part ends up with: `slot`, unless `props` (spread\n * after it) or a `render` element give the part another `data-slot`.\n */\nexport function slotClass(\n  slot: string | undefined,\n  props?: Props,\n  render?: unknown,\n): string | undefined {\n  const value = finalValue(\"data-slot\", slot, props, render);\n  return value === undefined\n    ? undefined\n    : classFor(`sp-${value}`, props, render);\n}\n\n/**\n * The class of the foundation a part ends up with, `sp-base-<foundation>`,\n * by the same rules as `slotClass`.\n */\nexport function baseClass(\n  foundation: string | undefined,\n  props?: Props,\n  render?: unknown,\n): string | undefined {\n  const value = finalValue(\"data-foundation\", foundation, props, render);\n  return value === undefined\n    ? undefined\n    : classFor(`sp-base-${value}`, props, render);\n}\n\n/**\n * The `className` a part is given, without the class of the slot it is given.\n * For a part that does not keep that slot: one that sets its own over it, or\n * one that passes it on to an inner element while its own element takes the\n * className.\n */\nexport function dropSlotClass(\n  className: string | undefined,\n  props?: Props,\n): string | undefined {\n  const given = slotClass(undefined, props);\n  if (!className || given === undefined) return className;\n  return (\n    className\n      .split(/\\s+/)\n      .filter((name) => name !== given)\n      .join(\" \") || undefined\n  );\n}\n\n/**\n * A `className` that a part takes through its props, with the part's classes\n * added: a string, or the function of the part's state that Base UI and React\n * Aria accept.\n */\nexport function withClass<ClassName>(\n  className: ClassName,\n  ...classes: Array<string | undefined>\n): ClassName {\n  if (typeof className === \"function\")\n    return ((state: unknown) =>\n      cn(...classes, className(state)) || undefined) as ClassName;\n  return (cn(...classes, className as string | undefined) ||\n    undefined) as ClassName;\n}\n","type":"registry:file","target":"~/src/siteplane/system/utils/slot-class.ts"},{"path":"packages/ui/src/system/utils/text-runs.tsx","content":"import { Children, type ReactNode } from \"react\";\n\n/**\n * Wraps each run of text children, the strings and numbers between elements,\n * in an element of the caller's, such as the one-line label of a badge, a\n * button or a menu row, so the label can shrink and end with an ellipsis.\n * Elements stay children as they are, so the owner still lays out its icons,\n * shortcuts and other parts. A run of spaces alone stays plain text.\n */\nexport function wrapTextRuns(\n  children: ReactNode,\n  wrap: (text: string, index: number) => ReactNode,\n): ReactNode[] {\n  const parts: ReactNode[] = [];\n  let run: (string | number)[] = [];\n  const flush = () => {\n    const text = run.join(\"\");\n    if (text.trim() !== \"\") parts.push(wrap(text, parts.length));\n    else parts.push(...run);\n    run = [];\n  };\n  for (const child of Children.toArray(children)) {\n    if (typeof child === \"string\" || typeof child === \"number\") {\n      run.push(child);\n    } else {\n      flush();\n      parts.push(child);\n    }\n  }\n  flush();\n  return parts;\n}\n","type":"registry:file","target":"~/src/siteplane/system/utils/text-runs.tsx"},{"path":"packages/ui/src/system/recipes/index.ts","content":"export * from \"./focus-ring.recipe\";\n","type":"registry:file","target":"~/src/siteplane/system/recipes/index.ts"},{"path":"packages/ui/src/system/recipes/focus-ring.recipe.ts","content":"import { cn } from \"../utils/cn\";\n\nexport type FocusRingRecipeOptions = {\n  className?: string;\n  inset?: boolean;\n  variant?: \"outline\" | \"ring\";\n  visible?: boolean;\n};\n\nexport function focusRingRecipe(options: FocusRingRecipeOptions = {}) {\n  return cn(\n    \"sp-focus-ring\",\n    options.inset && \"sp-focus-ring--inset\",\n    options.variant === \"ring\" && \"sp-focus-ring--ring\",\n    options.visible && \"sp-focus-ring--visible\",\n    options.className,\n  );\n}\n","type":"registry:file","target":"~/src/siteplane/system/recipes/focus-ring.recipe.ts"},{"path":"packages/ui/src/system/provider/visual-scope-context.tsx","content":"\"use client\";\n\nimport {\n  createContext,\n  type ReactNode,\n  type RefObject,\n  useContext,\n} from \"react\";\n\nconst VisualScopeContext = createContext<RefObject<HTMLElement | null> | null>(\n  null,\n);\n\nexport function VisualScopeBoundary({\n  children,\n  scopeRef,\n}: {\n  children: ReactNode;\n  scopeRef: RefObject<HTMLElement | null>;\n}) {\n  return (\n    <VisualScopeContext.Provider value={scopeRef}>\n      {children}\n    </VisualScopeContext.Provider>\n  );\n}\n\nexport function useVisualScopeRef() {\n  return useContext(VisualScopeContext);\n}\n","type":"registry:file","target":"~/src/siteplane/system/provider/visual-scope-context.tsx"},{"path":"packages/ui/src/system/provider/siteplane-ui-provider.tsx","content":"\"use client\";\n\nimport { CSPProvider } from \"@base-ui/react/csp-provider\";\nimport * as React from \"react\";\nimport { useSiteplaneMotionPreset } from \"../motion/siteplane-motion-provider\";\nimport { cn } from \"../utils/cn\";\n\nexport type SiteplaneControlSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type SiteplaneDensity = \"compact\" | \"default\";\nexport type SiteplaneRadius = \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nexport interface SiteplaneUIConfig {\n  controlSize: SiteplaneControlSize;\n  density: SiteplaneDensity;\n  radius: SiteplaneRadius;\n}\n\nexport interface SiteplaneUIProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n  children: React.ReactNode;\n  controlSize?: SiteplaneControlSize;\n  \"data-theme\"?: \"dark\" | \"light\";\n  density?: SiteplaneDensity;\n  /**\n   * The nonce of a Content Security Policy that allows inline scripts and\n   * styles only with a nonce. The kit renders no inline script or style; the\n   * provider hands the nonce to React Aria, which adds a style at run time,\n   * through a `<meta property=\"csp-nonce\">` in the head, and to Base UI's\n   * `CSPProvider` for any inline element Base UI renders. An inner provider\n   * without one keeps the outer provider's.\n   */\n  nonce?: string | undefined;\n  /**\n   * The corner radius of every kit component inside: the base `--radius` and\n   * the role radii derived from it (design-rework.css). `md` is the kit's\n   * default, also without a provider.\n   */\n  radius?: SiteplaneRadius;\n}\n\nexport const siteplaneUIDefaults = {\n  controlSize: \"lg\",\n  density: \"default\",\n  radius: \"md\",\n} as const satisfies SiteplaneUIConfig;\n\n/** The base `--radius` of each provider radius, as the kit's stylesheet sets it. */\nexport const siteplaneRadiusValues = {\n  none: \"0rem\",\n  sm: \"0.5rem\",\n  md: \"0.625rem\",\n  lg: \"0.875rem\",\n  xl: \"1.25rem\",\n} as const satisfies Record<SiteplaneRadius, string>;\n\nconst SiteplaneUIContext = React.createContext<SiteplaneUIConfig | null>(null);\nconst SiteplaneNonceContext = React.createContext<string | undefined>(\n  undefined,\n);\nconst SiteplaneUIThemeContext = React.createContext<\n  \"dark\" | \"light\" | undefined\n>(undefined);\n\nexport function resolveControlSize<T extends string>(\n  size: T | null | undefined,\n  contextSize: SiteplaneControlSize,\n): T | SiteplaneControlSize {\n  return size ?? contextSize;\n}\n\nexport function resolveDensity<T extends string>(\n  density: T | null | undefined,\n  contextDensity: SiteplaneDensity,\n): T | SiteplaneDensity {\n  return density ?? contextDensity;\n}\n\nexport function resolveRadius<T extends string>(\n  radius: T | null | undefined,\n  contextRadius: SiteplaneRadius,\n): T | SiteplaneRadius {\n  return radius ?? contextRadius;\n}\n\nexport function useSiteplaneUI(): SiteplaneUIConfig {\n  return React.useContext(SiteplaneUIContext) ?? siteplaneUIDefaults;\n}\n\nexport function useResolvedControlSize<T extends string>(\n  size: T | null | undefined,\n): T | SiteplaneControlSize {\n  const context = useSiteplaneUI();\n  return resolveControlSize(size, context.controlSize);\n}\n\n/** The control sizes from the smallest; only a part that is xs at lg uses xs. */\nconst controlSizeSteps = [\"xs\", \"sm\", \"md\", \"lg\", \"xl\"] as const;\n\n/**\n * The size of a part designed for the default lg, at another control size: it\n * keeps its steps from lg within the scale. A code field one step above lg is\n * md at sm and xl at lg; presets two steps below are sm up to lg and md at xl\n * (Lukas, 13 October 2026). Patterns size their parts this way, so they keep\n * their proportions at every control size and look as designed at lg.\n */\nexport function resolveRelativeControlSize(\n  sizeAtLg: SiteplaneControlSize,\n  controlSize: SiteplaneControlSize,\n): SiteplaneControlSize;\nexport function resolveRelativeControlSize(\n  sizeAtLg: \"xs\" | SiteplaneControlSize,\n  controlSize: SiteplaneControlSize,\n): \"xs\" | SiteplaneControlSize;\nexport function resolveRelativeControlSize(\n  sizeAtLg: \"xs\" | SiteplaneControlSize,\n  controlSize: SiteplaneControlSize,\n): \"xs\" | SiteplaneControlSize {\n  const step =\n    controlSizeSteps.indexOf(controlSize) +\n    controlSizeSteps.indexOf(sizeAtLg) -\n    controlSizeSteps.indexOf(\"lg\");\n  const lowest = sizeAtLg === \"xs\" ? 0 : 1;\n  return controlSizeSteps[\n    Math.min(controlSizeSteps.length - 1, Math.max(lowest, step))\n  ]!;\n}\n\n/** `resolveRelativeControlSize` at the provider's control size. */\nexport function useRelativeControlSize(\n  sizeAtLg: SiteplaneControlSize,\n): SiteplaneControlSize;\nexport function useRelativeControlSize(\n  sizeAtLg: \"xs\" | SiteplaneControlSize,\n): \"xs\" | SiteplaneControlSize;\nexport function useRelativeControlSize(\n  sizeAtLg: \"xs\" | SiteplaneControlSize,\n): \"xs\" | SiteplaneControlSize {\n  return resolveRelativeControlSize(sizeAtLg, useSiteplaneUI().controlSize);\n}\n\n/** The provider's CSP nonce, for a kit part that hands it to a library that adds an inline style. */\nexport function useSiteplaneNonce(): string | undefined {\n  return React.useContext(SiteplaneNonceContext);\n}\n\nexport function useResolvedDensity<T extends string>(\n  density: T | null | undefined,\n): T | SiteplaneDensity {\n  const context = useSiteplaneUI();\n  return resolveDensity(density, context.density);\n}\n\nexport function useResolvedRadius<T extends string>(\n  radius: T | null | undefined,\n): T | SiteplaneRadius {\n  const context = useSiteplaneUI();\n  return resolveRadius(radius, context.radius);\n}\n\n// A provider and its portal scopes add no box of their own (`display:\n// contents` in design-rework.css, so a policy that blocks style attributes\n// leaves the layout alone). Their radius reaches the kit through data-radius:\n// the stylesheet sets the base for it and derives every corner again on the\n// scope, so a portal follows its provider.\nexport function SiteplaneUIPortalScope({\n  children,\n}: {\n  children: React.ReactNode;\n}): React.ReactElement {\n  const value = useSiteplaneUI();\n  const theme = React.useContext(SiteplaneUIThemeContext);\n  const motionPreset = useSiteplaneMotionPreset();\n\n  return (\n    <div\n      data-control-size={value.controlSize}\n      data-density={value.density}\n      data-radius={value.radius}\n      className=\"sp-siteplane-ui-portal-scope\"\n      data-slot=\"siteplane-ui-portal-scope\"\n      data-sp-motion={motionPreset}\n      data-theme={theme}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function SiteplaneUIProvider({\n  children,\n  controlSize,\n  density,\n  nonce,\n  radius,\n  \"data-theme\": theme,\n  ...props\n}: SiteplaneUIProviderProps): React.ReactElement {\n  const parent = useSiteplaneUI();\n  const parentNonce = React.useContext(SiteplaneNonceContext);\n  const parentTheme = React.useContext(SiteplaneUIThemeContext);\n  const resolvedTheme =\n    theme === \"dark\" || theme === \"light\" ? theme : parentTheme;\n  // An empty nonce is none.\n  const ownNonce = nonce || undefined;\n  const resolvedNonce = ownNonce ?? parentNonce;\n  const value = React.useMemo<SiteplaneUIConfig>(\n    () => ({\n      controlSize: controlSize ?? parent.controlSize,\n      density: density ?? parent.density,\n      radius: radius ?? parent.radius,\n    }),\n    [\n      controlSize,\n      density,\n      radius,\n      parent.controlSize,\n      parent.density,\n      parent.radius,\n    ],\n  );\n  return (\n    <SiteplaneUIContext.Provider value={value}>\n      <SiteplaneUIThemeContext.Provider value={resolvedTheme}>\n        <SiteplaneNonceContext.Provider value={resolvedNonce}>\n          <div\n            {...props}\n            className={cn(\"sp-siteplane-ui-provider\", props.className)}\n            data-control-size={value.controlSize}\n            data-density={value.density}\n            data-radius={value.radius}\n            data-slot=\"siteplane-ui-provider\"\n            data-theme={theme}\n          >\n            {/* React Aria reads the nonce for the styles it adds at run time\n                from this element, which React moves into the head; Vite's\n                html.cspNonce uses the same convention. Browsers hide a nonce\n                attribute once a policy applies, and React finds a hoisted meta\n                by its other attributes, so hydration keeps it. Only the\n                provider that brings a nonce renders it. */}\n            {ownNonce && ownNonce !== parentNonce ? (\n              <meta\n                nonce={ownNonce}\n                property=\"csp-nonce\"\n                suppressHydrationWarning\n              />\n            ) : null}\n            {/* Base UI renders no <style> of its own: the kit stylesheet holds\n                its scrollbar rules (design-rework.css). Any inline element it\n                still renders takes the nonce. */}\n            <CSPProvider disableStyleElements nonce={resolvedNonce}>\n              {children}\n            </CSPProvider>\n          </div>\n        </SiteplaneNonceContext.Provider>\n      </SiteplaneUIThemeContext.Provider>\n    </SiteplaneUIContext.Provider>\n  );\n}\n","type":"registry:file","target":"~/src/siteplane/system/provider/siteplane-ui-provider.tsx"},{"path":"packages/ui/src/system/motion/siteplane-motion-provider.tsx","content":"\"use client\";\n\nimport {\n  createContext,\n  type HTMLAttributes,\n  type ReactNode,\n  useContext,\n  useRef,\n} from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { VisualScopeBoundary } from \"../provider/visual-scope-context\";\n\nexport type SiteplaneMotionPreset = \"standard\" | \"reduced\";\n\nconst SiteplaneMotionContext = createContext<SiteplaneMotionPreset | undefined>(\n  undefined,\n);\n\nexport type SiteplaneMotionProviderProps = HTMLAttributes<HTMLDivElement> & {\n  children: ReactNode;\n  preset: SiteplaneMotionPreset;\n};\n\nexport function SiteplaneMotionProvider({\n  children,\n  className,\n  preset,\n  ...props\n}: SiteplaneMotionProviderProps) {\n  const scopeRef = useRef<HTMLDivElement>(null);\n\n  return (\n    <SiteplaneMotionContext.Provider value={preset}>\n      <VisualScopeBoundary scopeRef={scopeRef}>\n        <div\n          {...props}\n          className={cn(\"sp-motion-scope\", className)}\n          data-sp-motion={preset}\n          data-sp-scope=\"motion\"\n          ref={scopeRef}\n        >\n          {children}\n        </div>\n      </VisualScopeBoundary>\n    </SiteplaneMotionContext.Provider>\n  );\n}\n\nexport function useSiteplaneMotionPreset() {\n  return useContext(SiteplaneMotionContext);\n}\n","type":"registry:file","target":"~/src/siteplane/system/motion/siteplane-motion-provider.tsx"},{"path":"packages/ui/src/system/motion/use-reduced-motion-preference.ts","content":"\"use client\";\n\nimport * as React from \"react\";\n\nimport { useSiteplaneMotionPreset } from \"./siteplane-motion-provider\";\n\nconst systemQuery = \"(prefers-reduced-motion: reduce)\";\n\n/**\n * Reduced motion as the kit understands it: the system preference or\n * `html[data-motion-reduce=\"true\"]`. `SiteplaneMotionProvider preset=\"reduced\"`\n * is added by the hook, because the provider is context, not a document state.\n * Code that measures or moves an element outside React's render, such as the\n * flip of a popover, passes that element: inside the reduced preset, whose\n * scope a portal carries along, it reads as reduced too.\n */\nexport function readReducedMotion(element?: Element | null): boolean {\n  if (typeof document === \"undefined\") return false;\n  return (\n    document.documentElement.dataset.motionReduce === \"true\" ||\n    (typeof window.matchMedia === \"function\" &&\n      window.matchMedia(systemQuery).matches) ||\n    Boolean(element?.closest('[data-sp-motion=\"reduced\"]'))\n  );\n}\n\nfunction subscribe(onChange: () => void): () => void {\n  const cleanups: Array<() => void> = [];\n  if (\n    typeof window !== \"undefined\" &&\n    typeof window.matchMedia === \"function\"\n  ) {\n    const query = window.matchMedia(systemQuery);\n    query.addEventListener(\"change\", onChange);\n    cleanups.push(() => query.removeEventListener(\"change\", onChange));\n  }\n  if (typeof MutationObserver !== \"undefined\") {\n    const observer = new MutationObserver(onChange);\n    observer.observe(document.documentElement, {\n      attributeFilter: [\"data-motion-reduce\"],\n      attributes: true,\n    });\n    cleanups.push(() => observer.disconnect());\n  }\n  return () => {\n    for (const cleanup of cleanups) cleanup();\n  };\n}\n\n/**\n * Whether motion is reduced right now (rulebook M5): the system preference,\n * `html[data-motion-reduce=\"true\"]` or `SiteplaneMotionProvider preset=\"reduced\"`.\n *\n * The server and the hydration pass report `false` unless the provider asks for\n * the reduced preset, so the markup matches what the server rendered; the real\n * value follows in the next render. Markup that depends on it is hydration-safe\n * without any effort of its own, unlike `useReducedMotion()` from `motion/react`,\n * which already returns the client value while hydrating.\n */\nexport function useReducedMotionPreference(): boolean {\n  const preset = useSiteplaneMotionPreset();\n  const reduced = React.useSyncExternalStore(\n    subscribe,\n    readReducedMotion,\n    () => false,\n  );\n  return reduced || preset === \"reduced\";\n}\n","type":"registry:file","target":"~/src/siteplane/system/motion/use-reduced-motion-preference.ts"},{"path":"packages/ui/src/system/form/use-form-reset.ts","content":"import { useEffect, useLayoutEffect, useRef } from \"react\";\n\nexport function useFormReset<T extends HTMLElement>(\n  formId: string | undefined,\n  onReset: () => void,\n) {\n  const elementRef = useRef<T>(null);\n  const resetRef = useRef(onReset);\n  // Refs aren't written during render; the latest callback is stored when the\n  // render commits, before a later reset event can read it.\n  useLayoutEffect(() => {\n    resetRef.current = onReset;\n  });\n\n  useEffect(() => {\n    const associatedForm = formId\n      ? document.getElementById(formId)\n      : elementRef.current?.closest(\"form\");\n\n    if (!(associatedForm instanceof HTMLFormElement)) return;\n\n    const handleReset = (event: Event) =>\n      queueMicrotask(() => {\n        if (!event.defaultPrevented) resetRef.current();\n      });\n    associatedForm.addEventListener(\"reset\", handleReset);\n    return () => associatedForm.removeEventListener(\"reset\", handleReset);\n  }, [formId]);\n\n  return elementRef;\n}\n","type":"registry:file","target":"~/src/siteplane/system/form/use-form-reset.ts"},{"path":"packages/ui/src/system/form/aria-invalid.ts","content":"import type React from \"react\";\n\n/**\n * Whether an `aria-invalid` value marks the control invalid, for the React\n * Aria controls that take a boolean `isInvalid` instead. `true`, `\"true\"`,\n * `\"grammar\"` and `\"spelling\"` do; `false`, `\"false\"` and no value do not.\n */\nexport function isInvalidAriaValue(\n  value: React.AriaAttributes[\"aria-invalid\"],\n): boolean {\n  return (\n    value === true ||\n    value === \"true\" ||\n    value === \"grammar\" ||\n    value === \"spelling\"\n  );\n}\n","type":"registry:file","target":"~/src/siteplane/system/form/aria-invalid.ts"},{"path":"packages/ui/src/system/date/native-day.ts","content":"/**\n * The native Date, at local midnight, of a calendar day of a React Aria date\n * or date-time value, as the Calendar and the date and time controls report\n * it. `new Date(year, monthIndex, day)` reads the years 0 to 99 as 1900 to\n * 1999, so a year typed digit by digit into a controlled field jumped (0020\n * became 1920); `setFullYear` keeps every year as it is.\n */\nexport function toNativeDay(value: {\n  day: number;\n  month: number;\n  year: number;\n}): Date {\n  const date = new Date(2000, 0, 1);\n  date.setFullYear(value.year, value.month - 1, value.day);\n  return date;\n}\n\n/**\n * The time of the local midnight that starts the day of `date`, for comparing\n * days whatever their time. It keeps every year, as `toNativeDay` does.\n */\nexport function nativeDayTime(date: Date): number {\n  return toNativeDay({\n    day: date.getDate(),\n    month: date.getMonth() + 1,\n    year: date.getFullYear(),\n  }).getTime();\n}\n","type":"registry:file","target":"~/src/siteplane/system/date/native-day.ts"},{"path":"packages/ui/src/system/locale/formatted-text.ts","content":"/**\n * The spaces that servers and browsers write differently in formatted text.\n * ICU 72 and later put a narrow no-break space (U+202F) before AM and PM and a\n * thin space (U+2009) around the dash of a date range, and older and newer ICU\n * data disagree on the no-break space (U+00A0) of other formats. Node writes\n * them, while Chrome writes a plain space in their place, so a page that the\n * server renders holds other text than the browser renders, and React rejects\n * the hydration (error #418). The class is built from the three code points,\n * so the source shows which characters it holds.\n */\nconst unstableSpaces = new RegExp(\n  `[${String.fromCodePoint(0x00a0, 0x2009, 0x202f)}]`,\n  \"g\",\n);\n\n/**\n * Text that `Intl` formatted, with each space that ICU versions disagree on\n * written as a plain space, so the server and the browser render the same\n * text. The kit's formatted text goes through it: the segments of the Time\n * Field and the Date Time Picker, which React Aria builds with\n * `formatToParts`, the hour names of the time selector, the calendar headings,\n * the Date Picker and Task Calendar dates, and the numbers and dates of the\n * charts.\n */\nexport function normalizeFormattedText(text: string): string {\n  return text.replace(unstableSpaces, \" \");\n}\n","type":"registry:file","target":"~/src/siteplane/system/locale/formatted-text.ts"},{"path":"packages/ui/src/system/hooks/use-media-query.ts","content":"\"use client\";\n\nimport {\n  type RefObject,\n  useCallback,\n  useLayoutEffect,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\n\n// The kit's media utilities (sm:, md:, lg:, ...) use Tailwind's default\n// breakpoints, so the JS table names the same widths: a component that switches\n// in JS (Sidebar's sheet, Header Navigation's menu) and its CSS switch at the\n// same width.\nconst BREAKPOINTS = {\n  \"2xl\": 1536,\n  \"3xl\": 1600,\n  \"4xl\": 2000,\n  lg: 1024,\n  md: 768,\n  sm: 640,\n  xl: 1280,\n} as const;\n\ntype Breakpoint = keyof typeof BREAKPOINTS;\n\ntype BreakpointQuery =\n  Breakpoint | `max-${Breakpoint}` | `${Breakpoint}:max-${Breakpoint}`;\n\nfunction resolveMin(value: Breakpoint | number): string {\n  const px = typeof value === \"number\" ? value : BREAKPOINTS[value];\n  return `(min-width: ${px}px)`;\n}\n\nfunction resolveMax(value: Breakpoint | number): string {\n  const px = typeof value === \"number\" ? value : BREAKPOINTS[value];\n  return `(max-width: ${px - 1}px)`;\n}\n\nfunction parseQuery(\n  query: BreakpointQuery | MediaQueryInput | (string & {}),\n): string {\n  if (typeof query !== \"string\") {\n    const parts: string[] = [];\n    if (query.min != null) parts.push(resolveMin(query.min));\n    if (query.max != null) parts.push(resolveMax(query.max));\n    if (query.pointer === \"coarse\") parts.push(\"(pointer: coarse)\");\n    if (query.pointer === \"fine\") parts.push(\"(pointer: fine)\");\n    if (parts.length === 0) return \"(min-width: 0px)\";\n    return parts.join(\" and \");\n  }\n\n  if (query.startsWith(\"(\")) return query;\n\n  const parts: string[] = [];\n  for (const segment of query.split(\":\")) {\n    if (segment.startsWith(\"max-\")) {\n      const bp = segment.slice(4);\n      if (bp in BREAKPOINTS) parts.push(resolveMax(bp as Breakpoint));\n    } else if (segment in BREAKPOINTS) {\n      parts.push(resolveMin(segment as Breakpoint));\n    }\n  }\n\n  return parts.length > 0 ? parts.join(\" and \") : query;\n}\n\nfunction getServerSnapshot(): boolean {\n  return false;\n}\n\nexport type MediaQueryInput = {\n  min?: Breakpoint | number;\n  max?: Breakpoint | number;\n  /** Touch-like input (finger). Use \"fine\" for mouse/trackpad. */\n  pointer?: \"coarse\" | \"fine\";\n};\n\nexport function useMediaQuery(\n  query: BreakpointQuery | MediaQueryInput | (string & {}),\n): boolean {\n  const mediaQuery = parseQuery(query);\n\n  const subscribe = useCallback(\n    (callback: () => void) => {\n      if (typeof window === \"undefined\") return () => {};\n      const mql = window.matchMedia(mediaQuery);\n      mql.addEventListener(\"change\", callback);\n      return () => mql.removeEventListener(\"change\", callback);\n    },\n    [mediaQuery],\n  );\n\n  const getSnapshot = useCallback(() => {\n    if (typeof window === \"undefined\") return false;\n    return window.matchMedia(mediaQuery).matches;\n  }, [mediaQuery]);\n\n  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\nexport function useIsMobile(): boolean {\n  return useMediaQuery(\"max-md\");\n}\n\n/**\n * Whether the element in `ref` is narrower than a breakpoint (`max-lg`) or at\n * least that wide (`lg`): the container query of a component that switches in\n * JS at its own width, as Header Navigation does, under the names its\n * container query utilities use. The server and the first render, before the\n * element is measured, get false. The element is measured before the first\n * paint and again whenever its width changes; it renders together with the\n * component that asks, as Header Navigation's row does.\n */\nexport function useContainerQuery(\n  ref: RefObject<Element | null>,\n  query: Breakpoint | `max-${Breakpoint}`,\n): boolean {\n  const [matches, setMatches] = useState(false);\n  useLayoutEffect(() => {\n    const element = ref.current;\n    if (!element) return;\n    const max = query.startsWith(\"max-\");\n    const px = BREAKPOINTS[(max ? query.slice(4) : query) as Breakpoint];\n    const measure = (width: number) =>\n      setMatches(max ? width < px : width >= px);\n    measure(element.getBoundingClientRect().width);\n    if (typeof ResizeObserver === \"undefined\") return;\n    // A change of width is applied in the next frame, outside the observer's\n    // callback, so a component that swaps its content never loops it.\n    let frame = 0;\n    const observer = new ResizeObserver(([entry]) => {\n      if (!entry) return;\n      const width =\n        entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;\n      cancelAnimationFrame(frame);\n      frame = requestAnimationFrame(() => measure(width));\n    });\n    observer.observe(element);\n    return () => {\n      cancelAnimationFrame(frame);\n      observer.disconnect();\n    };\n  }, [ref, query]);\n  return matches;\n}\n","type":"registry:file","target":"~/src/siteplane/system/hooks/use-media-query.ts"},{"path":"packages/ui/src/system/hooks/use-truncated.ts","content":"\"use client\";\n\nimport { useCallback, useLayoutEffect, useRef, useState } from \"react\";\n\n/**\n * Whether an element shows only part of its content: the content is wider or\n * taller than the element's box, so an ellipsis or a line clamp cuts it. One\n * pixel of slack absorbs the rounding of fractional sizes.\n */\nexport function isTruncated(element: Element): boolean {\n  return (\n    element.scrollHeight > element.clientHeight + 1 ||\n    element.scrollWidth > element.clientWidth + 1\n  );\n}\n\n/** Told whether its element is cut, with the element. */\nexport type TruncationListener = (truncated: boolean, element: Element) => void;\n\n/*\n * One ResizeObserver serves every element the kit watches for a cut, such as a\n * Button label or a Truncate. It reads an element after the element's first\n * layout and whenever its size changes.\n */\nlet observer: ResizeObserver | null | undefined;\nconst listeners = new WeakMap<Element, TruncationListener>();\n\n/**\n * Tells `listener` whether `element` is cut, after the element's first layout\n * and whenever its size changes, until the returned function stops it. Where\n * the browser has no ResizeObserver it tells nothing.\n */\nexport function observeTruncation(\n  element: Element,\n  listener: TruncationListener,\n): () => void {\n  if (observer === undefined)\n    observer =\n      typeof ResizeObserver === \"undefined\"\n        ? null\n        : new ResizeObserver((entries) => {\n            for (const { target } of entries)\n              listeners.get(target)?.(isTruncated(target), target);\n          });\n  if (!observer) return () => {};\n  listeners.set(element, listener);\n  observer.observe(element);\n  return () => {\n    observer?.unobserve(element);\n    listeners.delete(element);\n  };\n}\n\n/**\n * Whether the element of the returned ref is cut, for a part that shows its\n * full text only then, such as the tooltip of a Truncate. It is false on the\n * server and in the first render in the browser, so both render the same. The\n * element is measured before the first paint, after every render, since new\n * content can cut it without changing its size, and whenever its size changes.\n * With `enabled` false it measures nothing and stays false.\n */\nexport function useTruncated<T extends Element>(\n  enabled = true,\n): [ref: (element: T | null) => (() => void) | void, truncated: boolean] {\n  const [truncated, setTruncated] = useState(false);\n  const elementRef = useRef<T | null>(null);\n  const ref = useCallback(\n    (element: T | null): (() => void) | void => {\n      if (!element || !enabled) return;\n      elementRef.current = element;\n      const stop = observeTruncation(element, (cut) => setTruncated(cut));\n      return () => {\n        stop();\n        elementRef.current = null;\n      };\n    },\n    [enabled],\n  );\n  useLayoutEffect(() => {\n    const element = elementRef.current;\n    if (element) setTruncated(isTruncated(element));\n  });\n  return [ref, enabled && truncated];\n}\n","type":"registry:file","target":"~/src/siteplane/system/hooks/use-truncated.ts"},{"path":"LICENSE","content":"Siteplane UI License\n\nCopyright (c) 2026 Lukas van Uden (Siteplane UI, https://ui.siteplane.io)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to use,\ncopy, modify, merge, publish, distribute, sublicense and sell copies of the\nSoftware, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\n1. The above copyright notice and this permission notice shall be included in\n   all copies or substantial portions of the Software.\n\n2. You may charge for the Software, original or modified, only as part of an\n   end product: an application or website whose purpose is not to supply user\n   interface source to others. You may not charge for a UI kit, a component,\n   block or pattern library, a design system, a template or a page builder\n   that contains the Software or is built on it. This also applies to versions\n   of the Software that are reproduced, translated or rewritten, with AI tools\n   or by any other means.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","type":"registry:file","target":"~/LICENSE.siteplane-ui"},{"path":"TRADEMARKS.md","content":"# Siteplane UI Trademark Policy\n\nThe Siteplane UI source code is licensed under the Siteplane UI License (`LICENSE`). The license does not grant permission to use the Siteplane UI name, wordmark or official logo as branding for another product or distribution.\n\nTruthful references to Siteplane UI and links to the official repository are allowed when they do not imply sponsorship, endorsement or official status. Forks and derivatives must use distinct branding unless prior permission has been granted.\n\nThis policy does not limit rights already provided by applicable trademark law.\n","type":"registry:file","target":"~/TRADEMARKS.siteplane-ui.md"}],"type":"registry:base"}