{"version":3,"file":"web-CYByxIZf.mjs","names":["useEffect","defineScriptState","InputModalityTracker","$","_c","t0","Symbol","for","_temp","trackInputModality","document","doc","Document","root","documentElement","marked","Element","sync","target","MODALITY","read","POINTER","focusedElement","removeAttribute","POINTER_FOCUS_ATTR","setAttribute","onPointer","write","onKey","e","KeyboardEvent","NAVIGATION_KEYS","has","key","undefined","onFocusOut","addEventListener","removeEventListener","el","activeElement","shadowRoot","body","Set","isPointerModality","_c","_temp","createContext","useContext","ReactNode","ControlSize","DensityControlled","size","ButtonIconSize","ControlSizeContext","ControlSizeProvider","t0","$","_c","children","t1","useControlSize","iconSizeFor","textSizeFor","textStepFor","density","buttonTextClassFor","fieldSizeClassFor","textareaSizeClassFor","_c","createContext","useContext","ReactNode","SingleLineContext","SingleLineProvider","t0","$","_c","value","children","t1","useSingleLine","_c","createContext","useContext","ReactNode","SidewaysScrollContext","SidewaysScrollProvider","t0","$","_c","value","children","t1","useSidewaysScroll","_c","createContext","useContext","ReactNode","MenuSurfaceContext","MenuSurfaceProvider","t0","$","_c","value","children","t1","useMenuSurface","_c","createContext","useContext","useMemo","Provider","ReactNode","PortalForwardedAttrs","Record","ForwardedBags","region","popup","FORWARD_SCOPE","PortalForwardScopeValue","PortalForwardContext","usePortalForwardedAttrs","useRegionForwardedAttrs","ForwardReach","usePortalForward","name","value","t0","$","_c","reach","undefined","parent","t1","bb0","t2","t3","t4","PortalForwardScope","PortalForwardProvider","children","merged","_c","useLayoutEffect","useState","useResizeObserver","useEventCallback","ScrollFade","HTMLElement","measureRef","node","T","onScroll","top","bottom","useScrollFade","$","_c","setNode","t0","Symbol","for","state","setState","t1","scrollTop","clientHeight","scrollHeight","prev","measure","t2","t3","deps","t4","_c","ReactNode","PortalForwardProvider","usePortalForwardedAttrs","usePluginScopeId","bareThemeScope","isSubThemeScope","qualifyThemeScope","THEME_SCOPE_ATTR","usePortalThemeScope","$","_c","pluginScope","forwarded","t0","undefined","PortalThemeScopeProvider","scope","popupsOnly","t1","children","reach","t2","stamped","t3","_c","useCallback","React","cn","SURFACE_LEVELS","POPOVER_WIDTH","POPOVER_PADDING","POPOVER_MAX_HEIGHT","PopoverWidth","PopoverPadding","PopoverMaxHeight","SingleLineProvider","SidewaysScrollProvider","scopeSelectAllKeyDown","useRailGuard","OverlayBoundary","useScrollFade","Passthrough","IconScopeProvider","usePortalThemeScope","closestOverlayPanel","el","Element","HTMLElement","closest","HEADER_PIN_STYLE","top","FOOTER_PIN_STYLE","bottom","OverlayPanelProps","HTMLDivElement","width","padding","maxHeight","scroll","header","ReactNode","footer","rows","className","children","onKeyDown","KeyboardEventHandler","onScroll","UIEventHandler","OverlayPanel","t0","$","_c","ref","rest","t1","t2","t3","t4","t5","undefined","measureRef","measureScroll","railRef","t6","current","panelRef","t7","e","handleKeyDown","t8","e_0","handleScroll","popupScope","t9","t10","t11","t12","t13","t14","t15","overlay","t16","t17","t18","t19","t20","_c","useState","usePinOnClick","$","_c","pinned","setPinned","hovered","setHovered","focused","setFocused","t0","t1","Symbol","for","open","t2","reason","onOpenChange","t3","t4","t5","t6","closeOnClick","onClick","onPointerEnter","onPointerLeave","t7","root","trigger","_c","c","_c","mergeProps","cloneElement","useLayoutEffect","useMemo","useState","HTMLAttributes","ReactElement","ReactNode","Ref","RefCallback","SyntheticEvent","flushSync","TriggerProps","Record","ref","Element","RenderDeferredTrigger","props","IntentEvent","Snapshot","attrs","handlers","snapshotOf","key","value","Object","entries","push","sort","sameSnapshot","a","b","join","keys","length","every","is","attachRef","node","cleanup","current","elementRef","element","TriggerBridge","latest","attached","detach","pending","name","event","constructor","arm","setSnapshot","update","prev","syncTriggerRef","el","refFor","childRef","detachChild","intent","create","currentTarget","onPointerEnter","onFocus","delegates","names","e","out","handler","undefined","publish","stillFocused","ownerDocument","activeElement","next","render","DeferredTrigger","t0","$","children","mount","armed","setArmed","snapshot","t1","Symbol","for","bridge","t2","t3","t4","t5","own","t6","t7","PublishTriggerProps","_c","_Fragment","c","_c","Tooltip","TooltipPrimitive","ReactElement","ReactNode","cn","usePortalForwardedAttrs","OverlayBoundary","usePortalContainer","OverlayPanel","usePinOnClick","DeferredTrigger","RenderDeferredTrigger","TooltipTypeRole","TOOLTIP_ROLE_CLASS","Record","display","title","heading","subheading","body","label","group","caption","control","tag","code","TOOLTIP_TYPE","Object","values","join","TooltipProvider","t0","$","props","t1","delay","undefined","t2","TooltipTrigger","TooltipContent","children","className","t3","t4","side","sideOffset","align","alignOffset","forwarded","container","t5","t6","t7","Symbol","for","t8","t9","t10","TooltipCardContent","anchor","wide","TooltipSide","TooltipAlign","Tooltipped","tooltip","tooltipSide","tooltipAlign","hasTooltip","content","WithTooltipProps","pinOnClick","tooltipAttr","WithTooltip","attr","render","PinnableTooltip","pin","trigger","root","_c","TooltipPrimitive","Tooltip","Button","ButtonPrimitive","cva","VariantProps","useEffect","useRef","useState","cn","WithTooltip","Tooltipped","useControlSize","textSizeFor","iconSizeFor","buttonTextClassFor","ControlSize","DensityControlled","symbol","Icon","refreshIcon","buttonVariants","variants","variant","default","outline","secondary","ghost","destructive","dashed","frame","floating","link","size","md","xs","sm","lg","icon","inline","shape","pill","defaultVariants","ButtonOwnProps","Omit","Props","aspect","loading","t0","$","_c","children","className","onClick","props","t1","t2","t3","t4","t5","tooltip","tooltipAlign","tooltipSide","disabled","undefined","density","resolvedSize","textClass","autoPending","setAutoPending","mounted","t6","t7","Symbol","for","current","t8","event","result","then","Promise","resolve","finally","handleClick","isLoading","iconOnly","startsWith","ariaLabel","T0","T1","t9","t10","t11","t12","t13","t14","t15","t16","Button","_c","ButtonPrimitive","_Fragment","ComponentProps","cn","ButtonGroup","t0","$","_c","children","className","props","t1","shape","undefined","t2","t3","t4","t5","_c","React","MenuSurfaceProvider","Dialog","DialogPrimitive","cn","Button","usePortalForwardedAttrs","OverlayPanel","usePortalContainer","symbol","Icon","closeIcon","t0","$","_c","props","t1","DialogTrigger","DialogClose","DialogPortal","container","DialogOverlay","className","t2","DIALOG_SIZES","sm","md","lg","const","DIALOG_TOP","DIALOG_BOTTOM_GAP","DialogContentProps","Omit","Popup","Props","size","showCloseButton","DialogContent","children","undefined","forwarded","t3","Symbol","for","t4","t5","t6","t7","t8","t9","t10","CSSProperties","DialogTitle","DialogDescription","closeIcon","Dialog","_c","DialogPrimitive","Button","React","useReportPopupOpen","usePopupOpenMirror","t0","$","_c","open","defaultOpen","onOpenChange","mirrored","setMirrored","useState","isOpen","t1","nextOpen","t2","rest","_c","React","useFrameFocusDismiss","open","$","_c","actionsRef","useRef","t0","t1","onBlur","document","activeElement","HTMLIFrameElement","current","close","window","addEventListener","removeEventListener","useEffect","_c","React","MenuSurfaceProvider","Menu","MenuPrimitive","cn","usePortalForwardedAttrs","usePopupOpenMirror","useFrameFocusDismiss","OverlayPanel","PopoverWidth","PopoverPadding","PopoverMaxHeight","MENU_LABEL","MENU_ROW","MENU_ROW_TRAIL","MENU_SEPARATOR","MENU_VALUE","ControlSizeProvider","Button","usePortalContainer","symbol","IconRef","Icon","LinkTarget","activationProps","chevronRightIcon","checkIcon","DropdownMenu","t0","$","_c","defaultOpen","onOpenChange","open","props","t1","handleOpenChange","isOpen","actionsRef","t2","DropdownMenuPortal","container","DropdownMenuTrigger","DropdownMenuContent","children","className","header","t3","t4","t5","t6","t7","align","alignOffset","side","sideOffset","width","padding","maxHeight","undefined","forwarded","t8","t9","t10","t11","DropdownMenuGroup","DropdownMenuLabel","inset","DropdownMenuSection","action","label","MenuSectionAction","menuRowActionProps","isolate","Symbol","for","icon","MenuRowAction","onAction","e","MouseEvent","activation","onClick","stopPropagation","MenuRowTrailProps","shortcut","MenuRowTrailParts","DropdownMenuItem","variant","DropdownMenuSub","DropdownMenuSubTrigger","DropdownMenuSubContent","DropdownMenuCheckboxItem","checked","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuShortcut","checkIcon","_c","MenuPrimitive","Button","_Fragment","React","Input","InputPrimitive","cn","fieldSizeClassFor","useControlSize","DensityControlled","fieldChromeClass","t0","$","_c","className","props","t1","type","outline","undefined","density","t2","t3","Input","_c","InputPrimitive","React","cn","textareaSizeClassFor","useControlSize","DensityControlled","fieldChromeClass","Textarea","t0","$","_c","className","props","t1","fitContent","undefined","density","t2","t3","t4","_c","Popover","PopoverPrimitive","useMemo","RefObject","defineReportSink","PopoverAnchorLostFault","cause","anchor","Element","hiddenBy","positioner","popoverAnchorLostSink","SteadyAnchor","getBoundingClientRect","DOMRect","contextElement","boxCause","el","isConnected","getClientRects","length","displayNoneAncestor","cur","parentElement","getComputedStyle","display","steadyAnchor","target","fallback","positionerRef","resolve","current","lastGood","lost","undefined","emit","PositionerAnchor","Positioner","Props","isElement","value","useSteadyAnchor","trigger","$","_c","t0","bb0","t1","resolved","_c","React","MenuSurfaceProvider","Popover","PopoverPrimitive","usePortalForwardedAttrs","usePopupOpenMirror","useFrameFocusDismiss","OverlayPanel","PopoverWidth","PopoverPadding","PopoverMaxHeight","usePortalContainer","usePopupAnchorAtRest","useSteadyAnchor","PopoverEverOpenedContext","createContext","PopoverHoldContext","PopoverTriggerContext","Element","SetPopoverTriggerContext","el","useHoldPopoverOpen","active","$","_c","hold","useContext","t0","t1","useEffect","defaultOpen","onOpenChange","open","props","handleOpenChange","isOpen","anchorAtRest","drawn","setDrawn","useState","actionsRef","everOpened","setEverOpened","holds","useRef","t2","Symbol","for","current","t3","next","details","cancel","guardedOpenChange","trigger","setTrigger","t4","t5","t6","PopoverTrigger","ref","detach","_temp","cleanup","triggerRef","PopoverContent","anchor","children","className","collisionAvoidance","footer","header","rows","t7","t8","align","alignOffset","side","sideOffset","width","padding","maxHeight","resetOnClose","undefined","forwarded","container","positionerRef","steadyAnchor","t9","t10","fallbackAxisSide","t11","t12","t13","t14","_c","Popover","PopoverPrimitive","_temp","ResizablePrimitive","cn","ResizablePanelGroup","t0","$","_c","className","props","t1","t2","ResizablePanel","ResizableHandle","withHandle","t3","_c","ScrollArea","ScrollAreaPrimitive","cn","t0","$","_c","children","className","props","t1","t2","t3","t4","Symbol","for","t5","ScrollBar","orientation","undefined","ScrollArea","_c","ScrollAreaPrimitive","React","MenuSurfaceProvider","Select","SelectPrimitive","MENU_LABEL","MENU_ROW","MENU_SEPARATOR","cn","usePortalForwardedAttrs","usePopupOpenMirror","OverlayPanel","PopoverWidth","PopoverPadding","PopoverMaxHeight","fieldSizeClassFor","useControlSize","DensityControlled","usePortalContainer","symbol","Icon","keyboardArrowDownIcon","checkIcon","keyboardArrowUpIcon","t0","$","_c","defaultOpen","onOpenChange","open","props","t1","handleOpenChange","t2","SelectGroup","className","SelectValue","SelectTrigger","children","density","compact","Symbol","for","t3","SelectContent","header","t4","t5","t6","t7","t8","side","sideOffset","align","alignOffset","alignItemWithTrigger","width","padding","maxHeight","undefined","forwarded","container","t9","t10","t11","t12","t13","t14","t15","t16","t17","SelectLabel","SelectItem","SelectSeparator","SelectScrollUpButton","SelectScrollDownButton","Select","_c","SelectPrimitive","Separator","SeparatorPrimitive","cn","PlainSeparatorProps","Props","label","bleed","Omit","orientation","LabelledSeparatorProps","React","ComponentPropsWithoutRef","SeparatorProps","props","$","_c","undefined","className","rest","t0","t1","_orientation","t2","Symbol","for","t3","t4","className_0","rest_0","_label","t5","Separator","_c","SeparatorPrimitive","React","MenuSurfaceProvider","Dialog","SheetPrimitive","cn","usePortalForwardedAttrs","ContentScope","SingleLineProvider","Button","usePortalContainer","symbol","Icon","closeIcon","Sheet","t0","$","_c","props","t1","SheetTrigger","SheetClose","SheetPortal","container","SheetOverlay","className","t2","SheetContent","children","side","showCloseButton","undefined","forwarded","t3","Symbol","for","t4","t5","t6","t7","SheetHeader","SheetFooter","SheetTitle","SheetDescription","_c","SheetPrimitive","Button","React","MOBILE_BREAKPOINT","subscribe","callback","mql","window","matchMedia","addEventListener","removeEventListener","getSnapshot","innerWidth","getServerSnapshot","useIsMobile","useSyncExternalStore","c","_c","React","mergeProps","useRender","cva","VariantProps","useIsMobile","cn","Button","Input","Separator","Sheet","SheetContent","SheetDescription","SheetHeader","SheetTitle","Tooltip","TooltipContent","TooltipTrigger","useLatestRef","useSurfaceShortcuts","ShortcutDescriptor","symbol","IconRef","Icon","leftPanelOpenIcon","leftPanelCloseIcon","Skeleton","t0","$","className","props","t1","t2","SIDEBAR_COOKIE_NAME","SIDEBAR_COOKIE_MAX_AGE","SIDEBAR_WIDTH","SIDEBAR_WIDTH_MOBILE","SIDEBAR_WIDTH_ICON","SIDEBAR_KEYBOARD_SHORTCUT","SidebarContextProps","state","open","setOpen","openMobile","setOpenMobile","isMobile","toggleSidebar","SidebarContext","createContext","useSidebar","context","useContext","Error","InSidebarPanelContext","useInSidebarPanel","SidebarProvider","children","openProp","setOpenProp","style","defaultOpen","onOpenChange","undefined","useState","_open","_setOpen","value","openState","document","cookie","t3","_temp","_temp2","toggleSidebarRef","t4","id","keys","label","group","handler","current","shortcuts","t5","contextValue","t6","t7","CSSProperties","t8","t9","t10","open_1","open_0","Sidebar","dir","side","variant","collapsible","Symbol","for","t11","t12","SidebarToggleGlyph","icon","active","SidebarToggleIcons","closed","defaultToggleIcons","sameGlyph","a","b","kind","name","SidebarTrigger","onClick","icons","isOpen","glyph","event","SidebarRail","SidebarInset","SidebarInput","SidebarHeader","SidebarFooter","SidebarSeparator","SidebarContent","SidebarGroup","SidebarGroupLabel","render","slot","sidebar","defaultTagName","SidebarGroupAction","SidebarGroupContent","SidebarMenu","SidebarMenuItem","sidebarMenuButtonVariants","variants","default","outline","size","sm","lg","defaultVariants","SidebarMenuButton","isActive","tooltip","comp","SidebarMenuAction","SidebarMenuBadge","SidebarMenuSkeleton","showIcon","width","_temp3","Math","floor","random","SidebarMenuSub","SidebarMenuSubItem","SidebarMenuSubButton","_c","shortcuts","Button","Input","Separator","Tooltip","PluginDefinition","Core","InputModalityTracker","cn","isPointerModality","trackInputModality","SURFACE_LEVELS","SurfaceLevel","POPOVER_WIDTH","POPOVER_PADDING","POPOVER_MAX_HEIGHT","PopoverWidth","PopoverPadding","PopoverMaxHeight","ControlSizeProvider","useControlSize","iconSizeFor","textSizeFor","textStepFor","buttonTextClassFor","ControlSize","ButtonIconSize","DensityControlled","SingleLineProvider","useSingleLine","SidewaysScrollProvider","useSidewaysScroll","MenuSurfaceProvider","useMenuSurface","MENU_ROW","MENU_ROW_PAINT","MENU_ROW_CHECKED","MENU_LABEL","MENU_LABEL_PAINT","MENU_VALUE","MENU_SEPARATOR","Button","buttonVariants","ButtonGroup","Dialog","DialogTrigger","DialogClose","DialogPortal","DialogOverlay","DialogContent","DialogTitle","DialogDescription","DropdownMenu","DropdownMenuPortal","DropdownMenuTrigger","DropdownMenuContent","DropdownMenuGroup","DropdownMenuLabel","DropdownMenuSection","DropdownMenuItem","DropdownMenuCheckboxItem","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuShortcut","DropdownMenuSub","DropdownMenuSubTrigger","DropdownMenuSubContent","MenuRowAction","Input","Textarea","PortalThemeScopeProvider","usePortalThemeScope","PortalForwardProvider","PortalForwardScope","usePortalForward","usePortalForwardedAttrs","useRegionForwardedAttrs","PortalForwardedAttrs","PortalForwardScopeValue","OverlayPanel","closestOverlayPanel","OverlayPanelProps","Popover","PopoverTrigger","PopoverContent","useHoldPopoverOpen","popoverAnchorLostSink","PopoverAnchorLostFault","ResizableHandle","ResizablePanel","ResizablePanelGroup","ScrollArea","ScrollBar","Select","SelectContent","SelectGroup","SelectItem","SelectLabel","SelectScrollDownButton","SelectScrollUpButton","SelectSeparator","SelectTrigger","SelectValue","Separator","Sheet","SheetTrigger","SheetClose","SheetContent","SheetHeader","SheetFooter","SheetTitle","SheetDescription","Sidebar","SidebarContent","SidebarFooter","SidebarGroup","SidebarGroupAction","SidebarGroupContent","SidebarGroupLabel","SidebarHeader","SidebarInput","SidebarInset","SidebarMenu","SidebarMenuAction","SidebarMenuBadge","SidebarMenuButton","SidebarMenuItem","SidebarMenuSkeleton","SidebarMenuSub","SidebarMenuSubButton","SidebarMenuSubItem","SidebarProvider","SidebarRail","SidebarSeparator","SidebarTrigger","useSidebar","useInSidebarPanel","SidebarToggleGlyph","SidebarToggleIcons","Tooltip","TooltipTrigger","TooltipContent","TooltipCardContent","TooltipProvider","WithTooltip","WithTooltipProps","Tooltipped","TooltipSide","usePinOnClick","description","contributions","Root","component","scope","React","ResizablePrimitive","$","c","t0","useEffect","root","doc","marked","target","MODALITY","POINTER","POINTER_FOCUS_ATTR","NAVIGATION_KEYS","el","defineScriptState","CUSTOM_UTILITY_REGISTRY","DEFAULT_CONFIG","getDefaultConfig","DEFAULT_CONFLICTS","REVERSE_CONFLICTS","beater","beaten","id","seen","work","seeds","next","edges","ids","where","classGroups","conflictingClassGroups","list","entry","b","twMerge","extendTailwindMerge","inputs","clsx","SURFACE_LEVELS","POPOVER_WIDTH","POPOVER_PADDING","POPOVER_MAX_HEIGHT","ControlSizeContext","createContext","t1","children","size","useContext","density","SingleLineContext","value","SidewaysScrollContext","MenuSurfaceContext","MENU_ROW_PAINT","MENU_ROW_CHECKED","MENU_ROW","MENU_LABEL_PAINT","MENU_LABEL","MENU_VALUE","MENU_ROW_TRAIL","MENU_SEPARATOR","PortalForwardContext","reach","parent","t2","name","t3","t4","PortalForwardScope","merged","node","setNode","useState","state","setState","top","bottom","prev","measure","useEventCallback","useResizeObserver","useLayoutEffect","THEME_SCOPE_ATTR","pluginScope","usePluginScopeId","forwarded","bareThemeScope","scope","isSubThemeScope","qualifyThemeScope","stamped","HEADER_PIN_STYLE","FOOTER_PIN_STYLE","className","footer","header","onKeyDown","onScroll","ref","rest","t5","width","padding","maxHeight","scroll","rows","measureScroll","railRef","useRailGuard","measureRef","panelRef","scopeSelectAllKeyDown","handleKeyDown","e_0","handleScroll","popupScope","t9","t10","t11","t12","t13","t14","t15","t16","t17","IconScopeProvider","t18","t19","OverlayBoundary","t20","pinned","setPinned","hovered","setHovered","focused","setFocused","reason","open","t6","t7","attrs","handlers","key","props","a","keys","cleanup","element","arm","setSnapshot","current","detachChild","childRef","event","flushSync","out","names","handler","intent","armed","setArmed","snapshot","bridge","delegates","own","cloneElement","mergeProps","mount","Fragment","TOOLTIP_TYPE","delay","Tooltip","side","sideOffset","align","alignOffset","container","usePortalContainer","t8","anchor","wide","content","pinOnClick","attr","body","pin","render","refreshIcon","symbol","buttonVariants","cva","onClick","tooltip","tooltipAlign","tooltipSide","variant","aspect","shape","loading","disabled","resolvedSize","textClass","autoPending","setAutoPending","mounted","useRef","result","handleClick","isLoading","iconOnly","ariaLabel","T0","T1","Button","Icon","closeIcon$1","Dialog","DIALOG_SIZES","DIALOG_TOP","DIALOG_BOTTOM_GAP","showCloseButton","mirrored","setMirrored","defaultOpen","isOpen","onOpenChange","nextOpen","actionsRef","chevronRightIcon","checkIcon$1","handleOpenChange","Menu","inset","action","label","activationProps","onAction","isolate","shortcut","checked","fieldChromeClass","type","outline","Input","fitContent","popoverAnchorLostSink","defineReportSink","cur","fallback","lastGood","lost","cause","positionerRef","trigger","resolved","PopoverEverOpenedContext","PopoverHoldContext","PopoverTriggerContext","SetPopoverTriggerContext","hold","active","anchorAtRest","usePopupAnchorAtRest","drawn","setDrawn","everOpened","setEverOpened","holds","details","guardedOpenChange","setTrigger","Popover","triggerRef","collisionAvoidance","resetOnClose","steadyAnchor","withHandle","ScrollArea","orientation","keyboardArrowDownIcon","checkIcon","keyboardArrowUpIcon","Select","compact","alignItemWithTrigger","_orientation","className_0","rest_0","_label","bleed","Separator","closeIcon","ContentScope","MOBILE_BREAKPOINT","mql","callback","leftPanelOpenIcon","leftPanelCloseIcon","SIDEBAR_COOKIE_NAME","SIDEBAR_COOKIE_MAX_AGE","SIDEBAR_WIDTH","SIDEBAR_WIDTH_MOBILE","SIDEBAR_WIDTH_ICON","SIDEBAR_KEYBOARD_SHORTCUT","SidebarContext","context","InSidebarPanelContext","openProp","setOpenProp","style","isMobile","openMobile","setOpenMobile","_open","_setOpen","openState","setOpen","toggleSidebar","toggleSidebarRef","useLatestRef","useSurfaceShortcuts","contextValue","open_1","open_0","dir","collapsible","defaultToggleIcons","icons","glyph","useRender","sidebarMenuButtonVariants","isActive","comp","showIcon","web_default","Core"],"sources":["../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/input-modality.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/custom-utilities.generated.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/surface.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/popover-width.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/control-size.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/single-line.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/sideways-scroll.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/menu-surface.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/theme/menu-row.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/portal-forward.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/use-scroll-fade.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/portal-theme-scope.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/overlay-panel.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/use-pin-on-click.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/deferred-trigger.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/tooltip.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/button.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/button-group.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/dialog.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/popup-open-mirror.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/frame-focus-dismiss.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/dropdown-menu.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/field-chrome.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/input.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/textarea.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/steady-anchor.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/popover.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/resizable.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/scroll-area.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/select.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/separator.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/sheet.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/hooks/use-mobile.ts","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/sidebar.tsx","../../../../../__A__/dev/singularity/.claude/worktrees/att-1794515063-e6dm4h/plugins/primitives/plugins/css/plugins/ui-kit/web/index.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { defineScriptState } from \"@plugins/primitives/plugins/css/plugins/script-state/core\";\n\n/**\n * Whether the user is driving the page with the pointer or navigating it with\n * the keyboard, and what the focus rings read of it.\n *\n * `:focus-visible` alone is not enough. Chrome marks focus visible whenever the\n * last input was ANY key, so a popover opened with a click and closed with\n * Escape (or a submit shortcut) hands focus back to its trigger with a ring\n * the user never navigated to. Here only a NAVIGATION key — one that moves\n * focus or a highlight — flips the page back to keyboard; Escape, Enter and\n * typing do not. A pointer press flips it to pointer.\n *\n * Two marks, for two readers:\n *\n * - `data-input-modality=\"pointer\"` on `` (absent = keyboard) — the\n * page's state, for script (`isPointerModality`). Declared script-only\n * (`defineScriptState`), so `script-state:no-stylesheet-reads` rejects any\n * stylesheet selector naming it.\n * - `data-pointer-focus` on the ONE focused element, while the page is in\n * pointer modality — what the `focus-ring` utilities in `app.css` read\n * (`:focus-visible:where(:not([data-pointer-focus]))`). Only the focused\n * element can be `:focus-visible`, so it is the only element whose ring the\n * modality can change.\n *\n * Why not one mark on ``: a rule reading an attribute of the root makes\n * Chrome restyle every element that rule could match on each change of it —\n * every `focus-ring` host in the document, ~7,000 with four Sonata windows open\n * — on the page's first pointer press and on every Tab ⇄ click flip after it.\n * Marking the focused element restyles that element (and the `:has()` ring\n * hosts above it) alone. Measured by `ui-kit/e2e/input-modality-flip-cost.ts`.\n */\nexport function InputModalityTracker() {\n useEffect(() => trackInputModality(document), []);\n return null;\n}\n\n/**\n * Track the input modality of `doc` — the page's own (`InputModalityTracker`),\n * or another document the page draws into: a Document Picture-in-Picture\n * window, whose focus and input the page's listeners never see. Returns the\n * disposer.\n */\nexport function trackInputModality(doc: Document): () => void {\n const root = doc.documentElement;\n let marked: Element | null = null;\n /** Move the pointer-focus mark to where it belongs now. */\n const sync = () => {\n const target = MODALITY.read(root) === POINTER ? focusedElement(doc) : null;\n if (target === marked) return;\n marked?.removeAttribute(POINTER_FOCUS_ATTR);\n target?.setAttribute(POINTER_FOCUS_ATTR, \"\");\n marked = target;\n };\n const onPointer = () => {\n MODALITY.write(root, POINTER);\n sync();\n };\n const onKey = (e: KeyboardEvent) => {\n if (!NAVIGATION_KEYS.has(e.key)) return;\n MODALITY.write(root, undefined);\n sync();\n };\n // Focus left an element (blurred, or removed with focus inside it): drop\n // the mark, so it holds no detached node. A move fires focusin next.\n const onFocusOut = () => {\n marked?.removeAttribute(POINTER_FOCUS_ATTR);\n marked = null;\n };\n doc.addEventListener(\"pointerdown\", onPointer, true);\n // Observes every keystroke to tell navigation from pointer use; handles none.\n doc.addEventListener(\"keydown\", onKey, true);\n // Focus moved (a click, a popup handing focus back, a script): the mark\n // follows it — before the frame is styled, as focusin fires synchronously.\n doc.addEventListener(\"focusin\", sync, true);\n doc.addEventListener(\"focusout\", onFocusOut, true);\n return () => {\n doc.removeEventListener(\"pointerdown\", onPointer, true);\n doc.removeEventListener(\"keydown\", onKey, true);\n doc.removeEventListener(\"focusin\", sync, true);\n doc.removeEventListener(\"focusout\", onFocusOut, true);\n marked?.removeAttribute(POINTER_FOCUS_ATTR);\n MODALITY.write(root, undefined);\n };\n}\n\n/**\n * The focused element, through open shadow roots; null when none is. The\n * innermost one: a ring host whose own shadow root holds the focus is not\n * marked (no ring host has a shadow root today).\n */\nfunction focusedElement(doc: Document): Element | null {\n let el = doc.activeElement;\n while (el?.shadowRoot?.activeElement) el = el.shadowRoot.activeElement;\n return el === null || el === doc.body ? null : el;\n}\n\nconst NAVIGATION_KEYS = new Set([\n \"Tab\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n]);\n\n/** The page's modality on ``: \"pointer\", absent for keyboard. Script-only. */\nconst MODALITY = defineScriptState(\"data-input-modality\");\nconst POINTER = \"pointer\";\n/** Read by the `focus-ring*` utilities in `app.css` — keep the two in step. */\nconst POINTER_FOCUS_ATTR = \"data-pointer-focus\";\n\n/**\n * Whether the user last drove `doc` with the pointer (as stamped by\n * `InputModalityTracker`) — e.g. a button focused by a click, which a key\n * should not treat as one the user navigated to.\n */\nexport function isPointerModality(doc: Document): boolean {\n return MODALITY.read(doc.documentElement) === POINTER;\n}\n","// AUTO-GENERATED from app.css @utility `/* twmerge: … */` markers. Do not edit.\n// Run `./equin build` to regenerate.\n// (see plugins/framework/plugins/tooling/plugins/codegen/core/custom-utilities-gen.ts).\n//\n// The twMerge registry consumed by cn() (lib/utils.ts), derived from app.css —\n// the single source of truth for which custom @utility classes exist and how\n// tailwind-merge must classify each.\n//\n// The `app-css-utilities-in-sync` check fails on drift.\n\nimport type { RegistryEntry } from \"./custom-utilities-types\";\n\nexport const CUSTOM_UTILITY_REGISTRY = [\n { classes: [\"surface-roles-overlay\"], standalone: true, reason: \"Custom properties only; the overlay surface bundle's colour roles, no built-in group to conflict with.\" },\n { classes: [\"focus-ring\", \"focus-ring-within\"], standalone: true, reason: \"Additive box-shadow/outline; no single-value built-in group to conflict with.\" },\n { classes: [\"focus-border\"], standalone: true, reason: \"State-scoped border-color under :focus-visible; must coexist with the resting border-* class, not replace it.\" },\n { classes: [\"focus-border-within\"], standalone: true, reason: \"State-scoped border-color under :has(); must coexist with the resting border-* class, not replace it.\" },\n { classes: [\"focus-ring-from\"], standalone: true, reason: \"Additive box-shadow/outline; no single-value built-in group to conflict with.\" },\n { classes: [\"rounded-checkbox\", \"rounded-squircle\", \"rounded-card\", \"rounded-control\", \"rounded-control-sm\", \"rounded-control-xs\", \"rounded-popover\", \"rounded-float\", \"rounded-cover\"], extend: \"rounded\" },\n { classes: [\"shadow-popover\"], extend: \"shadow\" },\n { classes: [\"backdrop-popover\"], group: \"sg-backdrop\", excludes: [\"backdrop-filter\", \"backdrop-blur\", \"backdrop-brightness\", \"backdrop-contrast\", \"backdrop-grayscale\", \"backdrop-hue-rotate\", \"backdrop-invert\", \"backdrop-opacity\", \"backdrop-saturate\", \"backdrop-sepia\"], under: [] },\n { classes: [\"shadow-float\"], extend: \"shadow\" },\n { classes: [\"backdrop-float\"], group: \"sg-backdrop\", excludes: [\"backdrop-filter\", \"backdrop-blur\", \"backdrop-brightness\", \"backdrop-contrast\", \"backdrop-grayscale\", \"backdrop-hue-rotate\", \"backdrop-invert\", \"backdrop-opacity\", \"backdrop-saturate\", \"backdrop-sepia\"], under: [] },\n { classes: [\"float-sheen\"], standalone: true, reason: \"a token-driven background-image on the float surface; no bg-image class sits beside it.\" },\n { classes: [\"shadow-cover\", \"shadow-cover-hover\"], extend: \"shadow\" },\n { classes: [\"py-toolbar\"], extend: \"py\" },\n { classes: [\"gap-toolbar\"], extend: \"gap\" },\n { classes: [\"p-pill\"], extend: \"p\" },\n { classes: [\"gap-pill\"], extend: \"gap\" },\n { classes: [\"h-pill-divider\"], extend: \"h\" },\n { classes: [\"px-pill-divider\"], extend: \"px\" },\n { classes: [\"w-search\", \"w-search-narrow\"], extend: \"w\" },\n { classes: [\"rounded-search\"], extend: \"rounded\" },\n { classes: [\"size-search-icon\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"pl-search-text\"], extend: \"pl\" },\n { classes: [\"size-view-chip-icon\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"gap-view-chip\"], extend: \"gap\" },\n { classes: [\"rounded-view-chip\", \"rounded-view-add\", \"rounded-create\"], extend: \"rounded\" },\n { classes: [\"px-create\", \"px-create-narrow\"], extend: \"px\" },\n { classes: [\"gap-view-chips\", \"gap-view-switcher\"], extend: \"gap\" },\n { classes: [\"py-gallery-body\"], extend: \"py\" },\n { classes: [\"pt-card-meta\"], extend: \"pt\" },\n { classes: [\"px-card-meta\"], extend: \"px\" },\n { classes: [\"h-card-title-line\", \"h-card-subtitle-line\", \"h-card-tokens-line\"], extend: \"h\" },\n { classes: [\"pt-card-subtitle\", \"pt-card-tokens\"], extend: \"pt\" },\n { classes: [\"gap-card-token\", \"gap-card-token-icon\"], extend: \"gap\" },\n { classes: [\"size-card-token-icon\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"pt-card-excerpt\"], extend: \"pt\" },\n { classes: [\"line-clamp-card-excerpt\"], standalone: true, reason: \"a multi-line clamp read from a token; no built-in line-clamp class sits beside it on the excerpt.\" },\n { classes: [\"bg-cover-scrim\"], standalone: true, reason: \"a gradient read from a token; no other background-image class sits beside it on the scrim layer.\" },\n { classes: [\"line-clamp-card-subtitle\"], standalone: true, reason: \"a multi-line clamp read from a token; no built-in line-clamp class sits beside it on the subtitle.\" },\n { classes: [\"translate-cover-lift\"], standalone: true, reason: \"the individual translate property; worn only under the cover's hover group, beside no other translate class.\" },\n { classes: [\"scale-cover-zoom\"], standalone: true, reason: \"the individual scale property; worn only under the cover's hover group, beside no other scale class.\" },\n { classes: [\"filter-cover-hover\"], standalone: true, reason: \"the whole filter from a token; worn only under the cover's hover group, beside no other filter class.\" },\n { classes: [\"size-cover-primary\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"shadow-cover-primary\"], extend: \"shadow\" },\n { classes: [\"gap-group-header\"], extend: \"gap\" },\n { classes: [\"py-group-header\"], extend: \"py\" },\n { classes: [\"px-group-header\"], extend: \"px\" },\n { classes: [\"mb-group-header\"], standalone: true, reason: \"the room under a group header; no margin class is ever set beside it.\" },\n { classes: [\"gap-group\"], extend: \"gap\" },\n { classes: [\"size-group-chevron\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"mx-group-rule\"], standalone: true, reason: \"the group rule's own margins; no margin class sits beside it.\" },\n { classes: [\"size-group-chip\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"rounded-group-chip\"], extend: \"rounded\" },\n { classes: [\"size-group-chip-icon\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"px-group-pill\"], extend: \"px\" },\n { classes: [\"gap-list-slot\"], extend: \"gap\" },\n { classes: [\"min-h-soft-row\"], extend: \"min-h\" },\n { classes: [\"p-soft-row\"], extend: \"p\" },\n { classes: [\"rounded-soft-row\"], extend: \"rounded\" },\n { classes: [\"gap-x-soft\"], extend: \"gap-x\" },\n { classes: [\"opacity-soft-rule\"], standalone: true, reason: \"the between-row hairline's own opacity; nothing else sets one on it.\" },\n { classes: [\"ms-soft-rule\"], standalone: true, reason: \"where the between-row hairline starts; no margin class sits beside it.\" },\n { classes: [\"me-soft-rule\"], standalone: true, reason: \"how far short of the row's end the hairline stops; no margin class sits beside it.\" },\n { classes: [\"h-soft-header\"], extend: \"h\" },\n { classes: [\"mb-soft-header\"], standalone: true, reason: \"the room under the soft header row; no margin class sits beside it.\" },\n { classes: [\"rounded-soft-frame\"], extend: \"rounded\" },\n { classes: [\"soft-frame-edge\"], standalone: true, reason: \"the soft frame's border width from a token; no other border-width class sits on the frame.\" },\n { classes: [\"p-soft-frame\"], extend: \"p\" },\n { classes: [\"backdrop-soft-frame\"], group: \"sg-backdrop\", excludes: [\"backdrop-filter\", \"backdrop-blur\", \"backdrop-brightness\", \"backdrop-contrast\", \"backdrop-grayscale\", \"backdrop-hue-rotate\", \"backdrop-invert\", \"backdrop-opacity\", \"backdrop-saturate\", \"backdrop-sepia\"], under: [] },\n { classes: [\"pt-title-cell-subtitle\"], extend: \"pt\" },\n { classes: [\"shadow-soft-frame\"], extend: \"shadow\" },\n { classes: [\"size-soft-thumb\"], group: \"sg-dv-size\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"rounded-soft-thumb\", \"rounded-inherit\"], extend: \"rounded\" },\n { classes: [\"tracking-column-header\"], standalone: true, reason: \"the column labels' tracking from a token; no tracking class sits beside it on the header.\" },\n { classes: [\"overlay-enter\", \"overlay-exit\", \"surface-enter\"], extend: \"animate\" },\n { classes: [\"rounded-tooltip\"], extend: \"rounded\" },\n { classes: [\"p-tooltip\"], extend: \"p\" },\n { classes: [\"shadow-tooltip\"], extend: \"shadow\" },\n { classes: [\"tooltip-arrow\"], group: \"sg-tooltip-arrow\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"tooltip-offset\"], standalone: true, reason: \"margins keyed on data-side only; no tooltip margin class to collide with\" },\n { classes: [\"region-line\"], standalone: true, reason: \"Composite single-line invariant (align-items + whitespace); name doesn't misfile into a built-in group and it's a base layer, not a selectively-overridden single property.\" },\n { classes: [\"no-scrollbar\"], standalone: true, reason: \"Hides scrollbar chrome (scrollbar-width + ::-webkit-scrollbar); additive, no single-value built-in group to conflict with.\" },\n { classes: [\"scroll-fade\"], standalone: true, reason: \"Additive ::before/::after gradient overlays gated on data-fade-*; pseudo-element paint has no single-value built-in group to conflict with.\" },\n { classes: [\"p-chip\", \"p-row\", \"p-card\", \"p-chip-compact\"], group: \"sg-pad\", excludes: [\"p\"], under: [] },\n { classes: [\"rounded-chip-compact\"], extend: \"rounded\" },\n { classes: [\"p-chip-header\", \"p-thread-card\", \"p-composer\", \"p-composer-text\", \"p-composer-actions\", \"p-tool-badge\", \"p-inline-code\"], group: \"sg-pad\", excludes: [\"p\"], under: [] },\n { classes: [\"px-split-arrow\", \"px-row\"], extend: \"px\" },\n { classes: [\"lead-icon-xs\", \"lead-icon-sm\"], standalone: true, reason: \"Spaces a child svg, not the element itself; no built-in group sets a child's margin.\" },\n { classes: [\"rounded-tool-badge\", \"rounded-inline-code\"], extend: \"rounded\" },\n { classes: [\"rounded-l-inline-code\"], standalone: true, reason: \"Rounds only the start side of a two-part inline box; never combined with another radius class on the same element.\" },\n { classes: [\"rounded-r-inline-code\"], standalone: true, reason: \"Rounds only the end side of a two-part inline box; never combined with another radius class on the same element.\" },\n { classes: [\"hairline-tool-badge\", \"hairline-inline-code\"], standalone: true, reason: \"Role border width for one element that sets no other border width. Not named border-*: tailwind-merge files any border- as a border COLOR, so a colour class beside it would silently drop it.\" },\n { classes: [\"size-op-status-icon\", \"size-status-dot-xs\", \"size-status-dot-sm\", \"size-status-dot-md\", \"size-status-dot-lg\", \"size-avatar-xs\", \"size-avatar-sm\", \"size-avatar-md\", \"size-avatar-lg\", \"size-cover-thumb\"], extend: \"size\" },\n { classes: [\"gap-x-gallery\"], extend: \"gap-x\" },\n { classes: [\"gap-y-gallery\"], extend: \"gap-y\" },\n { classes: [\"pb-gallery\"], extend: \"pb\" },\n { classes: [\"clip-fade-end\"], standalone: true, reason: \"A mask-image on the box itself; no built-in group sets mask-image, so nothing to replace.\" },\n { classes: [\"sticky-fade\"], standalone: true, reason: \"An additive ::after gradient tail; pseudo-element paint has no single-value built-in group to conflict with.\" },\n { classes: [\"h-sidebar-row\"], extend: \"h\" },\n { classes: [\"px-sidebar-row\"], extend: \"px\" },\n { classes: [\"size-sidebar-icon\"], extend: \"size\" },\n { classes: [\"gap-sidebar-icon\"], extend: \"gap\" },\n { classes: [\"h-sidebar-end\"], extend: \"h\" },\n { classes: [\"size-sidebar-brand-mark\"], extend: \"size\" },\n { classes: [\"gap-sidebar-brand\"], extend: \"gap\" },\n { classes: [\"px-sidebar-brand-name\"], extend: \"px\" },\n { classes: [\"h-sidebar-brand\"], extend: \"h\" },\n { classes: [\"p-sidebar-brand\"], extend: \"p\" },\n { classes: [\"control-xs\", \"control-sm\", \"control-md\", \"control-lg\"], group: \"sg-control-height\", excludes: [\"h\"], under: [] },\n { classes: [\"control-icon-xs\", \"control-icon-sm\", \"control-icon-md\", \"control-icon-lg\"], group: \"sg-control-icon\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"control-min-xs\", \"control-min-sm\", \"control-min-md\", \"control-min-lg\"], group: \"sg-control-min\", excludes: [\"min-h\"], under: [] },\n { classes: [\"size-control-icon-xs\", \"size-control-icon-sm\", \"size-control-icon-md\", \"size-control-icon-lg\"], extend: \"size\" },\n { classes: [\"px-control-xs\", \"px-control-sm\", \"px-control-md\", \"px-control-lg\"], extend: \"px\" },\n { classes: [\"pill-ends\"], standalone: true, reason: \"Declares which sides are rounded ends; sets custom properties only, no built-in group to conflict with.\" },\n { classes: [\"pill-start\"], standalone: true, reason: \"Declares the start side a rounded end; sets a custom property only, no built-in group to conflict with.\" },\n { classes: [\"pill-end\"], standalone: true, reason: \"Declares the end side a rounded end; sets a custom property only, no built-in group to conflict with.\" },\n { classes: [\"gap-control-xs\", \"gap-control-sm\", \"gap-control-md\", \"gap-control-lg\", \"gap-inherit\"], extend: \"gap\" },\n { classes: [\"h-chrome-bar\", \"h-chrome-pane\", \"h-chrome-pane-card\"], extend: \"h\" },\n { classes: [\"px-chrome\"], extend: \"px\" },\n { classes: [\"pl-chrome\"], extend: \"pl\" },\n { classes: [\"px-chrome-pane\", \"px-chrome-pane-card\"], extend: \"px\" },\n { classes: [\"pr-floating-bar-pane\"], extend: \"pr\" },\n { classes: [\"px-subpane\"], extend: \"px\" },\n { classes: [\"border-b-chrome-pane\"], extend: \"border-w-b\" },\n { classes: [\"gap-tree-root\"], extend: \"gap\" },\n { classes: [\"pt-section-body\", \"pt-section-head\"], extend: \"pt\" },\n { classes: [\"pb-section-head\"], extend: \"pb\" },\n { classes: [\"pt-section-head-band\"], extend: \"pt\" },\n { classes: [\"pb-section-head-band\"], extend: \"pb\" },\n { classes: [\"min-h-tree-row\"], extend: \"min-h\" },\n { classes: [\"gap-tree-row\"], extend: \"gap\" },\n { classes: [\"w-tree-chevron\"], extend: \"w\" },\n { classes: [\"rail-none\", \"rail-2xs\", \"rail-xs\", \"rail-xsm\", \"rail-sm\", \"rail-smd\", \"rail-md\", \"rail-lg\", \"rail-xl\", \"rail-2xl\", \"rail-panel\"], group: \"sg-rail\", excludes: [\"p\", \"px\", \"py\", \"pt\", \"pr\", \"pb\", \"pl\"], under: [] },\n { classes: [\"rail-x-none\", \"rail-x-2xs\", \"rail-x-xs\", \"rail-x-xsm\", \"rail-x-sm\", \"rail-x-smd\", \"rail-x-md\", \"rail-x-lg\", \"rail-x-xl\", \"rail-x-2xl\", \"rail-owe-none\", \"rail-owe-2xs\", \"rail-owe-xs\", \"rail-owe-xsm\", \"rail-owe-sm\", \"rail-owe-smd\", \"rail-owe-md\", \"rail-owe-lg\", \"rail-owe-xl\", \"rail-owe-sidebar\", \"rail-owe-2xl\"], group: \"sg-rail-x\", excludes: [\"px\", \"pr\", \"pl\"], under: [] },\n { classes: [\"rail-y-none\", \"rail-y-2xs\", \"rail-y-xs\", \"rail-y-xsm\", \"rail-y-sm\", \"rail-y-smd\", \"rail-y-md\", \"rail-y-lg\", \"rail-y-xl\", \"rail-y-2xl\"], group: \"sg-rail-y\", excludes: [\"py\", \"pt\", \"pb\"], under: [] },\n { classes: [\"rail-bleed\", \"rail-follow\"], extend: \"px\" },\n { classes: [\"rail-rows\"], standalone: true, reason: \"a row host's text-rail declaration; it composes with whatever rail class the host wears.\" },\n { classes: [\"rail-band\"], extend: \"px\" },\n { classes: [\"rail-band-region\"], standalone: true, reason: \"a band's content wrapper republishing the band's padding; no padding of its own.\" },\n { classes: [\"py-row\", \"py-row-compact\"], extend: \"py\" },\n { classes: [\"min-h-panel-row\"], extend: \"min-h\" },\n { classes: [\"rounded-panel-row\"], extend: \"rounded\" },\n { classes: [\"menu-row\"], standalone: true, reason: \"The row's STATE layer (tone vars, checked, destructive, icon tone); every overridable single property is a separate class in menu-row.ts.\" },\n { classes: [\"menu-row-lit\"], standalone: true, reason: \"The highlighted paint, only ever applied under a state variant (data-highlighted:, data-popup-open:, hover:); reads the vars menu-row declares.\" },\n { classes: [\"pr-floating-bar\"], extend: \"pr\" },\n { classes: [\"anchor-floating-bar-band\"], standalone: true, reason: \"Names this box as an anchor; no built-in group sets anchor-name.\" },\n { classes: [\"floating-bar-band\"], standalone: true, reason: \"Anchor-derived top + height pair for one fixed band; splitting them into top/h groups would let a later top-* silently detach it from its anchor.\" },\n { classes: [\"floating-bar-boundary\"], standalone: true, reason: \"Scopes the floating-bar anchor name to this box (the bar's safe-area rule withdraws it from headers inside); no built-in group sets anchor-scope.\" },\n { classes: [\"cp-panel\"], extend: \"p\" },\n { classes: [\"cp-surface\"], extend: \"w\" },\n { classes: [\"cp-card\"], standalone: true, reason: \"Re-declares the panel geometry vars for a settings card's subtree; no single-value built-in group to conflict with.\" },\n { classes: [\"cp-body\"], standalone: true, reason: \"Composite band container (flex column + gap + the positioned first-rule mask); no single-value built-in group to conflict with.\" },\n { classes: [\"cp-band\"], standalone: true, reason: \"Composite band marker (positioning context + the full-bleed ::before hairline); no single-value built-in group to conflict with.\" },\n { classes: [\"cp-row\"], standalone: true, reason: \"Composite row grid (display + tracks + gap + min-height + inline padding + radius); not a single-property utility another class should selectively override.\" },\n { classes: [\"menu-field\"], standalone: true, reason: \"Composite row geometry for a menu's search field (bleed margins + width, and its glyph / input / clear cells); not a single-property utility another class should selectively override.\" },\n { classes: [\"cp-rule\"], standalone: true, reason: \"Composite builder grid (display + 6 tracks + gap + min-height + inline padding + radius + the data-span collapse); not a single-property utility another class should selectively override.\" },\n { classes: [\"cp-setting\"], standalone: true, reason: \"Composite value-row grid (display + tracks + gap + min-height + the two-sided inline padding + radius); not a single-property utility another class should selectively override.\" },\n { classes: [\"cp-group\"], standalone: true, reason: \"Composite nested rail region (the deeper published rail + the inline padding that pays it + the region-relative row pad); no single-value built-in group to conflict with.\" },\n { classes: [\"gap-none\", \"gap-2xs\", \"gap-xs\", \"gap-xsm\", \"gap-sm\", \"gap-smd\", \"gap-md\", \"gap-lg\", \"gap-xl\", \"gap-2xl\"], extend: \"gap\" },\n { classes: [\"gap-x-none\", \"gap-x-2xs\", \"gap-x-xs\", \"gap-x-xsm\", \"gap-x-sm\", \"gap-x-smd\", \"gap-x-md\", \"gap-x-lg\", \"gap-x-xl\", \"gap-x-2xl\"], extend: \"gap-x\" },\n { classes: [\"gap-y-none\", \"gap-y-2xs\", \"gap-y-xs\", \"gap-y-xsm\", \"gap-y-sm\", \"gap-y-smd\", \"gap-y-md\", \"gap-y-lg\", \"gap-y-xl\", \"gap-y-2xl\"], extend: \"gap-y\" },\n { classes: [\"p-none\", \"p-2xs\", \"p-xs\", \"p-xsm\", \"p-sm\", \"p-smd\", \"p-md\", \"p-lg\", \"p-xl\", \"p-2xl\"], extend: \"p\" },\n { classes: [\"px-none\", \"px-2xs\", \"px-xs\", \"px-xsm\", \"px-sm\", \"px-smd\", \"px-md\", \"px-lg\", \"px-xl\", \"px-2xl\"], extend: \"px\" },\n { classes: [\"py-none\", \"py-2xs\", \"py-xs\", \"py-xsm\", \"py-sm\", \"py-smd\", \"py-md\", \"py-lg\", \"py-xl\", \"py-2xl\"], extend: \"py\" },\n { classes: [\"pt-none\", \"pt-2xs\", \"pt-xs\", \"pt-xsm\", \"pt-sm\", \"pt-smd\", \"pt-md\", \"pt-lg\", \"pt-xl\", \"pt-2xl\"], extend: \"pt\" },\n { classes: [\"pr-none\", \"pr-2xs\", \"pr-xs\", \"pr-xsm\", \"pr-sm\", \"pr-smd\", \"pr-md\", \"pr-lg\", \"pr-xl\", \"pr-2xl\"], extend: \"pr\" },\n { classes: [\"pb-none\", \"pb-2xs\", \"pb-xs\", \"pb-xsm\", \"pb-sm\", \"pb-smd\", \"pb-md\", \"pb-lg\", \"pb-xl\", \"pb-2xl\"], extend: \"pb\" },\n { classes: [\"pl-none\", \"pl-2xs\", \"pl-xs\", \"pl-xsm\", \"pl-sm\", \"pl-smd\", \"pl-md\", \"pl-lg\", \"pl-xl\", \"pl-2xl\"], extend: \"pl\" },\n { classes: [\"my-flow-block\"], standalone: true, reason: \"prose block rhythm; the only margin class a flow block wears.\" },\n { classes: [\"my-flow-item\"], standalone: true, reason: \"list-item rhythm in prose; the only margin class a list item wears.\" },\n { classes: [\"my-flow-heading\"], standalone: true, reason: \"the room above and below a top-level prose heading; no other margin class sits beside it.\" },\n { classes: [\"my-flow-subheading\"], standalone: true, reason: \"the room above and below a minor prose heading; no other margin class sits beside it.\" },\n { classes: [\"my-flow-rule\"], standalone: true, reason: \"the room around a prose thematic break; no other margin class sits beside it.\" },\n { classes: [\"my-menu-separator\"], standalone: true, reason: \"the room around a menu hairline; no other margin class sits beside it.\" },\n { classes: [\"my-overlay-header\"], standalone: true, reason: \"a panel header's block bleed through the panel padding plus the room under it; no other margin class sits beside it.\" },\n { classes: [\"my-overlay-footer\"], standalone: true, reason: \"a panel footer's block bleed through the panel padding plus the room above it; no other margin class sits beside it.\" },\n { classes: [\"text-display\"], extend: \"font-size\" },\n { classes: [\"font-display\"], extend: \"font-weight\" },\n { classes: [\"text-title\", \"text-heading\", \"text-subheading\", \"text-body\", \"text-label\", \"text-caption\", \"text-group\"], extend: \"font-size\" },\n { classes: [\"font-group\"], extend: \"font-weight\" },\n { classes: [\"text-control\"], extend: \"font-size\" },\n { classes: [\"font-control\", \"font-control-strong\"], extend: \"font-weight\" },\n { classes: [\"text-tag\"], extend: \"font-size\" },\n { classes: [\"font-tag\", \"font-tag-strong\"], extend: \"font-weight\" },\n { classes: [\"text-code\", \"text-display-compact\", \"text-title-compact\", \"text-heading-compact\", \"text-subheading-compact\", \"text-body-compact\", \"text-label-compact\", \"text-caption-compact\", \"text-group-compact\", \"text-control-compact\", \"text-tag-compact\", \"text-code-compact\"], extend: \"font-size\" },\n { classes: [\"icon-auto\"], group: \"sg-icon-auto\", excludes: [\"size\", \"h\", \"w\"], under: [] },\n { classes: [\"z-under\", \"z-base\", \"z-raised\", \"z-nav\", \"z-float\", \"z-drag\", \"z-overlay\", \"z-popover\", \"z-max\"], extend: \"z\" },\n] as const satisfies readonly RegistryEntry[];\n\n// Synthetic group ids (for extendTailwindMerge's generic type parameter).\nexport type CustomGroupId = Extract<(typeof CUSTOM_UTILITY_REGISTRY)[number], { group: string }>[\"group\"];\n","import { clsx, type ClassValue } from \"clsx\";\nimport { extendTailwindMerge, getDefaultConfig } from \"tailwind-merge\";\n\nimport type { ClassName } from \"@plugins/primitives/plugins/css/plugins/ui-kit/core\";\nimport {\n CUSTOM_UTILITY_REGISTRY,\n type CustomGroupId,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/custom-utilities.generated\";\n\n// Derive the twMerge extension from the single-source registry so adding a custom\n// @utility never requires a separate hand-edit of the conflict map (the coupling\n// that let role utilities like `text-caption` get silently stripped). The registry\n// is GENERATED from the `/* twmerge: … */` markers in app.css (the single source of\n// truth) — see custom-utilities-types.ts for the wiring semantics.\n\n// tailwind-merge reads `conflictingClassGroups[a] = [b]` as \"a LATER class of group\n// `a` removes an EARLIER class of group `b`\" — one-directional. A synthetic group\n// therefore needs BOTH entries to be mutually exclusive with a built-in; with only\n// one, base-classes-first / caller-className-last (the repo's dominant order)\n// leaves both classes alive and the CSS cascade decides. That is a real lie for a\n// utility that publishes what it applies: `cn(\"px-md\", \"rail-lg\")` used to keep\n// both, so the element advertised `--rail-start: lg` while actually being padded\n// `md`. `excludes:` emits both directions.\nconst DEFAULT_CONFIG = getDefaultConfig();\nconst DEFAULT_CONFLICTS: Record =\n DEFAULT_CONFIG.conflictingClassGroups;\n\n// tailwind-merge's own map is the authority on how the built-in groups nest — `p`\n// over `px` over `pr`/`pl`, `size` over `w`/`h` — so a group's declared relation is\n// CLOSED over it rather than hand-listed. A hand list misses `ps`/`pe`/`pbs`/`pbe`\n// (real 3.5.0 groups that aren't even spellable in BuiltinGroupId) and cannot\n// follow tailwind-merge when it changes; a closure inherits its coverage exactly.\nconst REVERSE_CONFLICTS: Record = {};\nfor (const [beater, beaten] of Object.entries(DEFAULT_CONFLICTS)) {\n for (const id of beaten) (REVERSE_CONFLICTS[id] ??= []).push(beater);\n}\n\n// Worklist + visited set, not recursion: the default map has genuine cycles\n// (`fvn-normal` ↔ `fvn-ordinal`, `touch` ↔ `touch-x`, `translate` ↔\n// `translate-none`), which a naive walk would not terminate on.\nfunction closure(\n seeds: readonly string[],\n edges: Record,\n): string[] {\n const seen = new Set();\n const work = [...seeds];\n while (work.length > 0) {\n const id = work.pop()!;\n if (seen.has(id)) continue;\n seen.add(id);\n for (const next of edges[id] ?? []) work.push(next);\n }\n return [...seen];\n}\n\n/** Every group a later member of this group removes. */\nconst dominates = (ids: readonly string[]): string[] =>\n closure(ids, DEFAULT_CONFLICTS);\n\n/** Every group whose later member removes this group. */\nconst dominatedBy = (ids: readonly string[]): string[] =>\n closure(ids, REVERSE_CONFLICTS);\n\n// The built-in id allow-list the generator validates against is a hand-maintained\n// copy (custom-utilities-gen.ts / custom-utilities-types.ts). This is the backstop\n// that catches it diverging from the ids tailwind-merge actually ships: a group id\n// that is not a real class group silently relates to nothing at all.\nfunction assertRealGroup(id: string, where: string): void {\n if (!(id in DEFAULT_CONFIG.classGroups)) {\n throw new Error(\n `custom-utility group \"${where}\" names \"${id}\", which is not a tailwind-merge ` +\n `class group. Fix the \"/* @twmerge group ${where} … */\" declaration in app.css ` +\n `(or update the allow-list if tailwind-merge renamed the group).`,\n );\n }\n}\n\nconst classGroups: Record = {};\nconst conflictingClassGroups: Record = {};\n\nfunction addConflict(beater: string, beaten: string): void {\n const list = (conflictingClassGroups[beater] ??= []);\n if (!list.includes(beaten)) list.push(beaten);\n}\n\nfor (const entry of CUSTOM_UTILITY_REGISTRY) {\n if (\"extend\" in entry) {\n (classGroups[entry.extend] ??= []).push(...entry.classes);\n continue;\n }\n // standalone entries are intentionally invisible to twMerge.\n if (!(\"group\" in entry)) continue;\n\n // Append, never assign: the generator coalesces only CONSECUTIVE records, so a\n // group whose members sit in two non-adjacent runs of app.css emits two entries\n // with the same id — and an assignment would drop the first run's classes out of\n // twMerge entirely, silently.\n (classGroups[entry.group] ??= []).push(...entry.classes);\n\n for (const id of entry.excludes) assertRealGroup(id, entry.group);\n // Mutual: the group beats everything the listed built-ins beat, and everything\n // that beats the listed built-ins beats the group.\n for (const id of dominates(entry.excludes)) addConflict(entry.group, id);\n for (const id of dominatedBy(entry.excludes)) addConflict(id, entry.group);\n\n // `under`: the built-in is strictly broader, so it removes the group but the\n // group must not remove it — the group owns only part of what the built-in\n // writes, and the rest legitimately survives.\n for (const { group: id } of entry.under) {\n assertRealGroup(id, entry.group);\n for (const b of dominatedBy([id])) addConflict(b, entry.group);\n }\n}\n\nconst twMerge = extendTailwindMerge({\n extend: { classGroups, conflictingClassGroups },\n});\n\n/**\n * The one minter of a `ClassName` (see ui-kit's `core`). A field typed\n * `ClassName` cannot be filled with a bare literal, so its author has to route\n * the literal through here — which is precisely the `cn()` call anchor the\n * `no-adhoc-*` class rules already read tokens out of.\n *\n * `as ClassName` is deliberately the SINGLE cast to that type in the repo: grep\n * `as ClassName` and you have enumerated every way a class string can be minted\n * without passing the merge. Do not add a second one — call `cn()` instead.\n */\nexport function cn(...inputs: ClassValue[]): ClassName {\n return twMerge(clsx(inputs)) as ClassName;\n}\n","// Single source of truth for semantic surface elevation roles. Each level is a\n// frozen bundle of background (plus border / radius / shadow as the role\n// warrants), all token-backed — so a color / shadow / shape preset swap\n// re-themes every surface of a role *together* instead of each call site drifting.\n//\n// Co-located in ui-kit (not the `surface` primitive) on purpose: the foundational\n// shadcn overlays (Popover / DropdownMenu) consume `SURFACE_LEVELS.overlay`\n// directly, and ui-kit is the lowest layer — putting the map here lets them read\n// it without a layer-inverting import cycle. Same precedent as control-size's\n// runtime living next to the ambient ui-kit so foundational Button can read it.\n//\n// These are plain Tailwind class strings — NOT one multi-property `@utility` — by\n// design: composed via cn(), each property stays a real class, so a consumer's\n// `className` override (`bg-muted/30`, `rounded-lg`, `shadow-none`, …) cleanly\n// replaces just that property through tailwind-merge instead of nuking the whole\n// surface. The `no-adhoc-surface` lint rule\n// (plugins/primitives/plugins/surface/lint) bans open-coding these bundles so\n// every surface routes through `` / `` / the overlay primitives.\n\nexport type SurfaceLevel = \"sunken\" | \"base\" | \"raised\" | \"overlay\" | \"float\";\n\n// Each role also co-publishes its background as `--chrome-mask` (the arbitrary\n// `[--chrome-mask:…]` property), so a sticky bar pinned inside a surface masks\n// with that surface's own color instead of the page `--background` — a `base`\n// surface resets it back when nested inside a tinted one. This is the surface\n// half of the sticky-chrome masking contract (see `Sticky`'s `mask` / `Bar`).\n//\n// Each role ALSO co-publishes `--hover-fill` — the tone a transient hover paints\n// ON it (`bg-hover-fill`, consumed by `Button variant=\"ghost\"`). Same contract,\n// opposite direction: `--chrome-mask` is \"my background, for something painting\n// over me\"; `--hover-fill` is \"a visible step off my background, for something\n// highlighting inside me\". A ghost control is transparent, so without this it\n// hovers to the page-canvas `--muted` regardless of where it was dropped — which\n// on a `sunken` (`bg-muted`) surface or the sidebar is the surface color itself,\n// i.e. no hover at all.\nexport const SURFACE_LEVELS: Record = {\n // Recessed well / band — sits BELOW the base plane. Tone only; contained wells\n // add their own radius and bands add a directional border (e.g. `border-b`).\n // Its background IS `--muted`, so no palette token is a step off it (`--accent`\n // is `--muted`'s twin in every preset) — the hover tone is derived as a small\n // foreground tint, which darkens under light and lightens under dark by\n // construction, at any preset.\n sunken:\n \"bg-muted [--chrome-mask:var(--muted)] [--hover-fill:color-mix(in_oklab,var(--foreground)_6%,var(--muted))] [--selected-fill:var(--selected)] [--field-fill:var(--field)] [--field-outline:var(--input)]\",\n // The ground plane: pane / page canvas, toolbar bands, sticky headers. Tone\n // only — flush bands supply their own `border-b` / radius via className.\n base: \"bg-background [--chrome-mask:var(--background)] [--hover-fill:var(--muted)] [--selected-fill:var(--selected)] [--field-fill:var(--field)] [--field-outline:var(--input)]\",\n // A card lifted one step above base. Its corners are the shape group's\n // `--radius-card` (`rounded-card`), which defaults to the `rounded-md` step.\n raised:\n \"rounded-card border border-border bg-card shadow-sm [--chrome-mask:var(--card)] [--hover-fill:var(--muted)] [--selected-fill:var(--selected)] [--field-fill:var(--field)] [--field-outline:var(--input)]\",\n // Floats above everything — popovers, menus, command palette, floating panels.\n // Hovers to `--accent`, the tone base-ui's own menu/select items highlight\n // with, so a ghost button in a popover matches the items beside it.\n overlay:\n // The outline is `--popover-border` (default: the 10% foreground ring it\n // always drew); a row hovers at the overlay's own hover tone\n // (`--popover-hover`, default = the selected tier `--selected`, itself\n // default = the accent it always hovered to), so a theme whose accent IS\n // its popover tone still shows the hover. A selected row is filled with\n // the overlay's own selected tone (`--popover-selected`, default = that\n // hover step) for the same reason: in Mist `--selected` IS `--popover`,\n // so a selected row in a menu painted no highlight. Corners and shadow are the shape / shadow\n // groups' popover roles (`rounded-popover`, `shadow-popover`), defaulting\n // to the `rounded-lg` and `shadow-md` steps it always wore. A text field\n // inside wears the popover's own field tokens (`--popover-field`, default =\n // the canvas field), so a theme can sink a menu's search row into the panel.\n // Its material is the material group's (`bg-popover-fill`,\n // `backdrop-popover`): opaque and unfiltered by default, frosted glass in a\n // theme that sets them — while `--popover`, which the mask and a sticky\n // header paint, stays opaque. The colour roles (mask, hover, selected,\n // field) are the `surface-roles-overlay` utility (app.css), the one\n // definition a theme-scope root nested in the panel re-applies.\n \"rounded-popover bg-popover-fill backdrop-popover text-popover-foreground shadow-popover ring-1 ring-popover-border surface-roles-overlay\",\n // Floating CHROME over content — a toolbar card, a dock, a button floated\n // over a canvas — never a menu (that is `overlay`). Glass: the material\n // group's `--float-fill` (default the card at 86%) under its\n // `--float-backdrop` (default a 20px, 1.5× saturated blur), so what it\n // floats over shows through its edges, softened; closed by a real 1px\n // border in the popover's outline colour (inside the box, so a 42px card\n // is 42px), with its own corners (`rounded-float`, `--radius-float`, default\n // the popover's) and its own shadow tier (`--shadow-float`, default the\n // popover's). It hovers to `--accent`, the\n // tone a menu row highlights with; the mask is the card, the opaque tone\n // its fill is mixed from.\n float:\n \"rounded-float border border-float-border bg-float-fill float-sheen backdrop-float text-popover-foreground shadow-float [--chrome-mask:var(--card)] [--hover-fill:var(--accent)] [--selected-fill:var(--selected)] [--field-fill:var(--field)] [--field-outline:var(--input)]\",\n};\n","// Single source of truth for the popover width + padding roles. Each is a named\n// role mapped to a class via a module-level `Record` indexed by a\n// plain prop — mirroring `SURFACE_LEVELS` (theme/surface.ts), no Context.\n//\n// Co-located in ui-kit (not the `popover` primitive) on purpose: the foundational\n// `PopoverContent` consumes these maps directly, and ui-kit is the lowest layer —\n// putting the maps here lets it read them without a layer-inverting import cycle,\n// while the `popover` primitive (a ui-kit consumer) imports the types\n// boundary-legally through the ui-kit barrel.\n//\n// Width is a CLOSED ramp so a magic Tailwind width can't be sprinkled per call\n// site (double-declaration / overflow becomes unrepresentable). `content` is the\n// default — size-to-content, matching base-ui's native behavior. Every non-content\n// token also carries `max-w-(--available-width)` (base-ui's Positioner exposes the\n// CSS var on the popup, same as dropdown-menu.tsx) so no popover overflows a narrow\n// viewport. Padding is its own axis, mapped to the `rail-*` @utility classes owned\n// by app.css, defaulting to `md` (preserves PopoverContent's previously baked-in padding).\n//\n// `fit` is the role for a popover whose content has a VARIABLE but bounded natural\n// width (e.g. a filter-rule row: `[field] [operator] [value-control]`). It grows to\n// its content (`w-max`) with a comfortable `min-w` floor and the same viewport cap.\n// Use it instead of a fixed `w-*` whenever a rigid child (a Button/Select trigger\n// that can't shrink) would otherwise be clipped when it doesn't fit the fixed box —\n// a fixed width can never hold a row of unbounded natural width without overflow.\n//\n// Three roles size a panel RELATIVE TO ITS TRIGGER rather than to a tier:\n//\n// - `anchor` — exactly the trigger's width (a listbox: the closed control and the\n// open list are the same box, so the selected row lands where the value was).\n// - `anchor-min` — at least the trigger's width (and the menu minimum), growing\n// past it for longer item labels (a Select; a picker standing in for a field:\n// the trigger is a floor, not a cap).\n// - `snug` — no meaningful anchor at all: size to content over a small floor.\n//\n// Two size a MENU, between the density tokens `popoverWidthMenuMin` /\n// `popoverWidthMenuMax` — the common rule (Apple, Material, Linear): a menu is\n// as wide as its items between a floor and a cap, and a surface never resizes\n// while it is open:\n//\n// - `menu-fit` — a PLAIN menu, `DropdownMenuContent`'s default (and its\n// submenus'): as wide as its items, clamped to the two tokens, whatever\n// the trigger's width (a menu's width is its content's business). Most\n// menus have short labels and land on the floor, so they match. Both\n// bounds are `min()`ed with the available width, because `min-width`\n// beats `max-width`.\n// - `menu` — a CONTROL PANEL's list of choices (`ControlPanelPopover`):\n// FIXED at the max, because a panel has pages and must not jump between\n// them; so it is exactly as wide as the widest plain menu. A theme that\n// wants every menu one width sets the two tokens equal.\n//\n// `anchor-min` (a Select, a picker standing in for a field) floors at the menu\n// minimum too, so a listbox under a narrow trigger is still a menu's width.\n//\n// Both anchor roles read `--anchor-width` WITH AN IN-VAR FALLBACK, for the same\n// reason `--available-height` carries one below: the var only exists once a\n// positioner's `size.apply` has run, and an undefined var invalidates the whole\n// `max()` at computed-value time — which would silently drop the menu floor, not\n// just the anchor term. Surfaces that publish no anchor at all (a dialog) then\n// degrade to the floor rather than to nothing.\n\n// Four roles size a panel by WHAT IT IS rather than by a tier — the\n// control-panel vocabulary's only width dial (`ControlPanelPopover size`).\n// `menu` is a list of choices (fixed at the menu max, above); `builder` is a six-track rule row; `picker` is a\n// panel whose body is a GRID (swatches, icons, cover thumbnails). They are new\n// roles rather than aliases of `md` (256px) / `xl` (320px) / `3xl` (480px) on\n// purpose: reusing a t-shirt size re-imports the thing the vocabulary exists to\n// delete — a panel width chosen because some measurement was nearby, which is\n// how the filter panel ended up 481px wide (its FOOTER was the widest row) and\n// resized every time a rule was added. The ramp already carries role names\n// beside its sizes (`fit`, `snug`, `anchor`, `anchor-min`); these three join\n// them.\n//\n// `picker` earns its place on evidence a single content set could not give: THREE\n// shipped panels arrived at 320px independently (the avatar picker, the page icon\n// button, the change-cover popover). One panel at a width is a measurement; three\n// unrelated ones is a role. Forcing them onto `menu` = 262px is what wraps the\n// avatar picker's ten colour swatches onto a second row.\n//\n// `described` is the fourth: a list of choices whose rows each carry a VISIBLE\n// description line (the page-kind menu, where each kind is a policy read before\n// it is picked). Its width decides how many lines each description wraps to, so\n// a theme sizes it apart from a plain menu of one-word rows — the Pages theme\n// draws its section menu at 248px and its kind menu at 300px.\n//\n// `card` is a hover card (`overlay/hover-card`): one subject described in a\n// title, a sentence, chips and a footer. Fixed, so moving the pointer down a\n// menu's rows swaps the card's content without resizing it. `card-wide` is the\n// same card when it holds a row of side-by-side columns (a stepper's step names):\n// at least the card width, growing to fit them (to a cap) so no column overflows\n// into its neighbour.\n//\n// `settings` is a settings card (`ControlPanelCard`, control-panel): a titled\n// card of settings sections — prose that wraps, fields that span it. Fixed,\n// like a dialog's size: the surface IS the role, so no call site names it.\nexport type PopoverWidth =\n | \"content\"\n | \"fit\"\n | \"snug\"\n | \"anchor\"\n | \"anchor-min\"\n | \"menu-fit\"\n | \"menu\"\n | \"described\"\n | \"builder\"\n | \"picker\"\n | \"card\"\n | \"card-wide\"\n | \"settings\"\n | \"xs\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"xl\"\n | \"2xl\"\n | \"3xl\"\n | \"4xl\";\n// `panel` is the theme's popover inset (density `popoverPad`, default the `md`\n// step) and the default of `PopoverContent` / `OverlayPanel`: a call site that\n// says nothing about padding is padded as its theme pads popovers. A named step\n// is a call site's own decision (a menu's `xs`, a dialog's `lg`) and keeps it.\nexport type PopoverPadding =\n \"none\" | \"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"panel\";\n\nexport const POPOVER_WIDTH: Record = {\n content: \"\",\n fit: \"w-max min-w-64 max-w-(--available-width)\",\n snug: \"w-max min-w-24 max-w-(--available-width)\",\n anchor: \"w-[var(--anchor-width,0px)] min-w-36\",\n \"anchor-min\":\n \"w-max min-w-[min(max(var(--popover-width-menu-min),var(--anchor-width,0px)),var(--available-width,100vw))] max-w-(--available-width)\",\n \"menu-fit\":\n \"w-max min-w-[min(var(--popover-width-menu-min),var(--available-width,100vw))] max-w-[min(var(--popover-width-menu-max),var(--available-width,100vw))]\",\n // The four role widths are density tokens (`popoverWidth*`, defaults\n // 300px / 280px / 524px / 320px), so a theme sizes its menus and pickers.\n menu: \"w-(--popover-width-menu-max) max-w-(--available-width)\", // a control panel's list of choices\n described: \"w-(--popover-width-described) max-w-(--available-width)\", // choices with description lines\n builder: \"w-(--popover-width-builder) max-w-(--available-width)\", // a six-track rule row\n picker: \"w-(--popover-width-picker) max-w-(--available-width)\", // a panel whose body is a grid\n card: \"w-(--popover-width-card) max-w-(--available-width)\", // a hover card describing one thing\n settings: \"w-(--popover-width-settings) max-w-(--available-width)\", // a titled card of settings sections\n \"card-wide\":\n \"w-max min-w-[min(var(--popover-width-card),var(--available-width,100vw))] max-w-[min(28rem,var(--available-width,100vw))]\", // a hover card holding side-by-side columns\n xs: \"w-48 max-w-(--available-width)\",\n sm: \"w-56 max-w-(--available-width)\",\n md: \"w-64 max-w-(--available-width)\",\n lg: \"w-72 max-w-(--available-width)\",\n xl: \"w-80 max-w-(--available-width)\",\n \"2xl\": \"w-96 max-w-(--available-width)\",\n \"3xl\": \"w-[30rem] max-w-(--available-width)\",\n \"4xl\": \"w-[40rem] max-w-(--available-width)\",\n};\n\n// A padding role is ONE `rail-` class, not a padding class plus a\n// hand-written co-publication of its value. Applying an inset IS opening a region\n// (the rail contract, in app.css): `rail-` pads AND publishes the four\n// `--rail-*` vars in the same declaration, so the number exists exactly once and a\n// descendant can read the panel's real inset. The pair that used to be spelled\n// `p- [--scroll-pad:…]` here is now a property of the shared contract rather\n// than of this file.\n//\n// The reason the value must be readable to CSS is unchanged. `scroll-fade`\n// (app.css) is the one consumer today: its sticky edge strips resolve their insets\n// against the panel's CONTENT box, so without the value they park `padding-block`\n// short of the panel's inner edges — and scrolled content, which does pass through\n// that padding strip, re-emerges UNFADED there. It now reads `--rail-block-start` /\n// `--rail-block-end` per edge, which is strictly better than the single\n// `--scroll-pad` it replaced: one value for both edges was silently wrong the\n// moment a role's block padding stopped being symmetric.\n//\n// Every step publishes, `none` included: custom properties INHERIT, so a `none`\n// panel whose class published nothing would pick up an ENCLOSING panel's inset and\n// offset its fade past its own edge. `rail-none` publishes zeros like every other\n// step, so that hole cannot be reopened one role at a time.\nexport const POPOVER_PADDING: Record = {\n none: \"rail-none\",\n \"2xs\": \"rail-2xs\",\n xs: \"rail-xs\",\n sm: \"rail-sm\",\n md: \"rail-md\",\n lg: \"rail-lg\",\n panel: \"rail-panel\",\n};\n\n// Max-height is its own axis, co-located with width/padding. The contract in one\n// line: THE PANEL ALWAYS FITS THE VIEWPORT, AND THE ROLE IS A COMFORT CAP ON TOP\n// OF THAT. `--available-height` — the space Floating UI measured between the\n// anchor and the collision boundary (published by base-ui's Positioner, and by\n// `FloatingSurface`'s own `size` middleware) — is the floor every role shares;\n// `viewport` is that floor alone, and a named role only ever makes the panel\n// SHORTER than the space it has, never taller. There is no opt-out: a panel that\n// outgrows the viewport with no clamp puts its tail permanently out of reach,\n// which is the entire bug class this axis exists to prevent.\n//\n// Three details that are load-bearing, not cosmetic:\n//\n// - ONE `max-h` class per role — a clamp, never a clamp PLUS a cap. Two classes\n// in the same tailwind-merge group only compose correctly while nobody\n// touches their order; `min(cap, var(--available-height))` folds both into a\n// single class, so a caller's `className` override still resolves cleanly.\n// - The `100vh` IN-VAR fallback is required, not defensive. `--available-height`\n// does not exist until Floating UI's `size.apply` has run once, and an\n// undefined var invalidates the WHOLE `min()` at computed-value time →\n// `max-height: none` → one full-height frame on first paint.\n// - `overflow-y-auto` deliberately does NOT live here. Scrolling is an\n// unconditional invariant of the panel — owned by `OverlayPanel` (and the\n// `PopoverContent` / `FloatingSurface` roots it backs) — not something a role\n// can switch off. A clamp without a scroller is exactly the unreachable tail\n// again, so the two must never be separable at a call site.\n\nexport type PopoverMaxHeight = \"viewport\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport const POPOVER_MAX_HEIGHT: Record = {\n viewport: \"max-h-[var(--available-height,100vh)]\",\n sm: \"max-h-[min(12rem,var(--available-height,100vh))]\",\n md: \"max-h-[min(16rem,var(--available-height,100vh))]\",\n lg: \"max-h-[min(20rem,var(--available-height,100vh))]\",\n xl: \"max-h-[min(24rem,var(--available-height,100vh))]\",\n};\n","import { createContext, useContext, type ReactNode } from \"react\";\n\n/**\n * The density of an interactive control — the single axis a toolbar/slot owns.\n * Each control maps this to its OWN shape bundle (text → `control-sm`, icon →\n * `control-icon-sm`, chip → its `sm`), so a row of mixed controls snaps to one\n * height while keeping their shapes. This is why density is a named token, not a\n * CSS height var: the size is a bundle (height + padding + radius + text + gap +\n * icon), and each control resolves its own bundle from the shared density name.\n *\n * Lives in web-core (the ambient ui-kit, co-located with `button.tsx` and\n * `control-utilities.ts`) — NOT in the `control-size` primitive — so the\n * foundational `Button` reads it without inverting layers (the same placement\n * rationale as the icon-auto `control-utilities` mirror).\n */\nexport type ControlSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\n/**\n * Marker contract for a density-participating control: it derives its size from\n * ambient `ControlSize` (via `useControlSize`), so it must NOT accept a\n * per-instance `size`. Intersect a primitive's props with this (or\n * `extends DensityControlled` for an interface) instead of hand-writing\n * `size?: never`, so the \"no size prop\" contract has ONE home. Enforced at call\n * sites by the `control-size/no-adhoc-density` lint rule.\n */\nexport type DensityControlled = { size?: never };\n\n/** The `Button` cva size tokens for the square icon-shape, per density. */\nexport type ButtonIconSize = \"icon-xs\" | \"icon-sm\" | \"icon\" | \"icon-lg\";\n\nconst ControlSizeContext = createContext(\"md\");\n\n/**\n * Declares the control density for everything inside. A size-owning slot wraps\n * its contributions in this (see slot-render's `controlSize` config); any host\n * may also wrap manually to override (innermost wins).\n */\nexport function ControlSizeProvider({\n size,\n children,\n}: {\n size: ControlSize;\n children: ReactNode;\n}) {\n return (\n \n {children}\n \n );\n}\n\n/** Reads the ambient control density. Defaults to `\"md\"` outside any provider. */\nexport function useControlSize(): ControlSize {\n return useContext(ControlSizeContext);\n}\n\n/** Density → the `Button` icon-shape cva token (`md` → `\"icon\"`). */\nexport function iconSizeFor(size: ControlSize): ButtonIconSize {\n switch (size) {\n case \"xs\":\n return \"icon-xs\";\n case \"sm\":\n return \"icon-sm\";\n case \"md\":\n return \"icon\";\n case \"lg\":\n return \"icon-lg\";\n }\n}\n\n/** Density → the `Button` text-shape cva token (identity; kept as the one mapping seam). */\nexport function textSizeFor(size: ControlSize): ControlSize {\n return size;\n}\n\n/**\n * THE single density→text-step policy — the one threshold consumed by `Button`,\n * `Badge`, AND `Text`, so a row of mixed leaves can never desync its type rung.\n * `xs` drops exactly one type rung; `sm`/`md`/`lg` keep the comfortable size\n * (type tracks content density, not chrome affordance — toolbars/headers default\n * to `sm` and must stay legible). A future change to where text steps is a single\n * edit here.\n */\nexport function textStepFor(density: ControlSize): 0 | 1 {\n return density === \"xs\" ? 1 : 0;\n}\n\n/**\n * `Button`'s own text rungs: the control role (`text-control`, the type-scale's\n * `--font-size-control`) and its compact rung, driven by the shared step. The\n * weight rides separately on the cva's `font-control`, so a caller that swaps\n * this size for another role keeps the control's weight.\n */\nexport function buttonTextClassFor(density: ControlSize): string {\n return textStepFor(density) ? \"text-control-compact\" : \"text-control\";\n}\n\n/**\n * A FIELD's size bundle (`Input`, `SelectTrigger`) at a density. The height is\n * the same `control-*` token as `Button`, so a density preset resizes a form\n * row's fields and buttons together. Padding and gap come from the density\n * group's spacing ramp (`px-sm` / `gap-xs`, `px-xs` at `xs`), which presets\n * scale too — a field's text sits closer to its edge than a button label does.\n * The text is the field's own rung, not `textStepFor`'s: a small field is\n * quieter (`caption`), while `xs` keeps `body` so an inline cell editor reads\n * like the cell it replaces. No block padding: the height is fixed and the\n * content centred.\n */\nexport function fieldSizeClassFor(density: ControlSize): string {\n switch (density) {\n case \"xs\":\n return \"control-xs px-xs gap-xs text-body\";\n case \"sm\":\n return \"control-sm px-sm gap-xs text-caption\";\n case \"md\":\n return \"control-md px-sm gap-xs text-body\";\n case \"lg\":\n return \"control-lg px-sm gap-xs text-body\";\n }\n}\n\n/**\n * A multi-line field's (`Textarea`) size bundle: `fieldSizeClassFor`'s inline\n * padding and text rung, with block padding in place of the fixed height — the\n * box grows with its rows.\n */\nexport function textareaSizeClassFor(density: ControlSize): string {\n switch (density) {\n case \"xs\":\n return \"px-xs py-2xs text-body\";\n case \"sm\":\n return \"px-sm py-xs text-caption\";\n case \"md\":\n case \"lg\":\n return \"px-sm py-xs text-body\";\n }\n}\n","import { createContext, useContext, type ReactNode } from \"react\"\n\n/**\n * Whether the surrounding region is **single-line by contract** — the ambient\n * twin of `ControlSize`, and the second half of the single-line guarantee.\n *\n * Whether text wraps is NOT a property of the text: the same `` is correct\n * in a paragraph and broken in a chrome row. It is a property of WHERE the text\n * lives, owned by the container. Containers come in two kinds:\n *\n * - **Line containers** (`Frame`, `Row`, `Bar`, collapsible headers) — single-line\n * by contract. They provide `true`, so every `` leaf inside ellipsizes.\n * - **Flow containers** (`Stack`, `Column`, and the `Cluster`/`Inline` that\n * delegate to `Stack`) — multi-line OK. They RESET to `false`, so a flow region\n * nested inside a line container lets its text wrap again.\n *\n * This is the ellipsis-polish layer (the leaf reads it and truncates). It rides\n * ALONGSIDE the structural CSS layer: a line container also sets\n * `whitespace-nowrap` (via `region-line`) on its root, which stops ALL descendant\n * text — ``, a raw string, an inline chip — from wrapping. The two layers\n * mirror the existing `region-line` + truncation-leaf split.\n *\n * Lives in ui-kit (the ambient kit, beside `control-size.tsx`) so the foundational\n * `Text` leaf reads it without inverting layers — exactly the placement rationale\n * `ControlSize` uses to sit beside `Button`.\n */\nconst SingleLineContext = createContext(false)\n\n/**\n * Declares whether everything inside is single-line. Line containers wrap their\n * children in `value={true}`; flow containers reset with `value={false}`\n * (innermost wins). No logic — a bare Provider, like `ControlSizeProvider`.\n */\nexport function SingleLineProvider({\n value,\n children,\n}: {\n value: boolean\n children: ReactNode\n}) {\n return (\n \n {children}\n \n )\n}\n\n/** Reads the ambient single-line contract. Defaults to `false` (flow) outside any provider. */\nexport function useSingleLine(): boolean {\n return useContext(SingleLineContext)\n}\n","import { createContext, useContext, type ReactNode } from \"react\";\n\n/**\n * Whether the surrounding region may scroll sideways for content wider than\n * it — the ambient twin of `SingleLine`, for a wide grid table (data-table).\n *\n * A table wider than its box can either squeeze into it or overflow and let\n * its scroller scroll sideways (first column pinned). Overflowing is right only\n * where something scrolls it: a pane's own vertical `Scroll` declares `true`.\n * A drawn box ends the region with `false` — `Surface` (every card, panel and\n * floating card) and `OverlayPanel` (popovers, menus, dialogs) — because a\n * table overflowing a box it sits in spills past its border instead of\n * scrolling. Innermost wins, so a `Scroll` inside a card opens its own region.\n *\n * A context, not a DOM walk alone, because \"draws a box\" is a fact of the\n * component, not of a computed style; and it crosses portals the right way —\n * a popover's `OverlayPanel` resets it for whatever it portals.\n *\n * Lives in ui-kit (beside `single-line.tsx`) so `OverlayPanel` can reset it\n * without inverting layers.\n */\nconst SidewaysScrollContext = createContext(false);\n\n/** Declares whether content inside may scroll its region sideways. A bare\n * Provider, like `SingleLineProvider`. */\nexport function SidewaysScrollProvider({\n value,\n children,\n}: {\n value: boolean;\n children: ReactNode;\n}) {\n return (\n \n {children}\n \n );\n}\n\n/** Reads the ambient sideways-scroll region. `false` outside any provider. */\nexport function useSidewaysScroll(): boolean {\n return useContext(SidewaysScrollContext);\n}\n","import { createContext, useContext, type ReactNode } from \"react\";\n\n/**\n * Whether the surrounding region is a **menu surface** — a dropdown, a select,\n * a popover, a control panel. The ambient twin of `SingleLine`, for headings.\n *\n * A section heading's look is not a property of the heading: the same\n * `` is an uppercase eyebrow on a page and the shared menu label\n * (`MENU_LABEL_PAINT`: faint caption, semibold, sentence case) inside a menu.\n * It is a property of WHERE the heading lives, owned by the container — so a\n * picker, a header or a contributed block dropped into a menu can no longer\n * draw a heading the menu's own `Section` labels don't (the icon picker's\n * \"ICON\" under a panel's \"Color\" was that drift).\n *\n * - **Menu surfaces** (`DropdownMenuContent` and its sub-menus, `SelectContent`,\n * `PopoverContent`, `ControlPanel`) provide `true`.\n * - **Page-level overlays** (`DialogContent`, `SheetContent`) RESET to `false`:\n * React context crosses portals, so a dialog opened from a menu item would\n * otherwise inherit the menu's headings.\n *\n * Lives in ui-kit beside `single-line.tsx` so the `Text` leaf reads it without\n * inverting layers.\n */\nconst MenuSurfaceContext = createContext(false);\n\n/** Declares whether everything inside is a menu surface (innermost wins). A bare Provider. */\nexport function MenuSurfaceProvider({\n value,\n children,\n}: {\n value: boolean;\n children: ReactNode;\n}) {\n return (\n \n {children}\n \n );\n}\n\n/** Reads the ambient menu-surface contract. Defaults to `false` (page) outside any provider. */\nexport function useMenuSurface(): boolean {\n return useContext(MenuSurfaceContext);\n}\n","// THE MENU ROW — one visual definition for every menu row in the app: the\n// dropdown's items and sub-trigger, `SelectItem`, and `ControlPanel.Row`. Every\n// one of them composes these lists instead of spelling its own, so the menus\n// match and cannot drift. A new menu-like row applies them too.\n//\n// Two halves, read together: these class lists (every overridable SINGLE\n// property, each a class twMerge classifies, so a caller's `className` replaces\n// exactly the one it names — `gap-lg` on a picker pill's row), and the\n// `menu-row` / `menu-row-lit` utilities in `app.css` (the states: tone vars,\n// checked, destructive, icon tone, and the highlighted paint). Geometry is\n// global; every colour is a theme token, so each app draws the same shape in\n// its own colours.\n//\n// The highlight is wired by each component to ITS state model, because there\n// are three: base-ui's `data-highlighted` (and `data-popup-open` on a submenu's\n// trigger), and a control-panel row's `:hover`. See `app.css` for why that is a\n// variant on one utility rather than a selector list ui-kit would have to keep\n// in step with control-panel's DOM.\n\n/**\n * The row's PAINT: state layer, type role (`label`: size, line-height, medium\n * weight) and resting text colour. Shared by every menu row, the control-panel\n * row included — which keeps its own subgrid geometry (`cp-row`) and so takes\n * the paint alone.\n */\nexport const MENU_ROW_PAINT =\n \"menu-row text-label text-subtle-foreground [&_svg:not([class*='size-'])]:icon-auto\";\n\n/**\n * A checked / selected row's label weight. The colour lives in `menu-row`\n * (it keys on the row's own state attribute); the weight cannot, since a\n * utility may set no type metric that is not a role. Matches the same three\n * states, a switch excluded (its on state is a toggle, not a choice).\n *\n * Arbitrary `[&[data-…]]` variants, not `data-checked:`: this repo's\n * `data-checked` / `data-selected` variants wrap their selector in `:where()`,\n * which adds no specificity, so the weight would tie with `text-label`'s and\n * lose on stylesheet order. `.x[data-checked]` outranks the role by one step.\n */\nexport const MENU_ROW_CHECKED =\n \"[&[data-checked]]:font-semibold [&[data-selected]]:font-semibold aria-checked:not-[[role=switch]]:font-semibold\";\n\n/**\n * The full row for a flex/grid menu item that owns its own geometry (the\n * dropdown's items, `SelectItem`): the paint, the checked weight, then height\n * (`--panel-row-h`, the same as a control-panel row), inline inset\n * (`--pad-row-x`), block inset (`xs`, which only a row taller than its\n * floor — a described choice — ever shows), icon↔label gap (`sm`), corners\n * (`--radius-panel-row`) and the disabled treatment. The component appends its own highlight variant.\n * Names itself `group/menu-row`, so a part of the row (the trailing action)\n * reveals on the row's own highlight: `group-data-highlighted/menu-row:…`.\n */\nexport const MENU_ROW = `${MENU_ROW_PAINT} ${MENU_ROW_CHECKED} group/menu-row w-full min-h-panel-row px-row py-xs gap-sm rounded-panel-row cursor-default select-none outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0`;\n\n/** A section label over a run of rows: faint, caption role, semibold, sentence case. */\nexport const MENU_LABEL_PAINT =\n \"text-caption font-semibold text-faint-foreground\";\n\n/** The section label as a menu draws it, on the rows' own inline inset. */\nexport const MENU_LABEL = `${MENU_LABEL_PAINT} px-row py-xs`;\n\n/**\n * A row's trailing value or keyboard shortcut: faint caption, plain text — no\n * badge and no fill.\n */\nexport const MENU_VALUE = \"text-caption text-faint-foreground\";\n\n/**\n * A row's trailing cluster — its action, shortcut and check mark, in that\n * order — pushed to the row's end (`ml-auto`; a grid row's last track needs\n * none). One class list so the dropdown's items and its sub-trigger end alike.\n */\nexport const MENU_ROW_TRAIL = \"ml-auto flex shrink-0 items-center gap-xs\";\n\n/**\n * The hairline between runs of rows. `rail-bleed` spans it through the panel's\n * own rail, whatever padding role the panel is on.\n */\nexport const MENU_SEPARATOR = \"rail-bleed my-menu-separator h-px bg-border\";\n","import {\n createContext,\n useContext,\n useMemo,\n type Provider,\n type ReactNode,\n} from \"react\";\n\n/** A bag of `data-*` attributes to re-stamp onto portaled content.\n *\n * Portals relocate their subtree to `document.body`, severing it from the DOM\n * ancestry that carries ancestry-derived signals — the scoped theme, the plugin\n * contribution lineage the element-picker reads off marker spans, the containing\n * pane id. Each such signal is lost the moment content portals out. The fix is\n * one bridge: a signal flows across the portal as React **context** (which\n * crosses portals, unlike DOM ancestry) and every portal surface re-stamps the\n * whole bag back onto its positioner so DOM-walking consumers see it again.\n *\n * This is the generic substrate. Contributors register one attribute via\n * {@link PortalForwardProvider}; portal surfaces consume the merged map via\n * {@link usePortalForwardedAttrs}. Neither side knows about the other, so a new\n * forwarded signal is a single provider and **zero** portal-surface edits — the\n * collection/consumer split that keeps \"portals sever ancestry\" from being a\n * bug re-fixed once per signal. Theme scope and plugin lineage are the first two\n * contributors. */\nexport type PortalForwardedAttrs = Record;\n\n/** The two bags a portal can re-stamp. They differ only by a value's\n * {@link ForwardReach}: a `region` value is in `region` alone (`popup` keeps\n * the value from above), a `popups` value in `popup` alone. */\ninterface ForwardedBags {\n region: PortalForwardedAttrs;\n popup: PortalForwardedAttrs;\n}\n\ndeclare const FORWARD_SCOPE: unique symbol;\n\n/** The forwarded bags as {@link usePortalForward} merged them — the only value\n * {@link PortalForwardScope} accepts, so a scope can only ever extend the one\n * above it, never replace it with a hand-built bag. */\nexport type PortalForwardScopeValue = ForwardedBags & {\n readonly [FORWARD_SCOPE]: true;\n};\n\nconst PortalForwardContext = createContext({\n region: {},\n popup: {},\n} as unknown as PortalForwardScopeValue);\n\n/** The merged `data-*` attributes a POPUP re-stamps onto its portaled\n * positioner/root — a popover, menu, dialog, tooltip or overlay opened from\n * here. A `region`-reach value is not in it: a popup is not part of\n * the region it was opened from. */\nexport function usePortalForwardedAttrs(): PortalForwardedAttrs {\n return useContext(PortalForwardContext).popup;\n}\n\n/** The merged `data-*` attributes for content that is portaled but still part\n * of this region — an adaptive bar's item, which renders through a portal\n * whether it sits in the bar or in its overflow panel. Includes every\n * `region`-reach value, and no `popups`-reach one. */\nexport function useRegionForwardedAttrs(): PortalForwardedAttrs {\n return useContext(PortalForwardContext).region;\n}\n\n/** Which portaled content a forwarded value reaches:\n * - `all` — content relocated within the region and popups opened from it;\n * - `region` — the region only: a popup opened from inside keeps the value\n * from above. A theme sub-theme is the case — it restyles a page, not the\n * menus opened from it;\n * - `popups` — popups only: content relocated within the region keeps the\n * value from above. The app launcher is the case — its trigger sits in the\n * app's header, while the grid it opens is chrome. */\nexport type ForwardReach = \"all\" | \"region\" | \"popups\";\n\n/** The bags below a forwarded `data-*` attribute: the inherited ones with\n * `name` merged in (nearest wins per key); an `undefined` value is a no-op so\n * callers forward conditionally without branching. Provide the result with\n * {@link PortalForwardScope}. The hook form is for a component that is a\n * provider among other things (a slot item middleware), so forwarding costs it\n * one fiber — {@link PortalForwardProvider} is the plain one. */\nexport function usePortalForward(\n name: string,\n value: string | undefined,\n reach: ForwardReach = \"all\",\n): PortalForwardScopeValue {\n const parent = useContext(PortalForwardContext);\n return useMemo(() => {\n if (value === undefined) return parent;\n return {\n region:\n reach === \"popups\"\n ? parent.region\n : { ...parent.region, [name]: value },\n popup:\n reach === \"region\" ? parent.popup : { ...parent.popup, [name]: value },\n } as PortalForwardScopeValue;\n }, [parent, name, value, reach]);\n}\n\n/** Provides what {@link usePortalForward} merged. */\nexport const PortalForwardScope: Provider =\n PortalForwardContext.Provider;\n\n/** Register one forwarded `data-*` attribute for portaled descendants. Merges\n * into the inherited bag (nearest provider wins per key); an `undefined` value\n * is a no-op so callers forward conditionally without branching. */\nexport function PortalForwardProvider({\n name,\n value,\n reach = \"all\",\n children,\n}: {\n name: string;\n value: string | undefined;\n reach?: ForwardReach;\n children: ReactNode;\n}) {\n const merged = usePortalForward(name, value, reach);\n return (\n \n {children}\n \n );\n}\n","import { useLayoutEffect, useState } from \"react\";\n\nimport { useResizeObserver } from \"@plugins/primitives/plugins/dom/plugins/element-size/web\";\nimport { useEventCallback } from \"@plugins/primitives/plugins/latest-ref/web\";\n\nexport interface ScrollFade {\n /**\n * Callback ref for the scroller. A caller that also has its own ref composes\n * the two itself — this hook never writes to a ref it was handed (the React\n * Compiler forbids mutating a hook argument, and the composition belongs in\n * the component that owns the `ref` prop anyway).\n */\n measureRef: (node: T | null) => void;\n /** Wire to the scroller's `onScroll`. */\n onScroll: () => void;\n /** There is content above the visible window. */\n top: boolean;\n /** There is content below the visible window. */\n bottom: boolean;\n}\n\n/**\n * The measurement half of the `scroll-fade` utility (app.css): tells a scroller\n * whether there is more content above / below what it is currently showing.\n *\n * The paint is pure CSS — two sticky pseudo-elements gated on `data-fade-top` /\n * `data-fade-bottom`. All this hook decides is when those attributes are on, and\n * the ONLY honest answer is a measurement: a fade that is always on claims\n * content that isn't there, which is worse than no signal at all.\n *\n * Three signals feed it, because scroll alone is not enough — the \"Turn into\"\n * and `/` block menus filter their lists as the user types, so scrollable-ness\n * changes with no scroll event ever firing:\n *\n * 1. **scroll** — the returned `onScroll`.\n * 2. **resize** — a `ResizeObserver` on the scroller (through the sanctioned\n * `element-size` primitive). A clamped panel whose list filters down shrinks;\n * one that refills grows back. This is the filter case.\n * 3. **every commit** — a layout effect with no dependency list. It closes the\n * gap the other two leave: content appended while the box is ALREADY at its\n * height cap changes `scrollHeight` without resizing the box or moving the\n * scroll position. Commit-driven, not a poll, and the setter bails out on an\n * unchanged pair so it can never loop.\n *\n * The node is held in state rather than a ref (the `useElementSize` precedent):\n * the measurement is render-visible, so it must re-subscribe the observer when\n * the node changes.\n */\nexport function useScrollFade(): ScrollFade {\n const [node, setNode] = useState(null);\n const [state, setState] = useState({ top: false, bottom: false });\n\n const measure = useEventCallback(() => {\n if (!node) return;\n // 1px of slack at both ends: fractional line boxes, browser zoom and\n // `alignItemWithTrigger`'s percentage sizing routinely leave\n // `scrollTop + clientHeight` a hair short of `scrollHeight` at the true end\n // of a list — without the slack that paints a permanent bottom fade on a\n // panel already fully scrolled, and on Select, whose panel doesn't scroll\n // here at all (base-ui makes its inner list the scroller).\n const top = node.scrollTop > 1;\n const bottom = node.scrollTop + node.clientHeight < node.scrollHeight - 1;\n setState((prev) =>\n prev.top === top && prev.bottom === bottom ? prev : { top, bottom },\n );\n });\n\n useResizeObserver(() => node, measure, { deps: [node] });\n // Intentionally no dependency list — see signal 3 above.\n useLayoutEffect(measure);\n\n return {\n measureRef: setNode,\n onScroll: measure,\n top: state.top,\n bottom: state.bottom,\n };\n}\n","import type { ReactNode } from \"react\";\nimport {\n PortalForwardProvider,\n usePortalForwardedAttrs,\n} from \"./portal-forward\";\nimport { usePluginScopeId } from \"@plugins/framework/plugins/web-sdk/core\";\nimport { bareThemeScope, isSubThemeScope, qualifyThemeScope } from \"../../core\";\n\n/** The DOM attribute this signal rides across portals on. */\nconst THEME_SCOPE_ATTR = \"data-theme-scope\";\n\n/** Theme-scope token (e.g. \"app:home\") to stamp on portaled content so it\n * inherits the originating surface's scoped theme instead of the global :root\n * chrome theme. Undefined → no attribute → default (global) theme.\n *\n * Theme scope is the first consumer of the generic {@link PortalForwardProvider}\n * bridge: it forwards `data-theme-scope` exactly the way plugin lineage and pane\n * id forward theirs, so portal surfaces re-stamp every forwarded signal at once. */\nexport function usePortalThemeScope(): string | undefined {\n const pluginScope = usePluginScopeId();\n const forwarded = usePortalForwardedAttrs()[THEME_SCOPE_ATTR];\n // The attribute is qualified by the plugin scope it was stamped in\n // (`qualifyThemeScope`); a reader wants the bare token.\n return forwarded === undefined\n ? undefined\n : bareThemeScope(pluginScope, forwarded);\n}\n\nexport function PortalThemeScopeProvider({\n scope,\n popupsOnly = false,\n children,\n}: {\n scope: string | undefined;\n /** Forward the scope to popups opened from here only — the region itself\n * (and content relocated within it) keeps the scope from above. */\n popupsOnly?: boolean;\n children: ReactNode;\n}) {\n // A sub-theme is region-only: a popup opened from inside it wears the theme\n // around the sub-theme, while content relocated within the region keeps it.\n const reach = popupsOnly\n ? \"popups\"\n : scope !== undefined && isSubThemeScope(scope)\n ? \"region\"\n : \"all\";\n // Re-stamped as the DOM attribute, so qualified by the plugin scope like\n // ``'s own (a page block must not match a scope's popup).\n const pluginScope = usePluginScopeId();\n const stamped =\n scope === undefined ? undefined : qualifyThemeScope(pluginScope, scope);\n return (\n \n {children}\n \n );\n}\n","import { useCallback } from \"react\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils\";\nimport { SURFACE_LEVELS } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/surface\";\nimport {\n POPOVER_WIDTH,\n POPOVER_PADDING,\n POPOVER_MAX_HEIGHT,\n type PopoverWidth,\n type PopoverPadding,\n type PopoverMaxHeight,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/popover-width\";\nimport { SingleLineProvider } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/single-line\";\nimport { SidewaysScrollProvider } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/sideways-scroll\";\nimport { scopeSelectAllKeyDown } from \"@plugins/primitives/plugins/select-scope/web\";\nimport { useRailGuard } from \"@plugins/primitives/plugins/css/plugins/rail/web\";\nimport { OverlayBoundary } from \"@plugins/primitives/plugins/overlay/plugins/overlay-boundary/web\";\nimport { useScrollFade } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/use-scroll-fade\";\nimport type { Passthrough } from \"@plugins/primitives/plugins/passthrough/core\";\nimport { IconScopeProvider } from \"@plugins/ui/plugins/icons/web\";\nimport { usePortalThemeScope } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/portal-theme-scope\";\n\n/**\n * The floating panel an element sits in, or null when it is in none — for\n * something hung off a row INSIDE a panel that places itself against the\n * panel's edge rather than the row's (a hover card beside its menu).\n */\nexport function closestOverlayPanel(el: Element): HTMLElement | null {\n return el.closest(\"[data-overlay-panel]\");\n}\n\n/** Pins the sticky header at the panel's true top edge, through its block\n * padding (see the header below). */\nconst HEADER_PIN_STYLE = { top: \"calc(-1 * var(--rail-block-start, 0px))\" };\n\n/** Pins the sticky footer at the panel's true bottom edge, the mirror. */\nconst FOOTER_PIN_STYLE = {\n bottom: \"calc(-1 * var(--rail-block-end, 0px))\",\n};\n\n/**\n * THE floating panel — the one definition of the box every overlay surface\n * contains.\n *\n * A popover, a menu, a listbox, a caret surface and a dialog are five different\n * *state machines* (base-ui's listbox / menu / dialog, plus one deliberately\n * focus-less Floating-UI surface). They are ONE panel: the same overlay paint,\n * the same open/close animation, the same width / padding / max-height ramp, the\n * same viewport fit, the same content-context resets. Re-deriving that panel per\n * component is what let `PopoverContent` ship with no height clamp at all while\n * its four siblings each had a different one.\n *\n * Composed into a state machine through base-ui's `render` prop, which\n * `cloneElement`s this element with the popup's merged props — so the ROOT here\n * must be a real host element that spreads `{...rest}`; a root that emits no DOM\n * node would silently swallow the popup's `ref`, handlers and aria wiring. Those\n * merged props ARE this panel's passthrough ({@link Passthrough}), `ref`\n * included: the panel composes the popup's ref with its own measurement and rail\n * refs onto the one root, rather than picking one of them.\n *\n * Two things are INVARIANTS, not props — a call site cannot forget them:\n *\n * - **Fit the viewport, and scroll** — unless the caller declares the panel\n * is no scroller (`scroll={false}`, below). `maxHeight` is only a comfort cap layered\n * on top (see `POPOVER_MAX_HEIGHT`); the clamp-to-`--available-height` and the\n * scroller are unconditional. X is hidden rather than scrolled because CSS\n * cannot pair `overflow-y: auto` with `overflow-x: visible` — asking for one\n * axis forces a value on both. A panel holding content of unbounded natural\n * width wants `width=\"fit\"`, not a fixed width it would clip.\n * - **The clamp announces itself.** A panel clamped to the viewport is only\n * honest if the user can tell there is more below: with macOS overlay\n * scrollbars invisible at rest, eight items ending in clean padding read as a\n * complete list of eight. The `scroll-fade` utility paints a sticky gradient at\n * an edge that has content beyond it — bottom until the end is reached, top\n * once scrolled — measured by {@link useScrollFade} and never merely assumed.\n * Additive only: no scrollbar chrome is touched, so the native transient\n * scrollbar still appears during a gesture.\n * - **Ctrl+A scopes to the panel.** Bound on the root rather than through a\n * `` wrapper: an intervening auto-height div would break the\n * `height: 100%` / `max-height: 100%` percentage chain base-ui's `SelectPopup`\n * installs for `alignItemWithTrigger`. No `tabIndex` either — base-ui already\n * stamps one on every popup, and focus is inside the panel already, so the\n * keydown reaches the root by bubbling.\n */\nexport interface OverlayPanelProps extends Passthrough {\n /** Closed width role; default size-to-content. */\n width?: PopoverWidth;\n /** Padding role; default `panel` — the theme's popover inset (`md` unless a theme sets `popoverPad`). */\n padding?: PopoverPadding;\n /** Comfort cap on top of the unconditional viewport fit; default `viewport`. */\n maxHeight?: PopoverMaxHeight;\n /**\n * `true` (default): the panel fits the viewport and scrolls, with edge fades.\n * `false`: the panel is not a scroller and owns its overflow — for a small\n * panel whose natural size always fits (a toolbar, a disclosure morphing open\n * out of its trigger), holding content that may legitimately draw past its\n * box. It gives up the viewport clamp, the scroller and the fades;\n * `maxHeight` does not apply.\n */\n scroll?: boolean;\n /**\n * Optional sticky header rendered above the content: its rule runs full-bleed\n * through the padding, its text sits on the panel's text rail. The panel's top\n * edge strip — never a hand-rolled `border-b px-* py-*` first child.\n */\n header?: React.ReactNode;\n /**\n * Optional sticky footer pinned under the content — the mirror of `header`\n * (`border-t`, full-bleed rule, text on the text rail). The panel's bottom\n * edge strip — never a hand-rolled `border-t px-* py-*` last child.\n */\n footer?: React.ReactNode;\n /**\n * The panel's content is padded ROWS (menu rows, `Row` pills): publish the\n * text rail one row pad in from the rail (`rail-rows`), so the header, the\n * footer and every `Band` inside put their text where the rows' text is.\n * Default false: the text rail is the rail.\n */\n rows?: boolean;\n /** Landing spot for a consumer override — always the LAST `cn()` argument. */\n className?: string;\n children?: React.ReactNode;\n /** Composed with the baked-in select-scope handler (consumer runs first). */\n onKeyDown?: React.KeyboardEventHandler;\n /** Composed with the baked-in scroll-fade measurement (consumer runs first). */\n onScroll?: React.UIEventHandler;\n}\n\nexport function OverlayPanel({\n width = \"content\",\n padding = \"panel\",\n maxHeight = \"viewport\",\n scroll = true,\n header,\n footer,\n rows = false,\n className,\n children,\n ref,\n onKeyDown,\n onScroll,\n ...rest\n}: OverlayPanelProps) {\n // The panel IS the scroller, so it is also what the edge fades measure. The\n // node takes one `ref`, so the fade's measurement ref and the caller's compose\n // here (base-ui merges the popup's own ref into `ref`, so dropping it would\n // silently break every floating surface).\n const {\n measureRef,\n onScroll: measureScroll,\n top,\n bottom,\n } = useScrollFade();\n // The `padding` role IS this panel's rail region (`POPOVER_PADDING` maps each\n // role to one `rail-`), so the panel is the publisher and the guard\n // belongs on its own box — the rail is measured from the publisher's PADDING\n // box, which for this bordered surface is one pixel inside its border box.\n const railRef = useRailGuard(\"OverlayPanel\");\n const panelRef = useCallback(\n (el: HTMLDivElement | null) => {\n measureRef(el);\n railRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) ref.current = el;\n },\n [measureRef, railRef, ref],\n );\n // Consumer first, then the scope — and the merged handler applied AFTER\n // `{...rest}` so nothing in the popup's merged props can clobber it. Safe in\n // either order: the scope handler only acts on Ctrl/Cmd+\"a\", which nothing in\n // the base-ui stack reads (typeahead bails on modifiers, dismiss reads Escape).\n const handleKeyDown = (e: React.KeyboardEvent) => {\n onKeyDown?.(e);\n scopeSelectAllKeyDown(e);\n };\n const handleScroll = (e: React.UIEvent) => {\n onScroll?.(e);\n measureScroll();\n };\n // The theme scope this panel's portal re-stamps (the popup bag): the icons\n // inside draw in THAT scope's style, so a popup that wears a different theme\n // from its trigger (a `PopupTheme`, e.g. the app launcher's chrome grid)\n // draws its icons in that theme's family too.\n const popupScope = usePortalThemeScope();\n return (\n \n {/* A floating panel is a fresh flow root: reset the ambient single-line\n contract so content opened from a line container (Bar/Row) wraps /\n pretty-prints instead of collapsing onto one line. Line containers\n inside re-assert `true` locally. Outside the boundary so the crash\n fallback gets the reset too. */}\n \n {/* A drawn box: a table inside squeezes rather than spilling past the\n panel's edge (see `sideways-scroll.tsx`). */}\n \n \n {header != null && (\n // `rail-band` for the inline half: it cancels whatever rail the\n // panel is ACTUALLY on and re-applies the panel's TEXT rail, so\n // the rule spans the panel while the title lines up with the\n // rows' text (the rail itself unless the panel holds `rows`).\n // `my-overlay-header` is the block half: it bleeds up through the\n // panel's block padding by the published `--rail-block-start` (the\n // contract's escape is inline-only, so the role reads the var) and\n // spaces the first item off the header's border.\n // The pin offset is that published block rail, negated: a sticky\n // box pins at its scroller's PADDING edge, so `top: 0` here left a\n // strip of the panel's block padding above the pinned header where\n // the scrolled items showed through.\n // `py-sm`: the strip's own breathing room — it has just bled up\n // through the panel's block padding, so this IS the room above\n // its title. The inner `rail-band-region` republishes the strip's\n // padding as the rail, so a child can bleed to the panel edge (a\n // sideways-scrolling chip strip).\n \n
{header}
\n \n )}\n {children}\n {footer != null && (\n // The header's mirror: `my-overlay-footer` bleeds DOWN through\n // the panel's block padding (`--rail-block-end`) and spaces the\n // last item off the rule; the pin offset keeps the panel's bottom\n // padding from showing scrolled rows under the pinned strip.\n \n
{footer}
\n \n )}\n
\n
\n
\n \n );\n}\n","import { useState } from \"react\";\n\n/**\n * Click-to-pin for a ui-kit `Tooltip`: a click on the trigger pins it open\n * (and a second click unpins it), on top of the usual hover and focus. A\n * click elsewhere or Esc closes it. Spread `root` on the `Tooltip` and\n * `trigger` on its `TooltipTrigger` — both, or neither when not pinning.\n *\n * For a trigger whose tooltip IS its content — a status mark with nothing else\n * to do on click — so the click that people try first shows it, and it stays\n * up while they read.\n */\nexport function usePinOnClick() {\n const [pinned, setPinned] = useState(false);\n const [hovered, setHovered] = useState(false);\n const [focused, setFocused] = useState(false);\n // Hover is tracked on the trigger itself: once the popup is held open by\n // the controlled `open`, base-ui stops reporting a later hover-in, so hover\n // would never reopen it after a pinned close. base-ui still owns focus and\n // the dismissals (Esc, a click elsewhere), which unpin.\n return {\n root: {\n open: pinned || hovered || focused,\n onOpenChange: (open: boolean, { reason }: { reason: string }) => {\n if (reason === \"outside-press\" || reason === \"escape-key\") {\n setPinned(false);\n setHovered(false);\n setFocused(false);\n } else if (reason === \"trigger-focus\") setFocused(open);\n },\n },\n trigger: {\n closeOnClick: false,\n onClick: () => {\n // Unpinning closes it even under the pointer: the click said \"away\".\n if (pinned) setHovered(false);\n setPinned(!pinned);\n },\n onPointerEnter: () => setHovered(true),\n onPointerLeave: () => setHovered(false),\n },\n };\n}\n","\"use client\";\n\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { cloneElement, useLayoutEffect, useMemo, useState } from \"react\";\nimport type {\n HTMLAttributes,\n ReactElement,\n ReactNode,\n Ref,\n RefCallback,\n SyntheticEvent,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\n\n/**\n * The props a base-ui trigger computes for its element (`render`'s first\n * argument): event handlers, state attributes and its merged `ref`.\n */\ntype TriggerProps = Record & { ref?: Ref };\n\n/**\n * Passed as the `render` of a base-ui trigger mounted by `mount`: it renders\n * nothing itself and hands the trigger's props to the deferred element.\n */\nexport type RenderDeferredTrigger = (\n props: HTMLAttributes & { ref?: Ref },\n) => ReactElement;\n\n/** The events that first show intent, replayed into the trigger once mounted. */\ntype IntentEvent = \"onPointerEnter\" | \"onFocus\";\n\n/**\n * What of the trigger's props reaches the element through React: its\n * attributes by value, its handlers by name (each a stable delegate to the\n * latest handler, so a trigger render that only rebuilds handlers costs no\n * re-render of the element).\n */\ninterface Snapshot {\n attrs: Record;\n handlers: readonly string[];\n}\n\nfunction snapshotOf(props: TriggerProps): Snapshot {\n const attrs: Record = {};\n const handlers: string[] = [];\n for (const [key, value] of Object.entries(props)) {\n if (key === \"ref\") continue;\n if (typeof value === \"function\") handlers.push(key);\n else attrs[key] = value;\n }\n return { attrs, handlers: handlers.sort() };\n}\n\nfunction sameSnapshot(a: Snapshot | null, b: Snapshot): boolean {\n if (!a || a.handlers.join() !== b.handlers.join()) return false;\n const keys = Object.keys(b.attrs);\n return (\n keys.length === Object.keys(a.attrs).length &&\n keys.every((key) => Object.is(a.attrs[key], b.attrs[key]))\n );\n}\n\n/** Attaches `ref` to `node`; returns its detach (React 19 ref semantics). */\nfunction attachRef(ref: Ref | undefined, node: Element): () => void {\n if (!ref) return () => {};\n if (typeof ref === \"function\") {\n const cleanup = ref(node);\n return typeof cleanup === \"function\" ? cleanup : () => void ref(null);\n }\n ref.current = node;\n return () => {\n ref.current = null;\n };\n}\n\nfunction elementRef(element: ReactElement): Ref | undefined {\n return (element.props as { ref?: Ref }).ref;\n}\n\n/**\n * One deferred element's imperative half: its DOM node, the trigger's latest\n * props, the trigger ref attached to that node, and an intent waiting to be\n * replayed. Lives for the component's lifetime (`useState`), touched only from\n * events, refs and effects — never read during render.\n */\nclass TriggerBridge {\n private node: Element | null = null;\n private latest: TriggerProps | null = null;\n private attached: {\n ref: Ref;\n node: Element;\n detach: () => void;\n } | null = null;\n private pending: { name: IntentEvent; event: SyntheticEvent } | null = null;\n\n constructor(\n private readonly arm: () => void,\n private readonly setSnapshot: (\n update: (prev: Snapshot | null) => Snapshot | null,\n ) => void,\n ) {}\n\n /** Keeps the trigger's ref attached to the element's current node. */\n private syncTriggerRef() {\n const ref = this.latest?.ref;\n const el = this.node;\n const current = this.attached;\n if (current && current.ref === ref && current.node === el) return;\n current?.detach();\n this.attached =\n ref && el ? { ref, node: el, detach: attachRef(ref, el) } : null;\n }\n\n /** The element's ref: the consumer's own, plus the node the trigger needs. */\n refFor(childRef: Ref | undefined): RefCallback {\n return (el) => {\n // Returning a cleanup, React 19 never calls this with null.\n if (!el) return;\n const detachChild = attachRef(childRef, el);\n this.node = el;\n this.syncTriggerRef();\n return () => {\n detachChild();\n this.node = null;\n this.syncTriggerRef();\n };\n };\n }\n\n private intent(name: IntentEvent, event: SyntheticEvent) {\n // A React event's `currentTarget` is cleared once its dispatch ends;\n // the replay may run after it.\n this.pending = {\n name,\n event: Object.create(event, {\n currentTarget: { value: event.currentTarget },\n }) as SyntheticEvent,\n };\n if (name === \"onPointerEnter\") flushSync(this.arm);\n else this.arm();\n }\n\n readonly onPointerEnter = (event: SyntheticEvent) =>\n this.intent(\"onPointerEnter\", event);\n readonly onFocus = (event: SyntheticEvent) => this.intent(\"onFocus\", event);\n\n /** Stable stand-ins for the trigger's handlers, each calling its latest. */\n delegates(names: readonly string[]): Record unknown> {\n const out: Record unknown> = {};\n for (const name of names) {\n out[name] = (event) => {\n const handler = this.latest?.[name];\n return typeof handler === \"function\"\n ? (handler as (e: unknown) => unknown)(event)\n : undefined;\n };\n }\n return out;\n }\n\n /** The trigger rendered: take its props, replay a pending intent. */\n publish(props: TriggerProps | null) {\n this.latest = props;\n this.syncTriggerRef();\n if (!props) return;\n const intent = this.pending;\n this.pending = null;\n const handler = intent ? props[intent.name] : undefined;\n const el = this.node;\n if (intent && typeof handler === \"function\" && el) {\n const stillFocused =\n intent.name !== \"onFocus\" || el.ownerDocument.activeElement === el;\n if (stillFocused) (handler as (e: SyntheticEvent) => void)(intent.event);\n }\n const next = snapshotOf(props);\n this.setSnapshot((prev) => (sameSnapshot(prev, next) ? prev : next));\n }\n\n readonly render: RenderDeferredTrigger = (props) => (\n \n );\n}\n\n/**\n * Renders `children` as is — same element, same DOM node, same ref — and\n * mounts the machinery `mount` returns (a base-ui Root with a Trigger whose\n * `render` is the given function) only on the first sign of intent: the\n * pointer entering the element, or focus landing on it. Until then the\n * element costs one cloneElement.\n *\n * The element is never re-parented: the trigger renders NOTHING itself — its\n * `render` captures the props it computed — and those props are merged onto\n * the element the same way base-ui merges a `render` element's own (theirs\n * first, so the element's handlers run before the trigger's and can\n * `preventBaseUIHandler()`), its ref attached to the element's node. Mounting\n * through a wrapper instead would remount the element under the pointer or the\n * focus.\n *\n * The intent event itself is replayed into the trigger: a pointer entry mounts\n * synchronously (`flushSync`), so the trigger sees the pointer type, and the\n * native `mouseenter` that follows in the same task reaches the hover\n * listeners it has just attached — the open delay, and the provider group's\n * skip-delay, run exactly as if it had always been mounted. Focus mounts on the\n * next commit (a focus can arrive inside one, where a synchronous flush is\n * refused) and is replayed while the element still holds it.\n */\nexport function DeferredTrigger({\n children,\n mount,\n attrs,\n}: {\n children: ReactElement;\n /** The machinery, given the `render` its trigger must use. */\n mount: (render: RenderDeferredTrigger) => ReactNode;\n /** Attributes stamped on the element before the machinery mounts (`data-tooltip`). */\n attrs?: Record;\n}) {\n const [armed, setArmed] = useState(false);\n const [snapshot, setSnapshot] = useState(null);\n const [bridge] = useState(\n () => new TriggerBridge(() => setArmed(true), setSnapshot),\n );\n const childRef = elementRef(children);\n const ref = useMemo(() => bridge.refFor(childRef), [bridge, childRef]);\n const handlers = snapshot?.handlers;\n const delegates = useMemo(\n () => bridge.delegates(handlers ?? []),\n [bridge, handlers],\n );\n\n const own =\n armed && snapshot\n ? { ...snapshot.attrs, ...delegates }\n : {\n ...attrs,\n onPointerEnter: bridge.onPointerEnter,\n onFocus: bridge.onFocus,\n };\n const element = cloneElement(children, {\n ...mergeProps(own, children.props as Record),\n ref,\n } as Record);\n return (\n <>\n {element}\n {armed && mount(bridge.render)}\n \n );\n}\n\n/**\n * Rendered by the trigger in place of an element: publishes the props it was\n * given after every trigger render, and withdraws them on unmount.\n */\nfunction PublishTriggerProps({\n props,\n bridge,\n}: {\n props: TriggerProps;\n bridge: TriggerBridge;\n}) {\n useLayoutEffect(() => bridge.publish(props));\n useLayoutEffect(() => () => bridge.publish(null), [bridge]);\n return null;\n}\n","\"use client\";\n\nimport { Tooltip as TooltipPrimitive } from \"@base-ui/react/tooltip\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nimport { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils\";\nimport { usePortalForwardedAttrs } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/portal-forward\";\nimport { OverlayBoundary } from \"@plugins/primitives/plugins/overlay/plugins/overlay-boundary/web\";\nimport { usePortalContainer } from \"@plugins/primitives/plugins/overlay/plugins/portal-host/web\";\nimport { OverlayPanel } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/overlay-panel\";\nimport { usePinOnClick } from \"./use-pin-on-click\";\nimport { DeferredTrigger } from \"./deferred-trigger\";\nimport type { RenderDeferredTrigger } from \"./deferred-trigger\";\n\n/**\n * The closed type-role ladder, spelled here because ui-kit cannot import the\n * text plugin (which builds on ui-kit). The `type-scale:closed-role-ladder`\n * check pins the map below to `TYPE_ROLES`, so a role added there fails until\n * it is added here.\n */\ntype TooltipTypeRole =\n | \"display\"\n | \"title\"\n | \"heading\"\n | \"subheading\"\n | \"body\"\n | \"label\"\n | \"group\"\n | \"caption\"\n | \"control\"\n | \"tag\"\n | \"code\";\n\n/**\n * The tooltip's words wear the type ROLE the theme names (`--tooltip-role`,\n * overlay token group, default `caption`) at the weight it picks\n * (`--tooltip-emphasis`: `regular` | `strong`). CSS cannot build a var name\n * from a value, so each role is one literal style query applying that role's\n * own utility — one entry per role of the ladder (tsc demands each; the\n * closed-role-ladder check pins the list to `TYPE_ROLES`). Sizes, leading and weights stay the\n * type-scale group's; the token only chooses among them. Strong exists only for\n * the roles with a strong step (`control`, `tag`). A browser without style\n * queries draws the base `text-caption`.\n */\nconst TOOLTIP_ROLE_CLASS: Record = {\n display: \"[@container_style(--tooltip-role:display)]:text-display\",\n title: \"[@container_style(--tooltip-role:title)]:text-title\",\n heading: \"[@container_style(--tooltip-role:heading)]:text-heading\",\n subheading: \"[@container_style(--tooltip-role:subheading)]:text-subheading\",\n body: \"[@container_style(--tooltip-role:body)]:text-body\",\n label: \"[@container_style(--tooltip-role:label)]:text-label\",\n group: \"[@container_style(--tooltip-role:group)]:text-group\",\n caption: \"[@container_style(--tooltip-role:caption)]:text-caption\",\n control:\n \"[@container_style(--tooltip-role:control)]:text-control [@container_style(--tooltip-role:control)_and_style(--tooltip-emphasis:strong)]:font-control-strong\",\n tag: \"[@container_style(--tooltip-role:tag)]:text-tag [@container_style(--tooltip-role:tag)_and_style(--tooltip-emphasis:strong)]:font-tag-strong\",\n code: \"[@container_style(--tooltip-role:code)]:text-code\",\n};\nconst TOOLTIP_TYPE = Object.values(TOOLTIP_ROLE_CLASS).join(\" \");\n\nfunction TooltipProvider({\n delay = 0,\n ...props\n}: TooltipPrimitive.Provider.Props) {\n return (\n \n );\n}\n\nfunction Tooltip({ ...props }: TooltipPrimitive.Root.Props) {\n return ;\n}\n\nfunction TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {\n return ;\n}\n\nfunction TooltipContent({\n className,\n side = \"top\",\n // The theme's distance is the popup's `tooltip-offset` margin (overlay\n // group, default 4px); a `sideOffset` adds to it.\n sideOffset = 0,\n align = \"center\",\n alignOffset = 0,\n children,\n ...props\n}: TooltipPrimitive.Popup.Props &\n Pick<\n TooltipPrimitive.Positioner.Props,\n \"align\" | \"alignOffset\" | \"side\" | \"sideOffset\"\n >) {\n const forwarded = usePortalForwardedAttrs();\n // Inside a PortalHost (a fullscreen region), draw there: under `body` the\n // popup would be invisible.\n const container = usePortalContainer();\n return (\n \n \n \n {children}\n \n \n \n \n );\n}\n\n/**\n * A tooltip drawn as a CARD: the same hover / focus state machine as\n * `TooltipContent`, but the popup IS the shared `OverlayPanel` — the menu's\n * paint, radius, shadow and animation — at the fixed `card` width, with no\n * arrow. For content that describes one thing in several parts, which the\n * one-line label chrome cannot hold. Not styled per call site: no\n * `className`, and the body is laid out by `overlay/hover-card`.\n *\n * `anchor` replaces the trigger as what the card is placed against (a virtual\n * rect: beside a menu, level with one of its rows).\n */\nfunction TooltipCardContent({\n side = \"right\",\n sideOffset = 6,\n align = \"start\",\n anchor,\n wide = false,\n children,\n ...props\n}: Omit &\n Pick<\n TooltipPrimitive.Positioner.Props,\n \"align\" | \"side\" | \"sideOffset\" | \"anchor\"\n > & {\n /** The card holds side-by-side columns: grow past the card width to fit them. */\n wide?: boolean;\n }) {\n const forwarded = usePortalForwardedAttrs();\n const container = usePortalContainer();\n return (\n \n \n \n {children}\n \n }\n />\n \n \n );\n}\n\nexport type TooltipSide = \"top\" | \"right\" | \"bottom\" | \"left\";\nexport type TooltipAlign = \"start\" | \"center\" | \"end\";\n\n/**\n * The ONE way a control names itself on hover. Every interactive primitive\n * (`Button`, `IconButton`, `Badge` and the chips built on it, `CopyButton`, …)\n * takes these props and draws the themed tooltip from them — so a control can\n * never fall back to the browser's native `title` bubble, which looks nothing\n * like the rest of the app and appears after its own fixed delay.\n *\n * `title` is `never` rather than omitted: a primitive with an open passthrough\n * (`Badge`'s index signature) would otherwise still accept it.\n */\nexport interface Tooltipped {\n /**\n * Shown on hover and focus. A plain string, or rich content (``,\n * a `` shortcut). Empty (`undefined`, `null`, `false`, `\"\"`) draws none.\n */\n tooltip?: ReactNode;\n /** Which side of the control the tooltip opens on. Default \"top\". */\n tooltipSide?: TooltipSide;\n /**\n * Which edge of the control the tooltip lines up with along that side.\n * Default \"center\"; `start` / `end` for a control at the end of a row whose\n * container clips (a rounded pill), so the tooltip grows inwards.\n */\n tooltipAlign?: TooltipAlign;\n /** Never the native bubble — pass `tooltip`. */\n title?: never;\n}\n\n/** Whether a `tooltip` value draws anything. */\nfunction hasTooltip(content: ReactNode): boolean {\n return (\n content !== undefined &&\n content !== null &&\n content !== false &&\n content !== \"\"\n );\n}\n\nexport interface WithTooltipProps {\n /**\n * A short label (or a `TooltipDoc`). Something described in parts — a\n * title with an id, chips, a footer — is a hover card\n * (`overlay/hover-card`), which owns that layout and its width.\n */\n content: ReactNode;\n side?: TooltipSide;\n align?: TooltipAlign;\n /**\n * A click on the trigger pins the tooltip open (and a second click unpins\n * it), on top of the usual hover. For a trigger whose tooltip IS its content\n * — a status mark with nothing else to do on click — so the click that\n * people try first shows it, and it stays up while they read. A click\n * elsewhere or Esc closes it.\n */\n pinOnClick?: boolean;\n children: ReactElement;\n}\n\n/**\n * A plain-string tooltip is also stamped on the trigger as `data-tooltip` —\n * the stable selector a test or e2e script uses where it once read `[title]`.\n */\nfunction tooltipAttr(content: ReactNode) {\n return typeof content === \"string\" ? content : undefined;\n}\n\n/**\n * Puts a themed tooltip on any element. The primitives that take a\n * {@link Tooltipped} `tooltip` prop draw theirs through this; reach for it\n * directly only for an element that is not one of them. Empty `content`\n * renders `children` alone.\n *\n * A closed tooltip costs nothing: the base-ui machinery (Root, Trigger,\n * popup) mounts only on the first pointer entry or focus on the element\n * (`DeferredTrigger`), then behaves as if it had always been there — the open\n * delay and the provider group's skip-delay included. The element itself is\n * never wrapped or remounted.\n */\nexport function WithTooltip({\n content,\n side,\n align,\n pinOnClick = false,\n children,\n}: WithTooltipProps) {\n if (!hasTooltip(content)) return children;\n const attr = tooltipAttr(content);\n const body = (\n \n {content}\n \n );\n return (\n \n pinOnClick ? (\n \n ) : (\n \n \n {body}\n \n )\n }\n >\n {children}\n \n );\n}\n\nfunction PinnableTooltip({\n body,\n attr,\n render,\n}: {\n body: ReactElement;\n attr: string | undefined;\n render: RenderDeferredTrigger;\n}) {\n const pin = usePinOnClick();\n return (\n \n \n {body}\n \n );\n}\n\nexport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipCardContent,\n TooltipProvider,\n};\n","import { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils\";\nimport {\n WithTooltip,\n type Tooltipped,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/tooltip\";\nimport {\n useControlSize,\n textSizeFor,\n iconSizeFor,\n buttonTextClassFor,\n type ControlSize,\n type DensityControlled,\n} from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/control-size\";\nimport { symbol } from \"@plugins/ui/plugins/icons/core\";\nimport { Icon } from \"@plugins/ui/plugins/icons/web\";\n\nconst refreshIcon = symbol(\"refresh\");\n\n// The class walk reports a `cva` table at the call, so this covers the whole\n// table: its only raw radii are the `rounded-[min(var(--radius-md),Npx)]`\n// clamps on the xs / sm / inline sizes, which cap a small control's corner\n// whatever the Shape preset says (the same clamp `select.tsx` documents).\n// eslint-disable-next-line radius/no-adhoc-radius -- intentional min() clamps pin the small sizes' corners below the Shape preset's radius\nconst buttonVariants = cva(\n \"group/button focus-ring inline-flex shrink-0 items-center justify-center rounded-control border border-transparent bg-clip-padding font-control whitespace-nowrap transition-[color,background-color,border-color,outline-color,box-shadow,opacity,translate,scale,transform] select-none active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground [a]:hover:bg-primary/80\",\n // Its hairline and fill are the palette's `outlineBorder` /\n // `outlineFill` — defaults `border` + the canvas in light mode, `input`\n // + `input` at 30% in dark mode, exactly the pairs it always wore — so a\n // theme restyles every outlined control (and every segment of an\n // outlined ButtonGroup: the seam between two segments is their shared\n // hairline) at once. Its hover is the palette's `outlineHover` /\n // `outlineHoverForeground` (defaults `muted` in light mode, `input` at\n // 50% in dark, + `foreground`), so a theme whose fill already is its\n // muted tone can still step the hover visibly off it.\n outline:\n \"border-outline-border bg-outline-fill hover:bg-outline-hover hover:text-outline-hover-foreground aria-expanded:bg-muted aria-expanded:text-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n // Ghost is the ONE transparent variant — it has no background of its\n // own, so its hover tone belongs to the surface it was dropped into, not\n // to the page canvas. `bg-hover-fill` follows `--hover-fill`, which every\n // surface co-publishes (see `SURFACE_LEVELS` / the app-shell sidebar);\n // it defaults to `--muted`, so on the canvas this is byte-identical to\n // the `bg-muted` it replaces. The former `dark:hover:bg-muted/50` is\n // deliberately gone: that half-strength dark hover made ghost the only\n // control that highlighted weaker than the menu items and sidebar rows\n // beside it — the same inconsistency this token exists to remove.\n // The other variants paint their own background, so their hover is\n // relative to THEMSELVES and correctly stays a fixed token.\n ghost:\n \"hover:bg-hover-fill hover:text-foreground aria-expanded:bg-hover-fill aria-expanded:text-foreground\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n // The OPTIONAL affordance — attach, add, point at: something the user\n // may do beside the main action, so it sits quieter than `outline`. No\n // fill, a dashed hairline and a muted label, all brought to full\n // strength on hover (and while its popover is open).\n dashed:\n \"border-dashed border-muted-foreground/40 text-muted-foreground hover:border-muted-foreground/70 hover:text-foreground aria-expanded:border-muted-foreground/70 aria-expanded:text-foreground\",\n // A FRAME and nothing else: `outline`'s hairline with no fill of its\n // own, so — like `ghost` — it wears the surface it sits on and hovers\n // to that surface's `--hover-fill`. `outline` paints a fill (the canvas,\n // or `input/30` in dark mode), which on any other surface — the app\n // chrome's graphite bar — shows as a lighter patch behind the label.\n frame:\n \"border-border hover:bg-hover-fill hover:text-foreground aria-expanded:bg-hover-fill aria-expanded:text-foreground\",\n // A control that FLOATS over content — a zoom chip on a canvas, a\n // \"Dismiss all\" under the toast stack, a jump-to-bottom over a feed. It\n // paints the overlay surface (`bg-popover`), which is solid in every\n // scheme, and carries its own shadow. Every other bordered variant is\n // see-through somewhere (`outline` is `input/30` in dark mode, `frame`\n // and `ghost` have no fill), so whatever scrolls underneath shows\n // through the label. `ui-kit/no-button-shadow` sends every\n // `shadow-*` on a Button here.\n floating:\n \"border-border bg-popover text-popover-foreground shadow-md hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground\",\n link: \"text-primary-text underline-offset-4 hover:underline\",\n },\n // A text size is the density token group's bundle for that size: height\n // (`control-*`), inline padding (`px-control-*`) and icon gap\n // (`gap-control-*`) — so a preset sets a button's width rhythm, not only\n // its height. Never a numeric `px-`/`gap-` here: that is the width a\n // theme cannot reach.\n // The icon is the size's density token (`size-control-icon-*`, the\n // density group's `controlIcon*`). Its defaults follow the TEXT, not the\n // box: `sm` sets the same 14px label as `md` (`buttonTextClassFor`), so it\n // keeps the 16px icon; only `xs`, whose text drops a rung, gets 12px.\n // The corners are the shape group's control radius roles:\n // `rounded-control` (md / lg and every ButtonGroup segment) and the\n // capped `rounded-control-sm` / `-xs` for the small sizes.\n size: {\n md: \"control-md gap-control-md px-control-md [&_svg:not([class*='size-'])]:size-control-icon-md\",\n default:\n \"control-md gap-control-md px-control-md [&_svg:not([class*='size-'])]:size-control-icon-md\",\n xs: \"control-xs gap-control-xs px-control-xs rounded-control-xs in-data-[slot=button-group]:rounded-control [&_svg:not([class*='size-'])]:size-control-icon-xs\",\n sm: \"control-sm gap-control-sm px-control-sm rounded-control-sm in-data-[slot=button-group]:rounded-control [&_svg:not([class*='size-'])]:size-control-icon-sm\",\n lg: \"control-lg gap-control-lg px-control-lg [&_svg:not([class*='size-'])]:size-control-icon-lg\",\n icon: \"control-icon-md [&_svg:not([class*='size-'])]:size-control-icon-md\",\n \"icon-xs\":\n \"control-icon-xs rounded-control-xs in-data-[slot=button-group]:rounded-control [&_svg:not([class*='size-'])]:size-control-icon-xs\",\n \"icon-sm\":\n \"control-icon-sm rounded-control-sm in-data-[slot=button-group]:rounded-control [&_svg:not([class*='size-'])]:size-control-icon-sm\",\n \"icon-lg\":\n \"control-icon-lg [&_svg:not([class*='size-'])]:size-control-icon-lg\",\n // Sized by the surrounding text, so it has no control role: its\n // weight is the fixed medium, not the themable control weight.\n inline:\n \"h-auto rounded-[min(var(--radius-md),8px)] p-2xs align-middle text-[1em] font-medium [&_svg:not([class*='size-'])]:icon-auto\",\n },\n // A pill's rounded ends take room a rectangle's corners don't: `pill-ends`\n // adds the shape group's pill extra to both sides of the size's own\n // `px-control-*`. Icon sizes have no inline padding, so it is inert there.\n shape: {\n default: \"\",\n pill: \"rounded-full! pill-ends\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n shape: \"default\",\n },\n },\n);\n\n/**\n * `loading` shows a spinner and disables the button. It is also driven\n * automatically: if `onClick` returns a promise, the button enters the pending\n * state until it settles — so any async action is double-click-proof and\n * self-indicating with zero per-call-site wiring. The public `onClick` type is\n * left as base-ui's standard handler so consumers and value-returning handlers\n * keep type-checking; the promise is detected at runtime.\n *\n * `tooltip` (see {@link Tooltipped}) is the button's hover name — the themed\n * tooltip, never the native `title`, which the props reject. An icon-shaped\n * button with a plain-string tooltip and no `aria-label` takes the tooltip as\n * its accessible name, as the native `title` used to give it.\n */\ntype ButtonOwnProps = Omit &\n Omit, \"size\"> &\n DensityControlled &\n Tooltipped & {\n aspect?: \"text\" | \"icon\" | \"inline\";\n loading?: boolean;\n };\n\nfunction Button({\n className,\n variant = \"default\",\n aspect = \"text\",\n shape = \"default\",\n loading = false,\n disabled = false,\n onClick,\n tooltip,\n tooltipSide,\n tooltipAlign,\n children,\n ...props\n}: ButtonOwnProps) {\n // Density (height) is ALWAYS ambient — read from `useControlSize()` (set by a\n // toolbar/slot/region), never a per-instance dial that can desync from\n // neighbors. `aspect` selects the orthogonal SHAPE axis: \"text\" → text density,\n // \"icon\" → a square icon box at that same density, \"inline\" → the inline escape\n // (collapses to surrounding text height). There is no per-instance density\n // override.\n const density = useControlSize();\n const resolvedSize =\n aspect === \"inline\"\n ? \"inline\"\n : aspect === \"icon\"\n ? iconSizeFor(density)\n : textSizeFor(density);\n\n // Text size flows through the shared density→text policy (textStepFor, also\n // consumed by Badge + Text). The `inline` aspect keeps its own `text-[1em]`\n // (collapses to surrounding text height), so it opts out.\n const textClass =\n aspect === \"inline\" ? undefined : buttonTextClassFor(density);\n\n // Auto-pending: if the handler returns a promise, reflect in-flight state\n // until it settles. Guard setState against unmount mid-flight.\n const [autoPending, setAutoPending] = useState(false);\n const mounted = useRef(true);\n useEffect(() => () => void (mounted.current = false), []);\n\n const handleClick: NonNullable = (\n event,\n ) => {\n const result = onClick?.(event) as unknown;\n if (result && typeof (result as { then?: unknown }).then === \"function\") {\n setAutoPending(true);\n void Promise.resolve(result).finally(() => {\n if (mounted.current) setAutoPending(false);\n });\n }\n };\n\n const isLoading = loading || autoPending;\n // Icon-shaped buttons have no label, so the spinner replaces the glyph;\n // text buttons keep their label with the spinner as a leading indicator.\n const iconOnly = resolvedSize.startsWith(\"icon\");\n\n const ariaLabel =\n props[\"aria-label\"] ??\n (aspect === \"icon\" && typeof tooltip === \"string\" ? tooltip : undefined);\n\n return (\n \n \n );\n}\n\nexport { Button, buttonVariants };\nexport type { ControlSize };\n","import type { ComponentProps } from \"react\";\n\nimport { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils\";\n\n/**\n * ButtonGroup joins 2+ controls into a single segmented/split control.\n *\n * This is the sanctioned replacement for the hand-rolled\n * `buttonVariants`-on-a-`
` + raw `
\n );\n}\n\nexport { ButtonGroup };\n","import type * as React from \"react\";\nimport { MenuSurfaceProvider } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/theme/menu-surface\";\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\n\nimport { cn } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/lib/utils\";\nimport { Button } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/ui/button\";\nimport { usePortalForwardedAttrs } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/portal-forward\";\nimport { OverlayPanel } from \"@plugins/primitives/plugins/css/plugins/ui-kit/web/components/overlay-panel\";\nimport { usePortalContainer } from \"@plugins/primitives/plugins/overlay/plugins/portal-host/web\";\nimport { symbol } from \"@plugins/ui/plugins/icons/core\";\nimport { Icon } from \"@plugins/ui/plugins/icons/web\";\n\nconst closeIcon = symbol(\"close\");\n\nfunction Dialog({ ...props }: DialogPrimitive.Root.Props) {\n return ;\n}\n\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\n return ;\n}\n\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\n return ;\n}\n\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\n const container = usePortalContainer();\n return (\n \n );\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: DialogPrimitive.Backdrop.Props) {\n return (\n \n );\n}\n\n/**\n * Width tiers, applied to the POPUP — not to the panel inside it. The popup's\n * box is what base-ui treats as \"inside the dialog\" for outside-press dismissal\n * (see `DialogContent`), so it must be the width of the visible panel and no\n * wider. The panel then fills it (`w-full`).\n */\nconst DIALOG_SIZES = {\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-4xl\",\n} as const;\n\n/**\n * How far below the top of the window the dialog sits, and the clearance it\n * keeps above the bottom.\n *\n * The offset was a bare `20vh`, which is a fraction of the window and so grows\n * without limit: on a tall display the dialog drifts ever further down the\n * screen, away from where the eye is. Capping it at `8rem` keeps the\n * proportional behaviour where it helps — a short window, where a fixed inset\n * would eat the room the dialog needs — and stops it where it stops helping.\n *\n * The bottom gap is the other half, and it is what makes the height HONEST: the\n * panel's `--available-height` is derived from both numbers, so the box can\n * never run past the bottom edge of the window. It used to be a separate `75vh`\n * that happened to leave 5vh under a 20vh offset — two numbers that had to be\n * kept in agreement by hand, and on a short window left the panel almost\n * touching the bottom.\n *\n * `dvh`, not `vh`: on a phone `vh` is the address-bar-less height, so a dialog\n * sized in `vh` is taller than the screen until the bar retracts.\n */\nconst DIALOG_TOP = \"min(20vh, 8rem)\";\nconst DIALOG_BOTTOM_GAP = \"2rem\";\n\ntype DialogContentProps = Omit & {\n /**\n * Plain override class landing LAST on the panel. Narrower than base-ui's\n * `className`, which also accepts a `(state) => string` form: this class goes\n * to the inner `OverlayPanel`, which has no access to the popup's state (and\n * the state-driven variants live as `data-*` selectors in the panel's own\n * class bundle anyway).\n */\n className?: string;\n /** Panel width tier. Default \"md\". */\n size?: keyof typeof DIALOG_SIZES;\n /**\n * The ✕ in the corner. Default `true`, because Escape and the outside press\n * are both invisible — without it a dialog can offer the user no way out they\n * can SEE, which is exactly what a dialog must never do.\n *\n * Pass `false` only when the content already shows its own way out (the\n * command palette's \"esc close\" footer). Never because the dialog is\n * important: a dialog that must not be dismissed by a stray press says that\n * with `dismissible`, and needs this button MORE, not less, since it is then\n * the only visible exit.\n */\n showCloseButton?: boolean;\n};\n\nfunction DialogContent({\n className,\n children,\n size = \"md\",\n showCloseButton = true,\n ...props\n}: DialogContentProps) {\n const forwarded = usePortalForwardedAttrs();\n return (\n \n \n \n {/* The popup's box IS the panel's box — never a full-viewport wrapper.\n base-ui dismisses on an outside press by asking \"is the press target\n inside the floating element?\", and the floating element is THIS node.\n A `fixed inset-0` popup with the panel centred inside it therefore\n answers yes to every press in the window, which is why this dialog\n used to be dismissable only with Escape. Centring is done with the\n box itself (`inset-x-*` + `mx-auto` against the width tier) so there\n is no wrapper left to swallow the press. */}\n \n {/* The dialog's box IS the shared panel — see `overlay-panel.tsx`. A\n dialog is CENTERED rather than anchored, so nothing positions it and\n no positioner publishes an `--available-height` for it; the popup\n above declares one from its own offset, which is what turns the\n panel's unconditional clamp into \"as tall as the room actually\n left\". Width comes from the popup too (`DIALOG_SIZES`) and the panel\n simply fills it: `POPOVER_WIDTH.content` (the default role) is the\n empty string, so the `w-full` here is unopposed.\n\n `padding=\"lg\"` is FIXED, and there is no `padded` prop to switch it\n off: the panel owns the region's edge, so it owns the rail (see the\n rail contract in app.css). A flush caller does not turn the region\n off — the child that must reach the panel edge (a header band's\n rule, a footer's) says so itself with `rail-bleed`, which cancels\n and re-applies the rail as one indivisible act, so its content stays\n on the same rail as everything else. Absent-from-the-type rather\n than defaulted-in-it is the same move `ControlPanelPopover` makes\n with `width`: an escape that has no spelling cannot be reached for\n by accident.\n\n Two caveats before reaching for it. Bleed only a DIRECT child of the\n panel: the panel's own `overflow-x-hidden` is what makes a bleed free\n — inside a nested `ScrollArea` (viewport `overflow: scroll` on both\n axes) the same class buys sideways scroll instead of a wider box. And\n never put it in this component's own `className`, however much it\n looks like the replacement for the old `padded={false}`:\n `rail-bleed` is `extend px`, so it would REMOVE the `rail-lg` below\n it, leaving the panel un-inset (the intended half) and also bleeding\n against a rail that is now zero. */}\n \n {children}\n \n {showCloseButton && (\n // A SIBLING of the panel, not a child: the panel is the scroller, so a\n // button inside it would scroll away with the content and be clipped\n // by its overflow. As a sibling of a `fixed` popup it resolves against\n // the popup — which is now exactly the panel's box, so \"the panel's\n // top-right corner\" and \"the popup's top-right corner\" are one place.\n //\n // `z-popover` matches the panel rather than beating it: equal layers\n // paint in tree order and this comes second, so it lands on top\n // without claiming a rung above the whole popover layer.\n \n }\n >\n \n Close\n \n )}\n \n \n \n );\n}\n\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\n return (\n \n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: DialogPrimitive.Description.Props) {\n return (\n \n );\n}\n\nexport {\n Dialog,\n DialogTrigger,\n DialogClose,\n DialogPortal,\n DialogOverlay,\n DialogContent,\n DialogTitle,\n DialogDescription,\n};\n","import * as React from \"react\";\n\nimport { useReportPopupOpen } from \"@plugins/primitives/plugins/overlay/plugins/popup-open/web\";\n\n/**\n * Publishes a base-ui `Root`'s open state to the enclosing `PopupOpenScope`.\n *\n * This is the ONLY place in the repo that reads a popup library's open state on\n * behalf of surrounding chrome — consumers get the typed boolean out of\n * `PopupOpenScope` instead of a CSS selector naming base-ui's `data-popup-open`\n * attribute (which is how the previous, dead Radix spelling rotted unnoticed).\n *\n * Returns the `onOpenChange` to hand back to the `Root`, and whether the popup\n * is open now. The caller's handler is invoked with EVERY argument base-ui passed — the signature is\n * `(open, eventDetails)` today and the spread keeps this wrapper transparent to\n * whatever it becomes.\n */\nexport function usePopupOpenMirror({\n open,\n defaultOpen,\n onOpenChange,\n}: {\n open?: boolean | undefined;\n defaultOpen?: boolean | undefined;\n onOpenChange?: ((open: boolean, ...args: Args) => void) | undefined;\n}): {\n onOpenChange: (open: boolean, ...args: Args) => void;\n isOpen: boolean;\n} {\n const [mirrored, setMirrored] = React.useState(defaultOpen ?? false);\n\n // A controlled `open` is the truth whenever it is supplied; the mirror only\n // stands in for the uncontrolled case, where base-ui keeps the state itself.\n const isOpen = open ?? mirrored;\n useReportPopupOpen(isOpen);\n\n return {\n onOpenChange: (nextOpen, ...rest) => {\n setMirrored(nextOpen);\n onOpenChange?.(nextOpen, ...rest);\n },\n isOpen,\n };\n}\n","import * as React from \"react\";\n\n/**\n * Closes an open popup when the user clicks into an `